ExpandableComponent.qml 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import QtQuick 2.7
  2. import QtQuick.Controls 2.3
  3. import UM 1.2 as UM
  4. import Cura 1.0 as Cura
  5. import QtGraphicalEffects 1.0 // For the dropshadow
  6. // The expandable component has 2 major sub components:
  7. // * The headerItem; Always visible and should hold some info about what happens if the component is expanded
  8. // * The contentItem; The content that needs to be shown if the component is expanded.
  9. Item
  10. {
  11. id: base
  12. // Enumeration with the different possible alignments of the content with respect of the headerItem
  13. enum ContentAlignment
  14. {
  15. AlignLeft,
  16. AlignRight
  17. }
  18. // The headerItem holds the QML item that is always displayed.
  19. property alias headerItem: headerItemLoader.sourceComponent
  20. // The contentItem holds the QML item that is shown when the "open" button is pressed
  21. property alias contentItem: content.contentItem
  22. property color contentBackgroundColor: UM.Theme.getColor("action_button")
  23. property color headerBackgroundColor: UM.Theme.getColor("action_button")
  24. property color headerActiveColor: UM.Theme.getColor("secondary")
  25. property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
  26. property alias enabled: mouseArea.enabled
  27. // Defines the alignment of the content with respect of the headerItem, by default to the right
  28. property int contentAlignment: ExpandableComponent.ContentAlignment.AlignRight
  29. // How much spacing is needed around the contentItem
  30. property alias contentPadding: content.padding
  31. // How much spacing is needed for the contentItem by Y coordinate
  32. property var contentSpacingY: UM.Theme.getSize("narrow_margin").width
  33. // How much padding is needed around the header & button
  34. property alias headerPadding: background.padding
  35. // What icon should be displayed on the right.
  36. property alias iconSource: collapseButton.source
  37. property alias iconColor: collapseButton.color
  38. // The icon size (it's always drawn as a square)
  39. property alias iconSize: collapseButton.height
  40. // Is the "drawer" open?
  41. readonly property alias expanded: content.visible
  42. // What should the radius of the header be. This is also influenced by the headerCornerSide
  43. property alias headerRadius: background.radius
  44. // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right.
  45. property alias headerCornerSide: background.cornerSide
  46. property alias headerShadowColor: shadow.color
  47. property alias enableHeaderShadow: shadow.visible
  48. property int shadowOffset: 2
  49. function toggleContent()
  50. {
  51. content.visible = !content.visible
  52. }
  53. implicitHeight: 100 * screenScaleFactor
  54. implicitWidth: 400 * screenScaleFactor
  55. RoundedRectangle
  56. {
  57. id: background
  58. property real padding: UM.Theme.getSize("default_margin").width
  59. color: headerBackgroundColor
  60. anchors.fill: parent
  61. Loader
  62. {
  63. id: headerItemLoader
  64. anchors
  65. {
  66. left: parent.left
  67. right: collapseButton.visible ? collapseButton.left : parent.right
  68. top: parent.top
  69. bottom: parent.bottom
  70. margins: background.padding
  71. }
  72. }
  73. UM.RecolorImage
  74. {
  75. id: collapseButton
  76. anchors
  77. {
  78. right: parent.right
  79. verticalCenter: parent.verticalCenter
  80. margins: background.padding
  81. }
  82. source: UM.Theme.getIcon("pencil")
  83. visible: source != "" && base.enabled
  84. width: UM.Theme.getSize("standard_arrow").width
  85. height: UM.Theme.getSize("standard_arrow").height
  86. color: UM.Theme.getColor("small_button_text")
  87. }
  88. MouseArea
  89. {
  90. id: mouseArea
  91. anchors.fill: parent
  92. onClicked: toggleContent()
  93. hoverEnabled: true
  94. onEntered: background.color = headerHoverColor
  95. onExited: background.color = expanded ? headerActiveColor : headerBackgroundColor
  96. }
  97. }
  98. DropShadow
  99. {
  100. id: shadow
  101. // Don't blur the shadow
  102. radius: 0
  103. anchors.fill: background
  104. source: background
  105. verticalOffset: base.shadowOffset
  106. visible: true
  107. color: UM.Theme.getColor("action_button_shadow")
  108. // Should always be drawn behind the background.
  109. z: background.z - 1
  110. }
  111. Control
  112. {
  113. id: content
  114. visible: false
  115. // Ensure that the content is located directly below the headerItem
  116. y: background.height + base.shadowOffset + base.contentSpacingY
  117. // Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left.
  118. // In case of right alignment, the 3x padding is due to left, right and padding between the button & text.
  119. x: contentAlignment == ExpandableComponent.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0
  120. padding: UM.Theme.getSize("default_margin").width
  121. background: Cura.RoundedRectangle
  122. {
  123. cornerSide: Cura.RoundedRectangle.Direction.Down
  124. color: contentBackgroundColor
  125. border.width: UM.Theme.getSize("default_lining").width
  126. border.color: UM.Theme.getColor("lining")
  127. radius: UM.Theme.getSize("default_radius").width
  128. }
  129. contentItem: Item {}
  130. onContentItemChanged:
  131. {
  132. // Since we want the size of the content to be set by the size of the content,
  133. // we need to do it like this.
  134. content.width = contentItem.width + 2 * content.padding
  135. content.height = contentItem.height + 2 * content.padding
  136. }
  137. }
  138. // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item.
  139. // Apparently the order in which these are handled matters and so the height is correctly updated if this is here.
  140. Connections
  141. {
  142. // Since it could be that the content is dynamically populated, we should also take these changes into account.
  143. target: content.contentItem
  144. onWidthChanged: content.width = content.contentItem.width + 2 * content.padding
  145. onHeightChanged: content.height = content.contentItem.height + 2 * content.padding
  146. }
  147. }