1/* 2 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org> 3 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com> 4 SPDX-License-Identifier: GPL-2.0-or-later 5*/ 6 7import QtQuick 2.7 8import QtQuick.Layouts 1.0 9import QtGraphicalEffects 1.0 10 11import org.kde.plasma.plasmoid 2.0 12import org.kde.plasma.core 2.0 as PlasmaCore 13import org.kde.plasma.components 2.0 as PlasmaComponents 14import org.kde.kquickcontrolsaddons 2.0 15 16import org.kde.latte.core 0.2 as LatteCore 17 18MouseArea { 19 id: configurationArea 20 z: 1000 21 22 width: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? root.width : thickness 23 height: plasmoid.formFactor === PlasmaCore.Types.Vertical ? root.height : thickness 24 25 visible: root.inConfigureAppletsMode 26 hoverEnabled: root.inConfigureAppletsMode 27 28 focus: true 29 cursorShape: { 30 if (currentApplet && tooltip.visible && currentApplet.latteStyleApplet) { 31 return root.isHorizontal ? Qt.SizeHorCursor : Qt.SizeVerCursor; 32 } 33 34 return Qt.ArrowCursor; 35 } 36 37 property bool isResizingLeft: false 38 property bool isResizingRight: false 39 property Item currentApplet 40 property Item previousCurrentApplet 41 readonly property alias draggedPlaceHolder: placeHolder 42 43 property Item currentHoveredLayout: { 44 if (placeHolder.parent !== configurationArea) { 45 return placeHolder.parent; 46 } 47 48 return currentApplet ? currentApplet.parent : null 49 } 50 51 property int lastX 52 property int lastY 53 property int appletX 54 property int appletY 55 56 readonly property int thickness: metrics.mask.thickness.maxNormal - metrics.extraThicknessForNormal 57 readonly property int spacerHandleSize: units.smallSpacing 58 59 onHeightChanged: tooltip.visible = false; 60 onWidthChanged: tooltip.visible = false; 61 62 63 function hoveredItem(x, y) { 64 //! main layout 65 var relevantLayout = mapFromItem(layoutsContainer.mainLayout, 0, 0); 66 var item = layoutsContainer.mainLayout.childAt(x-relevantLayout.x, y-relevantLayout.y); 67 68 if (!item) { 69 // start layout 70 relevantLayout = mapFromItem(layoutsContainer.startLayout,0,0); 71 item = layoutsContainer.startLayout.childAt(x-relevantLayout.x, y-relevantLayout.y); 72 } 73 74 if (!item) { 75 // end layout 76 relevantLayout = mapFromItem(layoutsContainer.endLayout,0,0); 77 item = layoutsContainer.endLayout.childAt(x-relevantLayout.x, y-relevantLayout.y); 78 } 79 80 return item; 81 } 82 83 function relevantLayoutForApplet(curapplet) { 84 var relevantLayout; 85 86 if (curapplet.parent === layoutsContainer.mainLayout) { 87 relevantLayout = mapFromItem(layoutsContainer.mainLayout, 0, 0); 88 } else if (curapplet.parent === layoutsContainer.startLayout) { 89 relevantLayout = mapFromItem(layoutsContainer.startLayout, 0, 0); 90 } else if (curapplet.parent === layoutsContainer.endLayout) { 91 relevantLayout = mapFromItem(layoutsContainer.endLayout, 0, 0); 92 } 93 94 return relevantLayout; 95 } 96 97 98 onPositionChanged: { 99 if (pressed) { 100 if(currentApplet){ 101 if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { 102 currentApplet.y += (mouse.y - lastY); 103 } else { 104 currentApplet.x += (mouse.x - lastX); 105 } 106 } 107 108 lastX = mouse.x; 109 lastY = mouse.y; 110 111 var mousesink = {x: mouse.x, y: mouse.y}; 112 113 //! ignore thicknes moving at all cases 114 if (plasmoid.formFactor === PlasmaCore.Types.Horizontal) { 115 mousesink.y = configurationArea.height / 2; 116 } else { 117 mousesink.x = configurationArea.width / 2; 118 } 119 120 var item = hoveredItem(mousesink.x, mousesink.y); 121 122 if (item && item !== placeHolder) { 123 placeHolder.parent = configurationArea; 124 var posInItem = mapToItem(item, mousesink.x, mousesink.y); 125 126 if ((plasmoid.formFactor === PlasmaCore.Types.Vertical && posInItem.y < item.height/2) || 127 (plasmoid.formFactor !== PlasmaCore.Types.Vertical && posInItem.x < item.width/2)) { 128 fastLayoutManager.insertBefore(item, placeHolder); 129 } else { 130 fastLayoutManager.insertAfter(item, placeHolder); 131 } 132 } 133 134 } else { 135 var item = hoveredItem(mouse.x, mouse.y); 136 137 if (root.dragOverlay) { 138 root.dragOverlay.currentApplet = item; 139 } else { 140 currentApplet = null; 141 root.dragOverlay.currentApplet = null; 142 } 143 } 144 145 if (root.dragOverlay.currentApplet) { 146 hideTimer.stop(); 147 148 tooltip.visible = true; 149 tooltip.raise(); 150 } 151 } 152 153 onExited: hideTimer.restart(); 154 155 onCurrentAppletChanged: { 156 previousCurrentApplet = currentApplet; 157 158 if (!currentApplet || !root.dragOverlay.currentApplet) { 159 hideTimer.restart(); 160 return; 161 } 162 163 var relevantLayout = relevantLayoutForApplet(currentApplet) ; 164 165 if (!relevantLayout) { 166 return; 167 } 168 169 lockButton.checked = currentApplet.lockZoom; 170 colorizingButton.checked = !currentApplet.userBlocksColorizing; 171 } 172 173 onPressed: { 174 if (!root.dragOverlay.currentApplet) { 175 return; 176 } 177 178 var relevantApplet = mapFromItem(currentApplet, 0, 0); 179 var rootArea = mapFromItem(root, 0, 0); 180 181 appletX = mouse.x - relevantApplet.x + rootArea.x; 182 appletY = mouse.y - relevantApplet.y + rootArea.y; 183 184 lastX = mouse.x; 185 lastY = mouse.y; 186 fastLayoutManager.insertBefore(currentApplet, placeHolder); 187 currentApplet.parent = root; 188 currentApplet.x = root.isHorizontal ? lastX - currentApplet.width/2 : lastX-appletX; 189 currentApplet.y = root.isVertical ? lastY - currentApplet.height/2 : lastY-appletY; 190 currentApplet.z = 900; 191 } 192 193 onReleased: { 194 if (!handle.visible) { 195 tooltip.visible = false; 196 } 197 198 if (!root.dragOverlay.currentApplet) { 199 return; 200 } 201 202 if(currentApplet && currentApplet.applet){ 203 if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { 204 currentApplet.applet.configuration.length = handle.height; 205 } else { 206 currentApplet.applet.configuration.length = handle.width; 207 } 208 } 209 210 configurationArea.isResizingLeft = false; 211 configurationArea.isResizingRight = false; 212 213 fastLayoutManager.insertBefore(placeHolder, currentApplet); 214 placeHolder.parent = configurationArea; 215 currentApplet.z = 1; 216 217 var relevantLayout = mapFromItem(layoutsContainer.mainLayout, 0, 0); 218 219 if (root.myView.alignment === LatteCore.Types.Justify) { 220 fastLayoutManager.moveAppletsBasedOnJustifyAlignment(); 221 } 222 223 fastLayoutManager.save(); 224 layouter.updateSizeForAppletsInFill(); 225 } 226 227 onWheel: { 228 if (!currentApplet || !currentApplet.latteStyleApplet) { 229 return; 230 } 231 232 var angle = wheel.angleDelta.y / 8; 233 234 if (angle > 12) 235 currentApplet.latteStyleApplet.increaseLength(); 236 else if (angle < 12) 237 currentApplet.latteStyleApplet.decreaseLength(); 238 } 239 240 Connections { 241 target: currentApplet 242 onWidthChanged: { 243 if (configurationArea.pressed && root.isHorizontal) { 244 currentApplet.x = configurationArea.lastX - currentApplet.width/2; 245 } 246 } 247 248 onHeightChanged: { 249 if (configurationArea.pressed && root.isVertical) { 250 currentApplet.y = configurationArea.lastY - currentApplet.height/2; 251 } 252 } 253 } 254 255 Item { 256 id: placeHolder 257 visible: configurationArea.pressed 258 width: currentApplet !== null ? (root.isVertical ? currentApplet.width : Math.min(root.maxLength / 2, currentApplet.width)) : 0 259 height: currentApplet !== null ? (!root.isVertical ? currentApplet.height : Math.min(root.maxLength / 2, currentApplet.height)) : 0 260 261 readonly property bool isPlaceHolder: true 262 readonly property int length: root.isVertical ? height : width 263 } 264 265 Timer { 266 id: hideTimer 267 interval: animations.duration.large * 2 268 onTriggered: { 269 if (!tooltipMouseArea.containsMouse) { 270 tooltip.visible = false; 271 currentApplet = null; 272 } 273 } 274 } 275 276 Item { 277 id: handle 278 parent: currentApplet ? currentApplet : configurationArea 279 anchors.fill: parent 280 visible: currentApplet && (configurationArea.containsMouse || tooltipMouseArea.containsMouse) 281 282 Loader { 283 anchors.fill: parent 284 active: root.debug.graphicsEnabled 285 sourceComponent: Rectangle { 286 color: "transparent" 287 border.width:1 288 border.color: "yellow" 289 } 290 } 291 292 //BEGIN functions 293 //END functions 294 295 Item { 296 id: handleVisualItem 297 width: root.isHorizontal ? parent.width : thickness 298 height: root.isHorizontal ? thickness : parent.height 299 300 readonly property int thickness: root.isHorizontal ? parent.height - metrics.margin.screenEdge : parent.width - metrics.margin.screenEdge 301 302 Rectangle{ 303 anchors.fill: parent 304 color: theme.backgroundColor 305 radius: 3 306 opacity: 0.35 307 } 308 309 PlasmaCore.IconItem { 310 source: "transform-move" 311 width: Math.min(144, root.metrics.iconSize) 312 height: width 313 anchors.centerIn: parent 314 opacity: 0.9 315 layer.enabled: root.environment.isGraphicsSystemAccelerated 316 layer.effect: DropShadow { 317 radius: root.myView.itemShadow.size 318 fast: true 319 samples: 2 * radius 320 color: root.myView.itemShadow.shadowColor 321 322 verticalOffset: 2 323 } 324 } 325 326 327 states:[ 328 State{ 329 name: "bottom" 330 when: plasmoid.location === PlasmaCore.Types.BottomEdge 331 332 AnchorChanges{ 333 target: handleVisualItem; 334 anchors.horizontalCenter: parent.horizontalCenter; anchors.verticalCenter: undefined; 335 anchors.right: undefined; anchors.left: undefined; anchors.top: undefined; anchors.bottom: parent.bottom; 336 } 337 PropertyChanges{ 338 target: handleVisualItem; 339 anchors.leftMargin: 0; anchors.rightMargin: 0; anchors.topMargin:0; anchors.bottomMargin: metrics.margin.screenEdge; 340 anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; 341 } 342 }, 343 State{ 344 name: "top" 345 when: plasmoid.location === PlasmaCore.Types.TopEdge 346 347 AnchorChanges{ 348 target: handleVisualItem; 349 anchors.horizontalCenter: parent.horizontalCenter; anchors.verticalCenter: undefined; 350 anchors.right: undefined; anchors.left: undefined; anchors.top: parent.top; anchors.bottom: undefined; 351 } 352 PropertyChanges{ 353 target: handleVisualItem; 354 anchors.leftMargin: 0; anchors.rightMargin: 0; anchors.topMargin: metrics.margin.screenEdge; anchors.bottomMargin: 0; 355 anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; 356 } 357 }, 358 State{ 359 name: "left" 360 when: plasmoid.location === PlasmaCore.Types.LeftEdge 361 362 AnchorChanges{ 363 target: handleVisualItem; 364 anchors.horizontalCenter: undefined; anchors.verticalCenter: parent.verticalCenter; 365 anchors.right: undefined; anchors.left: parent.left; anchors.top: undefined; anchors.bottom: undefined; 366 } 367 PropertyChanges{ 368 target: handleVisualItem; 369 anchors.leftMargin: metrics.margin.screenEdge; anchors.rightMargin: 0; anchors.topMargin:0; anchors.bottomMargin: 0; 370 anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; 371 } 372 }, 373 State{ 374 name: "right" 375 when: plasmoid.location === PlasmaCore.Types.RightEdge 376 377 AnchorChanges{ 378 target: handleVisualItem; 379 anchors.horizontalCenter: undefined; anchors.verticalCenter: parent.verticalCenter; 380 anchors.right: parent.right; anchors.left: undefined; anchors.top: undefined; anchors.bottom: undefined; 381 } 382 PropertyChanges{ 383 target: handleVisualItem; 384 anchors.leftMargin: 0; anchors.rightMargin: metrics.margin.screenEdge; anchors.topMargin:0; anchors.bottomMargin: 0; 385 anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; 386 } 387 } 388 ] 389 390 } 391 392 Behavior on opacity { 393 NumberAnimation { 394 duration: animations.duration.large 395 easing.type: Easing.InOutQuad 396 } 397 } 398 } 399 PlasmaCore.Dialog { 400 id: tooltip 401 visualParent: currentApplet 402 403 type: PlasmaCore.Dialog.Dock 404 flags: Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.BypassWindowManagerHint | Qt.ToolTip 405 location: plasmoid.location 406 407 onVisualParentChanged: { 408 if (visualParent && currentApplet 409 && (currentApplet.applet || currentApplet.isSeparator || currentApplet.isInternalViewSplitter)) { 410 411 configureButton.visible = !currentApplet.isInternalViewSplitter 412 && (currentApplet.applet.pluginName !== "org.kde.latte.plasmoid") 413 && currentApplet.applet.action("configure") 414 && currentApplet.applet.action("configure").enabled; 415 closeButton.visible = !currentApplet.isInternalViewSplitter && currentApplet.applet.action("remove") && currentApplet.applet.action("remove").enabled; 416 lockButton.visible = !currentApplet.isInternalViewSplitter 417 && !currentApplet.communicator.indexerIsSupported 418 && !currentApplet.isSeparator; 419 420 colorizingButton.visible = root.colorizerEnabled && !currentApplet.appletBlocksColorizing && !currentApplet.isInternalViewSplitter; 421 422 label.text = currentApplet.isInternalViewSplitter ? i18n("Justify Splitter") : currentApplet.applet.title; 423 } 424 } 425 426 mainItem: MouseArea { 427 id: tooltipMouseArea 428 enabled: currentApplet 429 width: handleRow.childrenRect.width + (2 * handleRow.spacing) 430 height: Math.max(configureButton.height, label.contentHeight, closeButton.height) 431 hoverEnabled: true 432 LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft 433 LayoutMirroring.childrenInherit: true 434 435 onEntered: hideTimer.stop(); 436 onExited: hideTimer.restart(); 437 438 Row { 439 id: handleRow 440 anchors.horizontalCenter: parent.horizontalCenter 441 spacing: 2*units.smallSpacing 442 443 Row{ 444 spacing: units.smallSpacing 445 PlasmaComponents.ToolButton { 446 id: configureButton 447 anchors.verticalCenter: parent.verticalCenter 448 iconSource: "configure" 449 tooltip: i18n("Configure applet") 450 onClicked: { 451 tooltip.visible = false; 452 currentApplet.applet.action("configure").trigger(); 453 } 454 } 455 456 PlasmaComponents.Label { 457 id: label 458 anchors.verticalCenter: parent.verticalCenter 459 anchors.rightMargin: units.smallSpacing 460 textFormat: Text.PlainText 461 maximumLineCount: 1 462 } 463 464 Row{ 465 spacing: units.smallSpacing/2 466 467 PlasmaComponents.ToolButton{ 468 id: colorizingButton 469 checkable: true 470 iconSource: "color-picker" 471 tooltip: i18n("Enable painting for this applet") 472 473 onCheckedChanged: { 474 currentApplet.userBlocksColorizing = !checked; 475 } 476 } 477 478 PlasmaComponents.ToolButton{ 479 id: lockButton 480 checkable: true 481 iconSource: checked ? "lock" : "unlock" 482 tooltip: i18n("Disable parabolic effect for this applet") 483 484 onCheckedChanged: { 485 currentApplet.lockZoom = checked; 486 } 487 } 488 489 PlasmaComponents.ToolButton { 490 id: closeButton 491 anchors.verticalCenter: parent.verticalCenter 492 iconSource: "delete" 493 tooltip: i18n("Remove applet") 494 onClicked: { 495 tooltip.visible = false; 496 if(currentApplet && currentApplet.applet) 497 currentApplet.applet.action("remove").trigger(); 498 } 499 } 500 } 501 } 502 } 503 } 504 } 505 506 states: [ 507 State { 508 name: "bottom" 509 when: (plasmoid.location === PlasmaCore.Types.BottomEdge) 510 511 AnchorChanges { 512 target: configurationArea 513 anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; 514 horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} 515 } 516 }, 517 State { 518 name: "top" 519 when: (plasmoid.location === PlasmaCore.Types.TopEdge) 520 521 AnchorChanges { 522 target: configurationArea 523 anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; 524 horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} 525 } 526 }, 527 State { 528 name: "left" 529 when: (plasmoid.location === PlasmaCore.Types.LeftEdge) 530 531 AnchorChanges { 532 target: configurationArea 533 anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; 534 horizontalCenter:undefined; verticalCenter:parent.verticalCenter} 535 } 536 }, 537 State { 538 name: "right" 539 when: (plasmoid.location === PlasmaCore.Types.RightEdge) 540 541 AnchorChanges { 542 target: configurationArea 543 anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; 544 horizontalCenter:undefined; verticalCenter:parent.verticalCenter} 545 } 546 } 547 ] 548} 549