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