// Copyright (c) 2019 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 import QtQuick.Controls 2.0 import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.1 import QtGraphicalEffects 1.0 import UM 1.3 as UM /** * This is a generic pop-up element which can be supplied with a target and a content item. The * content item will appear to the left, right, above, or below the target depending on the value of * the direction property */ Popup { id: base /** * The target item is what the pop-up is "tied" to, usually a button */ property var target /** * Which direction should the pop-up "point"? * Possible values include: * - "up" * - "down" * - "left" * - "right" */ property string direction: "down" /** * We save the default direction so that if a pop-up was flipped but later has space (i.e. it * moved), we can unflip it back to the default direction. */ property string originalDirection: "" /** * Should the popup close when you click outside it? For example, this is * disabled by the InfoBlurb component since it's opened and closed using mouse * hovers, not clicks. */ property bool closeOnClick: true /** * Use white for context menus, dark grey for info blurbs! */ property var color: "#ffffff" // TODO: Theme! Component.onCompleted: { recalculatePosition() // Set the direction here so it's only set once and never mutated originalDirection = (' ' + direction).slice(1) } background: Item { anchors.fill: parent DropShadow { anchors.fill: pointedRectangle color: UM.Theme.getColor("monitor_shadow") radius: UM.Theme.getSize("monitor_shadow_radius").width source: pointedRectangle transparentBorder: true verticalOffset: 2 * screenScaleFactor } Item { id: pointedRectangle anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } height: parent.height - 10 * screenScaleFactor // Because of the shadow width: parent.width - 10 * screenScaleFactor // Because of the shadow Rectangle { id: point anchors { horizontalCenter: { switch(direction) { case "left": return bloop.left case "right": return bloop.right default: return bloop.horizontalCenter } } verticalCenter: { switch(direction) { case "up": return bloop.top case "down": return bloop.bottom default: return bloop.verticalCenter } } } color: base.color height: 12 * screenScaleFactor transform: Rotation { angle: 45 origin.x: point.width / 2 origin.y: point.height / 2 } width: height } Rectangle { id: bloop anchors { fill: parent leftMargin: direction == "left" ? 8 * screenScaleFactor : 0 rightMargin: direction == "right" ? 8 * screenScaleFactor : 0 topMargin: direction == "up" ? 8 * screenScaleFactor : 0 bottomMargin: direction == "down" ? 8 * screenScaleFactor : 0 } color: base.color width: parent.width } } } visible: false onClosed: visible = false onOpened: { // Flip orientation if necessary recalculateOrientation() // Fix position if necessary recalculatePosition() // Show the pop up visible = true } closePolicy: closeOnClick ? Popup.CloseOnPressOutside : Popup.NoAutoClose clip: true padding: UM.Theme.getSize("monitor_shadow_radius").width topPadding: direction == "up" ? padding + 8 * screenScaleFactor : padding bottomPadding: direction == "down" ? padding + 8 * screenScaleFactor : padding leftPadding: direction == "left" ? padding + 8 * screenScaleFactor : padding rightPadding: direction == "right" ? padding + 8 * screenScaleFactor : padding function recalculatePosition() { // Stupid pop-up logic causes the pop-up to resize, so let's compute what it SHOULD be const realWidth = contentItem.implicitWidth + leftPadding + rightPadding const realHeight = contentItem.implicitHeight + topPadding + bottomPadding var centered = { x: target.x + target.width / 2 - realWidth / 2, y: target.y + target.height / 2 - realHeight / 2 } switch(direction) { case "left": x = target.x + target.width y = centered.y break case "right": x = target.x - realWidth y = centered.y break case "up": x = centered.x y = target.y + target.height break case "down": x = centered.x y = target.y - realHeight break } } function recalculateOrientation() { var availableSpace var targetPosition = target.mapToItem(monitorFrame, 0, 0) // Stupid pop-up logic causes the pop-up to resize, so let's compute what it SHOULD be const realWidth = contentItem.implicitWidth + leftPadding + rightPadding const realHeight = contentItem.implicitHeight + topPadding + bottomPadding switch(originalDirection) { case "up": availableSpace = monitorFrame.height - (targetPosition.y + target.height) direction = availableSpace < realHeight ? "down" : originalDirection break case "down": availableSpace = targetPosition.y direction = availableSpace < realHeight ? "up" : originalDirection break case "right": availableSpace = targetPosition.x direction = availableSpace < realWidth ? "left" : originalDirection break case "left": availableSpace = monitorFrame.width - (targetPosition.x + target.width) direction = availableSpace < realWidth ? "right" : originalDirection break } } }