ExpandablePopup.qml 8.1 KB

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