ExpandablePopup.qml 8.3 KB

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