GenericPopUp.qml 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright (c) 2019 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.2
  4. import QtQuick.Controls 2.0
  5. import QtGraphicalEffects 1.0
  6. import UM 1.3 as UM
  7. /**
  8. * This is a generic pop-up element which can be supplied with a target and a content item. The
  9. * content item will appear to the left, right, above, or below the target depending on the value of
  10. * the direction property
  11. */
  12. Popup
  13. {
  14. id: base
  15. /**
  16. * The target item is what the pop-up is "tied" to, usually a button
  17. */
  18. property var target
  19. /**
  20. * Which direction should the pop-up "point"?
  21. * Possible values include:
  22. * - "up"
  23. * - "down"
  24. * - "left"
  25. * - "right"
  26. */
  27. property string direction: "down"
  28. /**
  29. * We save the default direction so that if a pop-up was flipped but later has space (i.e. it
  30. * moved), we can unflip it back to the default direction.
  31. */
  32. property string originalDirection: ""
  33. /**
  34. * Should the popup close when you click outside it? For example, this is
  35. * disabled by the InfoBlurb component since it's opened and closed using mouse
  36. * hovers, not clicks.
  37. */
  38. property bool closeOnClick: true
  39. /**
  40. * Use white for context menus, dark grey for info blurbs!
  41. */
  42. property var color: "#ffffff" // TODO: Theme!
  43. Component.onCompleted:
  44. {
  45. recalculatePosition()
  46. // Set the direction here so it's only set once and never mutated
  47. originalDirection = (' ' + direction).slice(1)
  48. }
  49. background: Item
  50. {
  51. anchors.fill: parent
  52. DropShadow
  53. {
  54. anchors.fill: pointedRectangle
  55. color: UM.Theme.getColor("monitor_shadow")
  56. radius: UM.Theme.getSize("monitor_shadow_radius").width
  57. source: pointedRectangle
  58. transparentBorder: true
  59. verticalOffset: 2 * screenScaleFactor
  60. }
  61. Item
  62. {
  63. id: pointedRectangle
  64. anchors
  65. {
  66. horizontalCenter: parent.horizontalCenter
  67. verticalCenter: parent.verticalCenter
  68. }
  69. height: parent.height - 10 * screenScaleFactor // Because of the shadow
  70. width: parent.width - 10 * screenScaleFactor // Because of the shadow
  71. Rectangle
  72. {
  73. id: point
  74. anchors
  75. {
  76. horizontalCenter:
  77. {
  78. switch(direction)
  79. {
  80. case "left":
  81. return bloop.left
  82. case "right":
  83. return bloop.right
  84. default:
  85. return bloop.horizontalCenter
  86. }
  87. }
  88. verticalCenter:
  89. {
  90. switch(direction)
  91. {
  92. case "up":
  93. return bloop.top
  94. case "down":
  95. return bloop.bottom
  96. default:
  97. return bloop.verticalCenter
  98. }
  99. }
  100. }
  101. color: base.color
  102. height: 12 * screenScaleFactor
  103. transform: Rotation
  104. {
  105. angle: 45
  106. origin.x: point.width / 2
  107. origin.y: point.height / 2
  108. }
  109. width: height
  110. }
  111. Rectangle
  112. {
  113. id: bloop
  114. anchors
  115. {
  116. fill: parent
  117. leftMargin: direction == "left" ? 8 * screenScaleFactor : 0
  118. rightMargin: direction == "right" ? 8 * screenScaleFactor : 0
  119. topMargin: direction == "up" ? 8 * screenScaleFactor : 0
  120. bottomMargin: direction == "down" ? 8 * screenScaleFactor : 0
  121. }
  122. color: base.color
  123. width: parent.width
  124. }
  125. }
  126. }
  127. visible: false
  128. onClosed: visible = false
  129. onOpened:
  130. {
  131. // Flip orientation if necessary
  132. recalculateOrientation()
  133. // Fix position if necessary
  134. recalculatePosition()
  135. // Show the pop up
  136. visible = true
  137. }
  138. closePolicy: closeOnClick ? Popup.CloseOnPressOutside : Popup.NoAutoClose
  139. clip: true
  140. padding: UM.Theme.getSize("monitor_shadow_radius").width
  141. topPadding: direction == "up" ? padding + 8 * screenScaleFactor : padding
  142. bottomPadding: direction == "down" ? padding + 8 * screenScaleFactor : padding
  143. leftPadding: direction == "left" ? padding + 8 * screenScaleFactor : padding
  144. rightPadding: direction == "right" ? padding + 8 * screenScaleFactor : padding
  145. function recalculatePosition() {
  146. // Stupid pop-up logic causes the pop-up to resize, so let's compute what it SHOULD be
  147. const realWidth = contentItem.implicitWidth + leftPadding + rightPadding
  148. const realHeight = contentItem.implicitHeight + topPadding + bottomPadding
  149. var centered = {
  150. x: target.x + target.width / 2 - realWidth / 2,
  151. y: target.y + target.height / 2 - realHeight / 2
  152. }
  153. switch(direction)
  154. {
  155. case "left":
  156. x = target.x + target.width
  157. y = centered.y
  158. break
  159. case "right":
  160. x = target.x - realWidth
  161. y = centered.y
  162. break
  163. case "up":
  164. x = centered.x
  165. y = target.y + target.height
  166. break
  167. case "down":
  168. x = centered.x
  169. y = target.y - realHeight
  170. break
  171. }
  172. }
  173. function recalculateOrientation() {
  174. var availableSpace
  175. var targetPosition = target.mapToItem(monitorFrame, 0, 0)
  176. // Stupid pop-up logic causes the pop-up to resize, so let's compute what it SHOULD be
  177. const realWidth = contentItem.implicitWidth + leftPadding + rightPadding
  178. const realHeight = contentItem.implicitHeight + topPadding + bottomPadding
  179. switch(originalDirection)
  180. {
  181. case "up":
  182. availableSpace = monitorFrame.height - (targetPosition.y + target.height)
  183. direction = availableSpace < realHeight ? "down" : originalDirection
  184. break
  185. case "down":
  186. availableSpace = targetPosition.y
  187. direction = availableSpace < realHeight ? "up" : originalDirection
  188. break
  189. case "right":
  190. availableSpace = targetPosition.x
  191. direction = availableSpace < realWidth ? "left" : originalDirection
  192. break
  193. case "left":
  194. availableSpace = monitorFrame.width - (targetPosition.x + target.width)
  195. direction = availableSpace < realWidth ? "right" : originalDirection
  196. break
  197. }
  198. }
  199. }