/* SPDX-FileCopyrightText: 2013 Marco Martin SPDX-FileCopyrightText: 2022 Niccolò Venerandi SPDX-FileCopyrightText: 2023 ivan tkachenko SPDX-License-Identifier: GPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Layouts 1.0 import QtQuick.Window import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core as PlasmaCore import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 import org.kde.kirigami as Kirigami MouseArea { id: configurationArea z: 1000 hoverEnabled: true property Item currentApplet property real startDragOffset: 0.0 onPositionChanged: mouse => { if (pressed) { // 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)) { var newCont = root.containmentItemAt(mouse.x, mouse.y); if (newCont && newCont !== plasmoid) { var newPos = newCont.mapFromApplet(currentApplet.applet.plasmoid, mouse.x, mouse.y); var applet = currentApplet.applet; appletsModel.remove(placeHolder.parent.index); currentApplet.destroy(); applet.anchors.fill = undefined newCont.plasmoid.addApplet(applet.plasmoid, Qt.rect(newPos.x, newPos.y, applet.width, applet.height)); return; } } if (Plasmoid.formFactor === PlasmaCore.Types.Vertical && currentApplet) { currentApplet.y = mouse.y - startDragOffset; } else { currentApplet.x = mouse.x - startDragOffset; } const item = root.layoutManager.childAtCoordinates(mouse.x, mouse.y); if (item && item.applet !== placeHolder) { var posInItem = mapToItem(item, mouse.x, mouse.y) var pos = root.isHorizontal ? posInItem.x : posInItem.y var size = root.isHorizontal ? item.width : item.height if (pos < size / 3) { root.layoutManager.move(placeHolder.parent, item.index) } else if (pos > size / 3 * 2) { root.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: hideTimer.restart() onCurrentAppletChanged: { if (!currentApplet) { hideTimer.start(); return; } } onPressed: mouse => { // Need to set currentApplet here too, to make touch selection + drag // with with a touchscreen, because there are no entered events in that // case let item = currentLayout.childAt(mouse.x, mouse.y); // BUG 454095: Don't allow dragging lastSpacer as it's not a real applet if (!item || item == lastSpacer || item == addWidgetsButton) { configurationArea.currentApplet = null return; } tooltip.raise(); hideTimer.stop(); // We set the current applet being dragged as a property of placeHolder // to be able to read its properties from the LayoutManager appletsModel.insert(item.index, {applet: placeHolder}); placeHolder.parent.inThickArea = item.inThickArea currentApplet = appletContainerComponent.createObject(dropArea, {applet: item.applet, x: item.x, y: item.y, z: 900, width: item.width, height: item.height, index: -1}) placeHolder.parent.dragging = currentApplet appletsModel.remove(item.index) root.dragAndDropping = true if (Plasmoid.formFactor === PlasmaCore.Types.Vertical) { startDragOffset = mouse.y - currentApplet.y; } else { startDragOffset = mouse.x - currentApplet.x; } } onReleased: mouse => finishDragOperation() onCanceled: finishDragOperation() function finishDragOperation() { root.dragAndDropping = false if (!currentApplet) { return; } appletsModel.set(placeHolder.parent.index, {applet: currentApplet.applet}) let newCurrentApplet = currentApplet.applet.parent newCurrentApplet.animateFrom(currentApplet.x, currentApplet.y) newCurrentApplet.dragging = null placeHolder.parent = this currentApplet.destroy() root.layoutManager.save() } Item { id: placeHolder property Item dragging property bool busy: false visible: configurationArea.containsMouse Layout.preferredWidth: configurationArea.currentApplet?.Layout.preferredWidth ?? 0 Layout.preferredHeight: configurationArea.currentApplet?.Layout.preferredHeight ?? 0 Layout.maximumWidth: configurationArea.currentApplet?.Layout.maximumWidth ?? 0 Layout.maximumHeight: configurationArea.currentApplet?.Layout.maximumHeight ?? 0 Layout.minimumWidth: configurationArea.currentApplet?.Layout.minimumWidth ?? 0 Layout.minimumHeight: configurationArea.currentApplet?.Layout.minimumHeight ?? 0 Layout.fillWidth: configurationArea.currentApplet?.Layout.fillWidth ?? false Layout.fillHeight: configurationArea.currentApplet?.Layout.fillHeight ?? false } Timer { id: hideTimer interval: Kirigami.Units.longDuration * 5 onTriggered: configurationArea.currentApplet = null } Rectangle { id: handle x: configurationArea.currentApplet?.x ?? 0 y: configurationArea.currentApplet?.y ?? 0 width: configurationArea.currentApplet?.width ?? 0 height: configurationArea.currentApplet?.height ?? 0 color: Kirigami.Theme.backgroundColor radius: Kirigami.Units.cornerRadius opacity: configurationArea.currentApplet && configurationArea.containsMouse ? 0.5 : 0 Kirigami.Icon { visible: !root.dragAndDropping source: "transform-move" width: Math.min(parent.width, parent.height) height: width anchors.centerIn: parent } } PlasmaCore.PopupPlasmaWindow { id: tooltip visible: configurationArea.currentApplet && !root.dragAndDropping visualParent: configurationArea.currentApplet // Try to dodge the ruler, as we can't cover it since it's a layershell surface margin: configurationArea.Window.window?.lengthMode === 2 ? Kirigami.Units.gridUnit * 2 : Kirigami.Units.largeSpacing width: mainItem.implicitWidth + leftPadding + rightPadding height: mainItem.implicitHeight + topPadding + bottomPadding backgroundHints: "SolidBackground" popupDirection: switch (Plasmoid.location) { case PlasmaCore.Types.TopEdge: return Qt.BottomEdge case PlasmaCore.Types.LeftEdge: return Qt.RightEdge case PlasmaCore.Types.RightEdge: return Qt.LeftEdge default: 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.visible implicitWidth: handleButtons.width implicitHeight: handleButtons.height hoverEnabled: true onEntered: hideTimer.stop(); onExited: hideTimer.restart(); Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.inherit: false ColumnLayout { id: handleButtons spacing: Kirigami.Units.smallSpacing PlasmaExtras.PlasmoidHeading { topPadding: Kirigami.Units.smallSpacing * 2 leftPadding: Kirigami.Units.smallSpacing * 4 rightPadding: Kirigami.Units.smallSpacing * 4 Layout.leftMargin: Kirigami.Units.largeSpacing * 2 Layout.rightMargin: Kirigami.Units.largeSpacing * 2 contentItem: Kirigami.Heading { id: label Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.inherit: false level: 4 horizontalAlignment: Text.AlignHCenter textFormat: Text.PlainText } } PlasmaComponents3.ToolButton { Layout.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 panel visible: tooltip.location !== PlasmaCore.Types.TopEdge && (configurationArea.currentApplet?.applet.plasmoid.internalAction("remove")?.enabled ?? false) icon.name: "edit-clear" text: i18n("Remove") onClicked: { configurationArea.currentApplet.applet.plasmoid.internalAction("remove").trigger(); configurationArea.currentApplet = null; } } PlasmaComponents3.ToolButton { id: configureButton Layout.fillWidth: true icon.name: "configure" text: i18n("Configure…") visible: configurationArea.currentApplet?.applet.plasmoid.hasConfigurationInterface ?? false onClicked: { configurationArea.currentApplet.applet.plasmoid.internalAction("configure").trigger(); configurationArea.currentApplet = null; } } PlasmaComponents3.ToolButton { id: alternativesButton Layout.fillWidth: true icon.name: "preferences-desktop-plasma" text: i18n("Show Alternatives…") onClicked: { configurationArea.currentApplet.applet.plasmoid.internalAction("alternatives").trigger(); configurationArea.currentApplet = null; } } PlasmaComponents3.ToolButton { Layout.fillWidth: true // we want destructive actions to be far from the initial // cursor position, so show this on the bottom for top panels visible: tooltip.location === PlasmaCore.Types.TopEdge && (configurationArea.currentApplet?.applet.plasmoid.internalAction("remove")?.enabled ?? false) icon.name: "edit-clear" text: i18n("Remove") onClicked: { configurationArea.currentApplet.applet.plasmoid.internalAction("remove").trigger(); configurationArea.currentApplet = null; } } Kirigami.Heading { Layout.fillWidth: true visible: panelSpacerWidth.visible text: i18n("Spacer width") textFormat: Text.PlainText level: 3 horizontalAlignment: Text.AlignHCenter } PlasmaComponents3.SpinBox { id: panelSpacerWidth editable: true Layout.fillWidth: true focus: !Kirigami.InputMethod.willShowOnActive visible: configurationArea.currentApplet?.applet.plasmoid.pluginName === "org.kde.plasma.panelspacer" && !configurationArea.currentApplet.applet.plasmoid.configuration.expanding from: 0 stepSize: 10 to: root.width value: configurationArea.currentApplet?.applet.plasmoid.configuration.length ?? 0 onValueModified: { configurationArea.currentApplet.applet.plasmoid.configuration.length = value } } } } } }