PieMenuStyle.qml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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 QtGraphicalEffects 1.0
  41. import QtQuick.Controls 1.4
  42. import QtQuick.Controls.Styles 1.4
  43. import QtQuick.Controls.Private 1.0
  44. import QtQuick.Extras 1.4
  45. import QtQuick.Extras.Private 1.0
  46. import QtQuick.Extras.Private.CppUtils 1.0
  47. /*!
  48. \qmltype PieMenuStyle
  49. \inqmlmodule QtQuick.Controls.Styles
  50. \since 5.5
  51. \ingroup controlsstyling
  52. \brief Provides custom styling for PieMenu.
  53. PieMenuStyle is a style for PieMenu that draws each section of the menu as a
  54. filled "slice".
  55. You can create a custom pie menu by replacing the following delegates:
  56. \list
  57. \li \l background
  58. \li \l cancel
  59. \li \l menuItem
  60. \li \l title
  61. \endlist
  62. To customize the appearance of each menuItem without having to define your
  63. own, you can use the \l backgroundColor and \l selectionColor properties.
  64. To customize the drop shadow, use the \l shadowColor, \l shadowRadius and
  65. \l shadowSpread properties.
  66. Icons that are too large for the section that they are in will be scaled
  67. down appropriately.
  68. To style individual sections of the menu, use the menuItem component:
  69. \code
  70. PieMenuStyle {
  71. shadowRadius: 0
  72. menuItem: Item {
  73. id: item
  74. rotation: -90 + sectionCenterAngle(styleData.index)
  75. Rectangle {
  76. width: parent.height * 0.2
  77. height: width
  78. color: "darkorange"
  79. radius: width / 2
  80. anchors.right: parent.right
  81. anchors.verticalCenter: parent.verticalCenter
  82. Text {
  83. id: textItem
  84. text: control.menuItems[styleData.index].text
  85. anchors.centerIn: parent
  86. color: control.currentIndex === styleData.index ? "red" : "white"
  87. rotation: -item.rotation
  88. }
  89. }
  90. }
  91. }
  92. \endcode
  93. \image piemenu-menuitem-example.png A custom PieMenu
  94. */
  95. Style {
  96. id: pieMenuStyle
  97. /*!
  98. The \l PieMenu that this style is attached to.
  99. */
  100. readonly property PieMenu control: __control
  101. /*! The background color. */
  102. property color backgroundColor: Qt.rgba(0.6, 0.6, 0.6, 0.66)
  103. /*! The selection color. */
  104. property color selectionColor: "#eee"
  105. /*!
  106. The shadow color.
  107. \sa DropShadow
  108. */
  109. property color shadowColor: Qt.rgba(0, 0, 0, 0.26)
  110. /*!
  111. The shadow radius.
  112. \sa DropShadow
  113. */
  114. property real shadowRadius: 10
  115. /*!
  116. The shadow spread.
  117. \sa DropShadow
  118. */
  119. property real shadowSpread: 0.3
  120. /*!
  121. The distance from the center of the menu to the outer edge of the menu.
  122. \sa cancelRadius
  123. */
  124. readonly property real radius: Math.min(control.width, control.height) * 0.5
  125. /*!
  126. The radius of the area that is used to cancel the menu.
  127. \sa radius
  128. */
  129. property real cancelRadius: radius * 0.4
  130. /*!
  131. The angle (in degrees) at which the first menu item will be drawn.
  132. The absolute range formed by \a startAngle and \l endAngle must be
  133. less than or equal to \c 360 degrees.
  134. Menu items are displayed clockwise when \a startAngle is less than
  135. \l endAngle, otherwise they are displayed anti-clockwise.
  136. \sa endAngle
  137. */
  138. property real startAngle: -90
  139. /*!
  140. The angle (in degrees) at which the last menu item will be drawn.
  141. The absolute range formed by \l startAngle and \a endAngle must be
  142. less than or equal to \c 360 degrees.
  143. Menu items are displayed clockwise when \l startAngle is less than
  144. \a endAngle, otherwise they are displayed anti-clockwise.
  145. \sa startAngle
  146. */
  147. property real endAngle: 90
  148. /*!
  149. \qmlmethod real PieMenuStyle::sectionStartAngle(int itemIndex)
  150. Returns the start of the section at \a itemIndex as an angle in degrees.
  151. */
  152. function sectionStartAngle(itemIndex) {
  153. return MathUtils.radToDegOffset(control.__protectedScope.sectionStartAngle(itemIndex));
  154. }
  155. /*!
  156. \qmlmethod real PieMenuStyle::sectionCenterAngle(int itemIndex)
  157. Returns the center of the section at \a itemIndex as an angle in
  158. degrees.
  159. */
  160. function sectionCenterAngle(itemIndex) {
  161. return MathUtils.radToDegOffset(control.__protectedScope.sectionCenterAngle(itemIndex));
  162. }
  163. /*!
  164. \qmlmethod real PieMenuStyle::sectionEndAngle(int itemIndex)
  165. Returns the end of the section at \a itemIndex as an angle in degrees.
  166. */
  167. function sectionEndAngle(itemIndex) {
  168. return MathUtils.radToDegOffset(control.__protectedScope.sectionEndAngle(itemIndex));
  169. }
  170. /*!
  171. \internal
  172. The distance in pixels from the center of each menu item's icon to the
  173. center of the menu. A higher value means that the icons will be further
  174. from the center of the menu.
  175. */
  176. readonly property real __iconOffset: cancelRadius + ((radius - cancelRadius) / 2)
  177. /*! \internal */
  178. readonly property real __selectableRadius: radius - cancelRadius
  179. /*! \internal */
  180. property int __implicitWidth: Math.round(TextSingleton.implicitHeight * 12.5)
  181. /*! \internal */
  182. property int __implicitHeight: __implicitWidth
  183. /*!
  184. The background of the menu.
  185. By default, there is no background defined.
  186. */
  187. property Component background
  188. /*!
  189. The cancel component of the menu.
  190. This is an area in the center of the menu that closes the menu when
  191. clicked.
  192. By default, it is not visible.
  193. */
  194. property Component cancel: null
  195. /*!
  196. The component that displays the text of the currently selected menu
  197. item, or the title if there is no current item.
  198. The current item's text is available via the \c styleData.text
  199. property.
  200. */
  201. property Component title: Text {
  202. font.pointSize: 20
  203. text: styleData.text
  204. horizontalAlignment: Text.AlignHCenter
  205. verticalAlignment: Text.AlignVCenter
  206. color: "#ccc"
  207. antialiasing: true
  208. }
  209. /*!
  210. This component defines each section of the pie menu.
  211. This component covers the width and height of the control.
  212. No mouse events are propagated to this component, which means that
  213. controls like Button will not function when used within it. You can
  214. check if the mouse is over this section by comparing
  215. \c control.currentIndex to \c styleData.index.
  216. Each instance of this component has access to the following properties:
  217. \table
  218. \row \li \c {readonly property int} \b styleData.index
  219. \li The index of this menu item.
  220. \row \li \c {readonly property bool} \b styleData.hovered
  221. \li \c true if this menu item is under the mouse.
  222. \row \li \c {readonly property bool} \b styleData.pressed
  223. \li \c true if the mouse is pressed down on this menu item.
  224. \endtable
  225. */
  226. property Component menuItem: Item {
  227. id: actionRootDelegateItem
  228. function drawRingSection(ctx, x, y, section, r, ringWidth, ringColor) {
  229. ctx.fillStyle = ringColor;
  230. // Draw one section.
  231. ctx.beginPath();
  232. ctx.moveTo(x,y);
  233. // Canvas draws 0 degrees at 3 o'clock, whereas we want it to draw it at 12.
  234. var start = control.__protectedScope.sectionStartAngle(section);
  235. var end = control.__protectedScope.sectionEndAngle(section);
  236. ctx.arc(x, y, r, start, end, start > end);
  237. ctx.fill();
  238. // Either change this to the background color, or use the global composition.
  239. ctx.fillStyle = "black";
  240. ctx.globalCompositeOperation = "destination-out";
  241. ctx.beginPath();
  242. ctx.moveTo(x, y);
  243. ctx.arc(x, y, ringWidth, 0, Math.PI * 2);
  244. ctx.closePath();
  245. ctx.fill();
  246. // If using the global composition method, make sure to change it back to default.
  247. ctx.globalCompositeOperation = "source-over";
  248. }
  249. Canvas {
  250. id: actionCanvas
  251. anchors.fill: parent
  252. property color currentColor: control.currentIndex === styleData.index ? selectionColor : backgroundColor
  253. Connections {
  254. target: pieMenuStyle
  255. function onStartAngleChanged() { actionCanvas.requestPaint() }
  256. function onEndAngleChanged() { actionCanvas.requestPaint() }
  257. }
  258. Connections {
  259. target: control
  260. function onCurrentIndexChanged() { actionCanvas.requestPaint() }
  261. }
  262. onPaint: {
  263. var ctx = getContext("2d");
  264. ctx.reset();
  265. drawRingSection(ctx, width / 2, height / 2, styleData.index, radius, cancelRadius, currentColor);
  266. }
  267. }
  268. readonly property var __styleData: styleData
  269. PieMenuIcon {
  270. control: pieMenuStyle.control
  271. styleData: __styleData
  272. }
  273. }
  274. /*! \internal */
  275. property Component panel: Item {
  276. implicitWidth: __implicitWidth
  277. implicitHeight: __implicitHeight
  278. property alias titleItem: titleLoader.item
  279. Item {
  280. id: itemgroup
  281. anchors.fill: parent
  282. visible: false
  283. Loader {
  284. id: backgroundLoader
  285. sourceComponent: background
  286. anchors.fill: parent
  287. }
  288. Loader {
  289. id: cancelLoader
  290. sourceComponent: cancel
  291. anchors.centerIn: parent
  292. }
  293. Repeater {
  294. id: menuItemRepeater
  295. model: control.__protectedScope.visibleItems
  296. delegate: Loader {
  297. id: menuItemLoader
  298. anchors.fill: parent
  299. sourceComponent: menuItem
  300. readonly property int __index: index
  301. property QtObject styleData: QtObject {
  302. readonly property alias index: menuItemLoader.__index
  303. readonly property bool hovered: control.currentIndex === index
  304. readonly property bool pressed: control.__protectedScope.pressedIndex === index
  305. }
  306. }
  307. }
  308. }
  309. DropShadow {
  310. id: dropShadow
  311. anchors.fill: itemgroup
  312. spread: shadowSpread
  313. samples: shadowRadius * 2 + 1
  314. transparentBorder: true
  315. color: shadowColor
  316. source: itemgroup
  317. }
  318. Loader {
  319. id: titleLoader
  320. sourceComponent: title
  321. x: parent.x + parent.width / 2 - width / 2
  322. y: -height - 10
  323. property QtObject styleData: QtObject {
  324. property string text: control.currentIndex !== -1
  325. ? control.__protectedScope.visibleItems[control.currentIndex].text
  326. : control.title
  327. }
  328. }
  329. }
  330. }