/*SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>SPDX-FileCopyrightText: 2022 NiccolΓ² Venerandi <niccolo@venerandi.com>SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>SPDX-License-Identifier: GPL-2.0-or-later*/import QtQuickimport QtQuick.Layoutsimport QtQuick.Windowimport org.kde.draganddrop as DragDropimport org.kde.plasma.plasmoidimport org.kde.plasma.core as PlasmaCoreimport org.kde.plasma.components as PlasmaComponents3import org.kde.plasma.extras as PlasmaExtrasimport org.kde.kirigami as KirigamiMouseArea {id: configurationAreaz: 1000hoverEnabled: truerequired property var layoutManagerrequired property ListModel appletsModelrequired property GridLayout currentLayoutrequired property Item addWidgetsButtonrequired property Item lastSpacerrequired property DragDrop.DropArea dropArearequired property Component appletContainerComponentrequired property bool isHorizontalrequired property bool reverserequired property int rootLengthproperty AppletContainer currentAppletproperty int draggedItemIndexproperty real startDragOffset: 0.0property bool dragAndDropping: falseproperty point __mouseDownPosproperty AppletContainer __previousAppletDrag.dragType: Drag.AutomaticDrag.active: falseDrag.supportedActions: Qt.MoveActionDrag.mimeData: {"text/x-plasmoidinstanceid": Plasmoid.containment.id+':'+configurationArea.currentApplet?.applet.Plasmoid.id}Drag.onDragFinished: dropEvent => {if (dropEvent != Qt.MoveAction) {//currentApplet.applet.plasmoid.internalAction("remove").trigger()appletsModel.insert(configurationArea.draggedItemIndex - 1, {applet: currentApplet.applet});}currentApplet.destroy()configurationArea.dragAndDropping = falseconfigurationArea.layoutManager.save()}onPositionChanged: mouse => {if (pressed) {// We start a drag when the Manhattan length of the mouse travel is more than startDragDistanceif (!configurationArea.dragAndDropping &&configurationArea.currentApplet &&Math.abs(mouse.x - __mouseDownPos.x) + Math.abs(mouse.y - __mouseDownPos.y) > Qt.styleHints.startDragDistance) {// We set the current applet being dragged as a property of placeHolder// to be able to read its properties from the LayoutManagerconst oldCurrentApplet = currentApplet;appletsModel.insert(configurationArea.currentApplet.index, {applet: placeHolder});placeHolder.parent.inThickArea = configurationArea.currentApplet.inThickAreacurrentApplet = appletContainerComponent.createObject(dropArea, {applet: configurationArea.currentApplet.applet, x: configurationArea.currentApplet.x,y: configurationArea.currentApplet.y, z: 900,width: configurationArea.currentApplet.width, height: configurationArea.currentApplet.height, index: -1})placeHolder.parent.dragging = currentAppletconfigurationArea.draggedItemIndex = oldCurrentApplet.indexappletsModel.remove(oldCurrentApplet.index)configurationArea.dragAndDropping = trueif (Plasmoid.formFactor === PlasmaCore.Types.Vertical) {startDragOffset = __mouseDownPos.y - currentApplet.y;} else {startDragOffset = __mouseDownPos.x - currentApplet.x;}} else if (configurationArea.dragAndDropping && configurationArea.currentApplet) {// If the object has been dragged outside of the panel and there's// a different containment there, we remove it from the panel// containment and add it to the new one.var padding = Kirigami.Units.gridUnit * 5;if (currentApplet && (mouse.x < -padding || mouse.y < -padding ||mouse.x > width + padding || mouse.y > height + padding)) {configurationArea.currentApplet.grabToImage(result => {configurationArea.Drag.imageSource = result.urlappletsModel.remove((placeHolder.parent as AppletContainer).index)currentApplet.visible = falseconfigurationArea.Drag.active = true})}if (Plasmoid.formFactor === PlasmaCore.Types.Vertical && currentApplet) {currentApplet.y = mouse.y - startDragOffset;} else {currentApplet.x = mouse.x - startDragOffset;}const item = configurationArea.layoutManager.childAtCoordinates(mouse.x, mouse.y);if (item && item.applet !== placeHolder) {var posInItem = mapToItem(item, mouse.x, mouse.y)var pos = configurationArea.isHorizontal ? posInItem.x : posInItem.yvar size = configurationArea.isHorizontal ? item.width : item.heightif (configurationArea.reverse) {pos = size - pos}if (pos < size / 3) {configurationArea.layoutManager.move(placeHolder.parent, item.index)} else if (pos > size / 3 * 2) {configurationArea.layoutManager.move(placeHolder.parent, item.index+1)}}}} else {const item = currentLayout.childAt(mouse.x, mouse.y);if (item && item !== lastSpacer) {currentApplet = item;}}if (currentApplet) {hideTimer.stop();tooltip.raise();}}onEntered: hideTimer.stop();onExited: {if (!tooltip.openedByTap) {hideTimer.restart();}}onCurrentAppletChanged: {if (currentApplet && __previousApplet) {showTimer.stop();hideTimer.stop();} else if (currentApplet) {showTimer.restart();} else {hideTimer.restart();dragAndDropping = false;}__previousApplet = currentApplet;}onPressed: mouse => {configurationArea.currentApplet = currentLayout.childAt(mouse.x, mouse.y) as AppletContainer;// BUG 454095: Don't allow dragging lastSpacer as it's not a real appletif (!configurationArea.currentApplet || configurationArea.currentApplet == lastSpacer || configurationArea.currentApplet == addWidgetsButton) {configurationArea.currentApplet = null;return;}__mouseDownPos.x = mouse.x;__mouseDownPos.y = mouse.y;}onReleased: mouse => {if (configurationArea.dragAndDropping) {finishDragOperation();} else {tooltip.openedByTap = true;tooltip.raise();hideTimer.stop();showTimer.stop();}}onCanceled: finishDragOperation()function finishDragOperation() {if (!configurationArea.dragAndDropping) {return;}configurationArea.dragAndDropping = falseif (!currentApplet) {return;}appletsModel.set((placeHolder.parent as AppletContainer).index, {applet: currentApplet.applet})let newCurrentApplet = currentApplet.applet.parentnewCurrentApplet.animateFrom(currentApplet.x, currentApplet.y)newCurrentApplet.dragging = nullplaceHolder.parent = thiscurrentApplet.destroy()configurationArea.layoutManager.save()}Item {id: placeHolderproperty Item draggingproperty bool busy: falseproperty bool isPlaceholder: truevisible: configurationArea.containsMouseLayout.preferredWidth: configurationArea.currentApplet?.Layout.preferredWidth ?? 0Layout.preferredHeight: configurationArea.currentApplet?.Layout.preferredHeight ?? 0Layout.maximumWidth: configurationArea.currentApplet?.Layout.maximumWidth ?? 0Layout.maximumHeight: configurationArea.currentApplet?.Layout.maximumHeight ?? 0Layout.minimumWidth: configurationArea.currentApplet?.Layout.minimumWidth ?? 0Layout.minimumHeight: configurationArea.currentApplet?.Layout.minimumHeight ?? 0Layout.fillWidth: configurationArea.currentApplet?.Layout.fillWidth ?? falseLayout.fillHeight: configurationArea.currentApplet?.Layout.fillHeight ?? false}Timer {id: hideTimerinterval: Kirigami.Units.humanMomentonTriggered: configurationArea.currentApplet = null}Timer {id: showTimerinterval: Kirigami.Units.toolTipDelayonTriggered: {if (!configurationArea.containsMouse) {configurationArea.currentApplet = null}}}Rectangle {id: handlex: configurationArea.currentApplet?.x ?? 0y: configurationArea.currentApplet?.y ?? 0width: configurationArea.currentApplet?.width ?? 0height: configurationArea.currentApplet?.height ?? 0color: Kirigami.Theme.backgroundColorradius: Kirigami.Units.cornerRadiusopacity: configurationArea.currentApplet && configurationArea.containsMouse ? 0.5 : 0Kirigami.Icon {visible: !configurationArea.dragAndDroppingsource: "transform-move"width: Math.min(parent.width, parent.height)height: widthanchors.centerIn: parent}Behavior on x {enabled: !configurationArea.pressedNumberAnimation {duration: Kirigami.Units.longDurationeasing.type: Easing.InOutQuad}}Behavior on y {enabled: !configurationArea.pressedNumberAnimation {duration: Kirigami.Units.longDurationeasing.type: Easing.InOutQuad}}Behavior on width {enabled: !configurationArea.pressedNumberAnimation {duration: Kirigami.Units.longDurationeasing.type: Easing.InOutQuad}}Behavior on height {enabled: !configurationArea.pressedNumberAnimation {duration: Kirigami.Units.longDurationeasing.type: Easing.InOutQuad}}Behavior on opacity {NumberAnimation {duration: Kirigami.Units.longDurationeasing.type: Easing.InOutQuad}}}PlasmaCore.PopupPlasmaWindow {id: tooltipproperty bool openedByTapvisible: configurationArea.currentApplet && !configurationArea.dragAndDropping && !showTimer.runningvisualParent: configurationArea.currentApplet// Try to dodge the ruler, as we can't cover it since it's a layershell surfacemargin: configurationArea.Window.window?.lengthMode === 2 ? Kirigami.Units.gridUnit * 2 : 0width: mainItem.implicitWidth + leftPadding + rightPaddingheight: mainItem.implicitHeight + topPadding + bottomPaddingonVisibleChanged: {if (visible) {requestActivate();} else {openedByTap = false;}}onActiveChanged: {if (!active && !configurationArea.dragAndDropping) {configurationArea.currentApplet = null;}}popupDirection: switch (Plasmoid.location) {case PlasmaCore.Types.TopEdge:return Qt.BottomEdgecase PlasmaCore.Types.LeftEdge:return Qt.RightEdgecase PlasmaCore.Types.RightEdge:return Qt.LeftEdgedefault:return Qt.TopEdge}onVisualParentChanged: {if (visualParent) {tooltip.requestActivate()const thisPlasmoid = configurationArea.currentApplet.applet.Plasmoid;thisPlasmoid.contextualActionsAboutToShow();alternativesButton.visible = thisPlasmoid.internalAction("alternatives")?.enabled ?? false;configureButton.visible = thisPlasmoid.internalAction("configure")?.enabled ?? false;label.text = thisPlasmoid.title;}}mainItem: MouseArea {enabled: tooltip.visibleimplicitWidth: handleButtons.widthimplicitHeight: handleButtons.heighthoverEnabled: trueonEntered: hideTimer.stop();onExited: hideTimer.restart();Keys.onEscapePressed: {tooltip.close()}ColumnLayout {id: handleButtonsspacing: Kirigami.Units.smallSpacingPlasmaExtras.PlasmoidHeading {leftPadding: Kirigami.Units.smallSpacing * 2rightPadding: Kirigami.Units.smallSpacing * 2Layout.maximumWidth: Kirigami.Units.gridUnit * 15contentItem: Kirigami.Heading {id: labellevel: 3horizontalAlignment: Text.AlignHCentertextFormat: Text.PlainTextelide: Text.ElideRightwrapMode: Text.WrapmaximumLineCount: 3}}PlasmaComponents3.ToolButton {id: removeButtonLayout.fillWidth: true// we want destructive actions to be far from the initial// cursor position, so show this on the top unless it's on// a top panelvisible: configurationArea.currentApplet?.applet.Plasmoid.internalAction("remove")?.enabled ?? falseicon.name: "edit-delete-remove-symbolic"text: i18nc("@action:inmenu removes widget", "Remove")onClicked: {configurationArea.currentApplet.applet.Plasmoid.internalAction("remove").trigger();configurationArea.currentApplet = null;}}PlasmaComponents3.ToolButton {id: configureButtonLayout.fillWidth: trueicon.name: "configure"text: i18nc("@action:inmenu opens widget config dialog", "Configureβ¦")visible: configurationArea.currentApplet?.applet.plasmoid.hasConfigurationInterface ?? falseonClicked: {configurationArea.currentApplet.applet.Plasmoid.internalAction("configure").trigger();configurationArea.currentApplet = null;}}PlasmaComponents3.ToolButton {id: alternativesButtonLayout.fillWidth: trueicon.name: "widget-alternatives"text: i18nc("@action:inmenu opens widget alternatives explorer", "Show Alternativesβ¦")onClicked: {configurationArea.currentApplet.applet.Plasmoid.internalAction("alternatives").trigger();// We keep this popup open for a brief interval of time instead// of re-setting the currentApplet now so that the alternatives// popup has time to gain focus. The hideTimer will close this// dialog.}}// we want destructive actions to be far from the initial// cursor position, so show this on the bottom for top panelsLayoutItemProxy {target: tooltip.popupDirection === Qt.BottomEdge ? removeButton : null}Kirigami.Heading {Layout.fillWidth: truevisible: panelSpacerWidth.visibletext: configurationArea.isHorizontal? i18nc("@label:spinbox", "Spacer width"): i18nc("@label:spinbox", "Spacer height")textFormat: Text.PlainTextlevel: 3horizontalAlignment: Text.AlignHCenter}PlasmaComponents3.SpinBox {id: panelSpacerWidtheditable: trueLayout.fillWidth: truefocus: !Kirigami.InputMethod.willShowOnActive && visiblevisible: configurationArea.currentApplet?.applet.Plasmoid.pluginName === "org.kde.plasma.panelspacer"&& !configurationArea.currentApplet.applet.Plasmoid.configuration.expandingfrom: 0stepSize: 10to: configurationArea.rootLengthvalue: configurationArea.currentApplet?.applet.Plasmoid.configuration.length ?? 0onValueModified: {configurationArea.currentApplet.applet.Plasmoid.configuration.length = value}}PlasmaComponents3.Switch {text: i18nc("@option:check Whether to allow a spacer widget to fill available space", "Flexible size")visible: configurationArea.currentApplet?.applet.Plasmoid?.pluginName === "org.kde.plasma.panelspacer"checked: configurationArea.currentApplet?.applet.Plasmoid.configuration.expanding ?? falseonToggled: configurationArea.currentApplet.applet.Plasmoid.configuration.expanding = checkedLayout.fillWidth: trueLayout.bottomMargin: Kirigami.Units.smallSpacing}}}}}