ExpandablePopup.qml 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright (c) 2018 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 2.3
  5. import UM 1.2 as UM
  6. import Cura 1.0 as Cura
  7. import QtGraphicalEffects 1.0 // For the dropshadow
  8. // The expandable component has 2 major sub components:
  9. // * The headerItem; Always visible and should hold some info about what happens if the component is expanded
  10. // * The contentItem; The content that needs to be shown if the component is expanded.
  11. Item
  12. {
  13. id: base
  14. // Enumeration with the different possible alignments of the content with respect of the headerItem
  15. enum ContentAlignment
  16. {
  17. AlignLeft,
  18. AlignRight
  19. }
  20. // The headerItem holds the QML item that is always displayed.
  21. property alias headerItem: headerItemLoader.sourceComponent
  22. // The contentItem holds the QML item that is shown when the "open" button is pressed
  23. property alias contentItem: content.contentItem
  24. property color contentBackgroundColor: UM.Theme.getColor("action_button")
  25. property color headerBackgroundColor: UM.Theme.getColor("action_button")
  26. property color headerActiveColor: UM.Theme.getColor("secondary")
  27. property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
  28. property alias enabled: mouseArea.enabled
  29. // Text to show when this component is disabled
  30. property alias disabledText: disabledLabel.text
  31. // Defines the alignment of the content with respect of the headerItem, by default to the right
  32. property int contentAlignment: ExpandablePopup.ContentAlignment.AlignRight
  33. // How much spacing is needed around the contentItem
  34. property alias contentPadding: content.padding
  35. // How much padding is needed around the header & button
  36. property alias headerPadding: background.padding
  37. // What icon should be displayed on the right.
  38. property alias iconSource: collapseButton.source
  39. property alias iconColor: collapseButton.color
  40. // The icon size (it's always drawn as a square)
  41. property alias iconSize: collapseButton.height
  42. // Is the "drawer" open?
  43. readonly property alias expanded: content.visible
  44. property alias expandedHighlightColor: expandedHighlight.color
  45. // What should the radius of the header be. This is also influenced by the headerCornerSide
  46. property alias headerRadius: background.radius
  47. // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right.
  48. property alias headerCornerSide: background.cornerSide
  49. // Change the contentItem close behaviour
  50. property alias contentClosePolicy : content.closePolicy
  51. property alias headerShadowColor: shadow.color
  52. property alias enableHeaderShadow: shadow.visible
  53. property int shadowOffset: 2
  54. function toggleContent()
  55. {
  56. if (content.visible)
  57. {
  58. content.close()
  59. }
  60. else
  61. {
  62. content.open()
  63. }
  64. }
  65. // Add this binding since the background color is not updated otherwise
  66. Binding
  67. {
  68. target: background
  69. property: "color"
  70. value: base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
  71. }
  72. // The panel needs to close when it becomes disabled
  73. Connections
  74. {
  75. target: base
  76. onEnabledChanged:
  77. {
  78. if (!base.enabled && expanded)
  79. {
  80. toggleContent()
  81. }
  82. }
  83. }
  84. implicitHeight: 100 * screenScaleFactor
  85. implicitWidth: 400 * screenScaleFactor
  86. RoundedRectangle
  87. {
  88. id: background
  89. property real padding: UM.Theme.getSize("default_margin").width
  90. color: base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
  91. anchors.fill: parent
  92. Label
  93. {
  94. id: disabledLabel
  95. visible: !base.enabled
  96. leftPadding: background.padding
  97. text: ""
  98. font: UM.Theme.getFont("default")
  99. renderType: Text.NativeRendering
  100. verticalAlignment: Text.AlignVCenter
  101. color: UM.Theme.getColor("text")
  102. height: parent.height
  103. }
  104. Item
  105. {
  106. anchors.fill: parent
  107. visible: base.enabled
  108. Loader
  109. {
  110. id: headerItemLoader
  111. anchors
  112. {
  113. left: parent.left
  114. right: collapseButton.visible ? collapseButton.left : parent.right
  115. top: parent.top
  116. bottom: parent.bottom
  117. margins: background.padding
  118. }
  119. }
  120. // A highlight that is shown when the content is expanded
  121. Rectangle
  122. {
  123. id: expandedHighlight
  124. width: parent.width
  125. height: UM.Theme.getSize("thick_lining").height
  126. color: UM.Theme.getColor("primary")
  127. visible: expanded
  128. anchors.bottom: parent.bottom
  129. }
  130. UM.RecolorImage
  131. {
  132. id: collapseButton
  133. anchors
  134. {
  135. right: parent.right
  136. verticalCenter: parent.verticalCenter
  137. margins: background.padding
  138. }
  139. source: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
  140. visible: source != ""
  141. width: UM.Theme.getSize("standard_arrow").width
  142. height: UM.Theme.getSize("standard_arrow").height
  143. color: UM.Theme.getColor("small_button_text")
  144. }
  145. }
  146. MouseArea
  147. {
  148. id: mouseArea
  149. anchors.fill: parent
  150. onClicked: toggleContent()
  151. hoverEnabled: true
  152. onEntered: background.color = headerHoverColor
  153. onExited: background.color = base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
  154. }
  155. }
  156. DropShadow
  157. {
  158. id: shadow
  159. // Don't blur the shadow
  160. radius: 0
  161. anchors.fill: background
  162. source: background
  163. verticalOffset: base.shadowOffset
  164. visible: true
  165. color: UM.Theme.getColor("action_button_shadow")
  166. // Should always be drawn behind the background.
  167. z: background.z - 1
  168. }
  169. Popup
  170. {
  171. id: content
  172. // Ensure that the content is located directly below the headerItem
  173. y: background.height + base.shadowOffset
  174. // Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left.
  175. // In case of right alignment, the 3x padding is due to left, right and padding between the button & text.
  176. x: contentAlignment == ExpandablePopup.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0
  177. padding: UM.Theme.getSize("default_margin").width
  178. closePolicy: Popup.CloseOnPressOutsideParent
  179. background: Cura.RoundedRectangle
  180. {
  181. cornerSide: Cura.RoundedRectangle.Direction.Down
  182. color: contentBackgroundColor
  183. border.width: UM.Theme.getSize("default_lining").width
  184. border.color: UM.Theme.getColor("lining")
  185. radius: UM.Theme.getSize("default_radius").width
  186. height: contentItem.implicitHeight || content.height
  187. }
  188. contentItem: Item {}
  189. onContentItemChanged:
  190. {
  191. // Since we want the size of the content to be set by the size of the content,
  192. // we need to do it like this.
  193. content.width = contentItem.width + 2 * content.padding
  194. content.height = contentItem.height + 2 * content.padding
  195. }
  196. }
  197. // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item.
  198. // Apparently the order in which these are handled matters and so the height is correctly updated if this is here.
  199. Connections
  200. {
  201. // Since it could be that the content is dynamically populated, we should also take these changes into account.
  202. target: content.contentItem
  203. onWidthChanged: content.width = content.contentItem.width + 2 * content.padding
  204. onHeightChanged: content.height = content.contentItem.height + 2 * content.padding
  205. }
  206. }