1/**************************************************************************** 2** 3** Copyright (C) 2021 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of Qt Creator. 7** 8** Commercial License Usage 9** Licensees holding valid commercial Qt licenses may use this file in 10** accordance with the commercial license agreement provided with the 11** Software or, alternatively, in accordance with the terms contained in 12** a written agreement between you and The Qt Company. For licensing terms 13** and conditions see https://www.qt.io/terms-conditions. For further 14** information use the contact form at https://www.qt.io/contact-us. 15** 16** GNU General Public License Usage 17** Alternatively, this file may be used under the terms of the GNU 18** General Public License version 3 as published by the Free Software 19** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20** included in the packaging of this file. Please review the following 21** information to ensure the GNU General Public License requirements will 22** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23** 24****************************************************************************/ 25 26import QtQuick 2.15 27import QtQuick.Shapes 1.15 28import QtQuick.Templates 2.15 as T 29import StudioTheme 1.0 as StudioTheme 30 31Rectangle { 32 id: linkIndicator 33 34 property Item myControl 35 36 property bool linked: linkXZ.linked || linkYZ.linked || linkXY.linked 37 38 color: "transparent" 39 border.color: "transparent" 40 41 implicitWidth: StudioTheme.Values.height 42 implicitHeight: StudioTheme.Values.height 43 44 z: 10 45 46 function linkAll() { 47 linkXY.linked = linkYZ.linked = linkXZ.linked = true 48 } 49 50 function unlinkAll() { 51 linkXY.linked = linkYZ.linked = linkXZ.linked = false 52 } 53 54 T.Label { 55 id: linkIndicatorIcon 56 anchors.fill: parent 57 text: linkIndicator.linked ? StudioTheme.Constants.linked 58 : StudioTheme.Constants.unLinked 59 visible: true 60 color: StudioTheme.Values.themeTextColor 61 font.family: StudioTheme.Constants.iconFont.family 62 font.pixelSize: StudioTheme.Values.myIconFontSize 63 verticalAlignment: Text.AlignVCenter 64 horizontalAlignment: Text.AlignHCenter 65 } 66 67 MouseArea { 68 id: mouseArea 69 anchors.fill: parent 70 hoverEnabled: true 71 onPressed: linkPopup.opened ? linkPopup.close() : linkPopup.open() 72 } 73 74 T.Popup { 75 id: linkPopup 76 77 x: 50 78 y: 0 79 80 // TODO proper size 81 width: 20 + (3 * StudioTheme.Values.height) 82 height: 20 + (3 * StudioTheme.Values.height) 83 84 padding: StudioTheme.Values.border 85 margins: 0 // If not defined margin will be -1 86 87 closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent 88 | T.Popup.CloseOnEscape | T.Popup.CloseOnReleaseOutside 89 | T.Popup.CloseOnReleaseOutsideParent 90 91 contentItem: Item { 92 id: content 93 anchors.fill: parent 94 95 Rectangle { 96 width: triangle.diameter 97 height: triangle.diameter 98 anchors.centerIn: parent 99 color: "transparent" 100 101 Shape { 102 // Kept for debugging purposes 103 id: triangle 104 105 visible: false 106 107 property real diameter: 30 * StudioTheme.Values.scaleFactor 108 property real radius: triangle.diameter * 0.5 109 110 width: triangle.diameter 111 height: triangle.diameter 112 113 ShapePath { 114 id: path 115 116 property real height: triangle.diameter * Math.cos(Math.PI / 6) 117 118 property vector2d pX: Qt.vector2d(triangle.radius, 0) 119 property vector2d pY: Qt.vector2d(0, path.height) 120 property vector2d pZ: Qt.vector2d(triangle.diameter, path.height) 121 122 property vector2d center: Qt.vector2d(triangle.radius, triangle.radius) 123 124 strokeWidth: StudioTheme.Values.border 125 strokeColor: triangleMouseArea.containsMouse ? "white" : "gray" 126 strokeStyle: ShapePath.SolidLine 127 fillColor: "transparent" 128 129 startX: path.pX.x 130 startY: path.pX.y 131 132 PathLine { x: path.pY.x; y: path.pY.y } 133 PathLine { x: path.pZ.x; y: path.pZ.y } 134 PathLine { x: path.pX.x; y: path.pX.y } 135 } 136 } 137 138 Item { 139 id: triangleMouseArea 140 141 anchors.fill: parent 142 143 property alias mouseX: tmpMouseArea.mouseX 144 property alias mouseY: tmpMouseArea.mouseY 145 146 signal clicked 147 148 function sign(p1, p2, p3) { 149 return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y) 150 } 151 152 function pointInTriangle(pt, v1, v2, v3) { 153 var d1 = sign(pt, v1, v2) 154 var d2 = sign(pt, v2, v3) 155 var d3 = sign(pt, v3, v1) 156 157 var has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0) 158 var has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0) 159 160 return !(has_neg && has_pos) 161 } 162 163 property bool containsMouse: { 164 if (!tmpMouseArea.containsMouse) 165 return false 166 167 var point = Qt.vector2d(triangleMouseArea.mouseX, triangleMouseArea.mouseY) 168 return pointInTriangle(point, path.pX, path.pZ, path.pY) 169 } 170 171 172 onClicked: { 173 if (linkXZ.linked && linkYZ.linked && linkXY.linked) 174 linkIndicator.unlinkAll() 175 else 176 linkIndicator.linkAll() 177 } 178 179 MouseArea { 180 id: tmpMouseArea 181 anchors.fill: parent 182 hoverEnabled: true 183 acceptedButtons: Qt.LeftButton | Qt.RightButton 184 onClicked: { 185 if (triangleMouseArea.containsMouse) 186 triangleMouseArea.clicked() 187 } 188 } 189 } 190 191 // https://stackoverflow.com/questions/38164074/how-to-create-a-round-mouse-area-in-qml 192 // https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle 193 194 T.Label { 195 id: triangleIcon 196 anchors.fill: parent 197 anchors.bottomMargin: 3 * StudioTheme.Values.scaleFactor 198 text: StudioTheme.Constants.linkTriangle 199 visible: true 200 color: triangleMouseArea.containsMouse ? "white" : "gray" 201 font.family: StudioTheme.Constants.iconFont.family 202 font.pixelSize: StudioTheme.Values.myIconFontSize * 3 203 verticalAlignment: Text.AlignVCenter 204 horizontalAlignment: Text.AlignHCenter 205 } 206 207 LinkIndicator3DComponent { 208 id: linkXZ 209 pointA: path.pX 210 pointB: path.pZ 211 rotation: 105 // 60 212 } 213 214 LinkIndicator3DComponent { 215 id: linkYZ 216 pointA: path.pZ 217 pointB: path.pY 218 rotation: 45 // -180 219 } 220 221 LinkIndicator3DComponent { 222 id: linkXY 223 pointA: path.pY 224 pointB: path.pX 225 rotation: -15 // -60 226 } 227 228 T.Label { 229 id: xIcon 230 x: path.pX.x - (xIcon.width * 0.5) 231 y: path.pX.y - xIcon.height 232 text: "X" 233 color: StudioTheme.Values.theme3DAxisXColor 234 235 font.family: StudioTheme.Constants.font.family 236 font.pixelSize: StudioTheme.Values.myFontSize 237 verticalAlignment: Text.AlignVCenter 238 horizontalAlignment: Text.AlignHCenter 239 } 240 T.Label { 241 id: yIcon 242 x: path.pY.x - yIcon.width - (4.0 * StudioTheme.Values.scaleFactor) 243 y: path.pY.y - (yIcon.height * 0.5) 244 text: "Y" 245 color: StudioTheme.Values.theme3DAxisYColor 246 247 font.family: StudioTheme.Constants.font.family 248 font.pixelSize: StudioTheme.Values.myFontSize 249 verticalAlignment: Text.AlignVCenter 250 horizontalAlignment: Text.AlignHCenter 251 } 252 T.Label { 253 id: zIcon 254 x: path.pZ.x + (4.0 * StudioTheme.Values.scaleFactor) 255 y: path.pZ.y - (zIcon.height * 0.5) 256 text: "Z" 257 color: StudioTheme.Values.theme3DAxisZColor 258 259 font.family: StudioTheme.Constants.font.family 260 font.pixelSize: StudioTheme.Values.myFontSize 261 verticalAlignment: Text.AlignVCenter 262 horizontalAlignment: Text.AlignHCenter 263 } 264 265 } 266 } 267 268 background: Rectangle { 269 color: StudioTheme.Values.themeControlBackground 270 border.color: StudioTheme.Values.themeInteraction 271 border.width: StudioTheme.Values.border 272 } 273 274 enter: Transition {} 275 exit: Transition {} 276 } 277 278 states: [ 279 State { 280 name: "default" 281 when: !mouseArea.containsMouse && !linkPopup.opened 282 PropertyChanges { 283 target: linkIndicatorIcon 284 color: StudioTheme.Values.themeLinkIndicatorColor 285 } 286 }, 287 State { 288 name: "hover" 289 when: mouseArea.containsMouse && !linkPopup.opened 290 PropertyChanges { 291 target: linkIndicatorIcon 292 color: StudioTheme.Values.themeLinkIndicatorColorHover 293 } 294 }, 295 State { 296 name: "active" 297 when: linkPopup.opened 298 PropertyChanges { 299 target: linkIndicatorIcon 300 color: StudioTheme.Values.themeLinkIndicatorColorInteraction 301 } 302 } 303 ] 304} 305