GenericPopUp.qml 6.8 KB

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