diff --git a/components/mobileshell/qml/components/ExtendedAbstractButton.qml b/components/mobileshell/qml/components/ExtendedAbstractButton.qml index 78fdb0eb..6cc31757 100644 --- a/components/mobileshell/qml/components/ExtendedAbstractButton.qml +++ b/components/mobileshell/qml/components/ExtendedAbstractButton.qml @@ -19,6 +19,11 @@ QQC2.AbstractButton { */ property alias cursorShape: hoverHandler.cursorShape + /** + * Alias to MouseArea used in the button. + */ + property alias mouseArea: mouseArea + /** * Whether a mouse is hovering over the button (not touch). */ diff --git a/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml b/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml index c99a310a..ce6d41e0 100644 --- a/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml +++ b/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml @@ -17,11 +17,16 @@ import org.kde.phone.homescreen.halcyon 1.0 as Halcyon import org.kde.kirigami 2.19 as Kirigami -MobileShell.ExtendedAbstractButton { +MouseArea { id: delegate + + property int visualIndex: 0 + + property real leftPadding + property real rightPadding property alias iconItem: icon - property Halcyon.Application application: model.application + property var application readonly property string applicationName: application ? application.name : "" readonly property string applicationStorageId: application ? application.storageId : "" @@ -30,6 +35,11 @@ MobileShell.ExtendedAbstractButton { signal launch(int x, int y, var source, string title, string storageId) signal dragStarted(string imageSource, int x, int y, string mimeData) + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + onLaunch: { if (icon !== "") { MobileShell.HomeScreenControls.openAppLaunchAnimation( @@ -57,9 +67,27 @@ MobileShell.ExtendedAbstractButton { } } - onRightClickPressed: openContextMenu() - onClicked: launchApp(); - onPressAndHold: openContextMenu() + property bool inDrag: false + + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: (mouse.button === Qt.RightButton) ? openContextMenu() : launchApp(); + onReleased: { + parent.Drag.drop(); + inDrag = false; + } + onPressAndHold: { inDrag = true; openContextMenu() } + + drag.target: inDrag ? delegate : undefined + Drag.active: delegate.drag.active + Drag.source: delegate + Drag.hotSpot.x: delegate.width / 2 + Drag.hotSpot.y: delegate.height / 2 + + HoverHandler { + id: hoverHandler + acceptedDevices: PointerDevice.Mouse + acceptedPointerTypes: PointerDevice.GenericPointer + } Loader { id: dialogLoader @@ -80,79 +108,86 @@ MobileShell.ExtendedAbstractButton { } } - Rectangle { + Item { + id: baseItem anchors.fill: parent - radius: height / 2 - color: delegate.pressed ? Qt.rgba(255, 255, 255, 0.2) : (delegate.mouseHovered ? Qt.rgba(255, 255, 255, 0.1) : "transparent") - } - - RowLayout { - id: rowLayout - anchors { - fill: parent - leftMargin: PlasmaCore.Units.smallSpacing * 2 - topMargin: PlasmaCore.Units.smallSpacing - rightMargin: PlasmaCore.Units.smallSpacing * 2 - bottomMargin: PlasmaCore.Units.smallSpacing + + Rectangle { + anchors.fill: parent + anchors.leftMargin: delegate.leftPadding + anchors.rightMargin: delegate.rightPadding + radius: height / 2 + color: delegate.pressed ? Qt.rgba(255, 255, 255, 0.2) : (hoverHandler.hovered ? Qt.rgba(255, 255, 255, 0.1) : "transparent") } - spacing: 0 + + RowLayout { + id: rowLayout + anchors { + fill: parent + leftMargin: PlasmaCore.Units.smallSpacing * 2 + delegate.leftPadding + topMargin: PlasmaCore.Units.smallSpacing + rightMargin: PlasmaCore.Units.smallSpacing * 2 + delegate.rightPadding + bottomMargin: PlasmaCore.Units.smallSpacing + } + spacing: 0 - PlasmaCore.IconItem { - id: icon + PlasmaCore.IconItem { + id: icon - Layout.alignment: Qt.AlignLeft - Layout.minimumWidth: Layout.minimumHeight - Layout.preferredWidth: Layout.minimumHeight - Layout.minimumHeight: parent.height - Layout.preferredHeight: Layout.minimumHeight + Layout.alignment: Qt.AlignLeft + Layout.minimumWidth: Layout.minimumHeight + Layout.preferredWidth: Layout.minimumHeight + Layout.minimumHeight: parent.height + Layout.preferredHeight: Layout.minimumHeight - usesPlasmaTheme: false - source: applicationIcon + usesPlasmaTheme: false + source: delegate.applicationIcon - Rectangle { - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom + Rectangle { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + } + visible: application ? application.running : false + radius: width + width: PlasmaCore.Units.smallSpacing + height: width + color: PlasmaCore.Theme.highlightColor + } + + layer.enabled: true + layer.effect: DropShadow { + verticalOffset: 1 + radius: 4 + samples: 6 + color: Qt.rgba(0, 0, 0, 0.5) } - visible: application ? application.running : false - radius: width - width: PlasmaCore.Units.smallSpacing - height: width - color: PlasmaCore.Theme.highlightColor } - - layer.enabled: true - layer.effect: DropShadow { - verticalOffset: 1 - radius: 4 - samples: 6 - color: Qt.rgba(0, 0, 0, 0.5) - } - } - PlasmaComponents.Label { - id: label - visible: text.length > 0 + PlasmaComponents.Label { + id: label + visible: text.length > 0 - Layout.fillWidth: true - Layout.leftMargin: PlasmaCore.Units.smallSpacing * 2 - Layout.rightMargin: PlasmaCore.Units.largeSpacing - wrapMode: Text.WordWrap - maximumLineCount: 1 - elide: Text.ElideRight + Layout.fillWidth: true + Layout.leftMargin: PlasmaCore.Units.smallSpacing * 2 + Layout.rightMargin: PlasmaCore.Units.largeSpacing + wrapMode: Text.WordWrap + maximumLineCount: 1 + elide: Text.ElideRight - text: applicationName + text: delegate.applicationName - font.pointSize: PlasmaCore.Theme.defaultFont.pointSize - font.weight: Font.Bold - color: "white" - - layer.enabled: true - layer.effect: DropShadow { - verticalOffset: 1 - radius: 4 - samples: 6 - color: Qt.rgba(0, 0, 0, 0.5) + font.pointSize: PlasmaCore.Theme.defaultFont.pointSize + font.weight: Font.Bold + color: "white" + + layer.enabled: true + layer.effect: DropShadow { + verticalOffset: 1 + radius: 4 + samples: 6 + color: Qt.rgba(0, 0, 0, 0.5) + } } } } diff --git a/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml b/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml index 92f6b3cc..af86811a 100644 --- a/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml +++ b/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml @@ -4,6 +4,7 @@ import QtQuick 2.12 import QtQuick.Controls 2.15 as QQC2 import QtQuick.Layouts 1.1 +import QtQml.Models 2.15 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore @@ -56,7 +57,11 @@ GridView { } } - model: Halcyon.PinnedModel + // open wallpaper menu when held on click + TapHandler { + onLongPressed: root.openConfigureRequested() + } + header: MobileShell.BaseItem { topPadding: Math.round(swipeView.height * 0.2) bottomPadding: PlasmaCore.Units.largeSpacing @@ -71,22 +76,76 @@ GridView { contentItem: Clock {} } - delegate: MobileShell.BaseItem { - id: baseItem - readonly property bool isLeftColumn: !root.twoColumn || ((model.index % 2) === 0) - readonly property bool isRightColumn: !root.twoColumn || ((model.index % 2) !== 0) - leftPadding: isLeftColumn ? root.leftMargin : 0 - rightPadding: isRightColumn ? root.rightMargin : 0 + model: DelegateModel { + id: visualModel + model: Halcyon.PinnedModel - contentItem: FavoritesAppDelegate { - implicitWidth: root.cellWidth - (baseItem.isLeftColumn ? root.leftMargin : 0) - (baseItem.isRightColumn ? root.rightMargin : 0) - implicitHeight: visible ? root.cellHeight : 0 + delegate: DropArea { + id: delegateRoot + property var application: model.application + + property int modelIndex + property int visualIndex: DelegateModel.itemsIndex + + width: root.cellWidth + height: root.cellHeight + + onEntered: (drag) => { + let from = (drag.source as MobileShell.BaseItem).visualIndex; + let to = appDelegate.visualIndex; + visualModel.items.move(from, to); + Halcyon.PinnedModel.moveEntry(from, to); + } + + //onDropped: (drag) => { + //let from = modelIndex; + //let to = (drag.source as MobileShell.BaseItem).visualIndex + //Halcyon.PinnedModel.moveEntry(from, to); + //} + + FavoritesAppDelegate { + id: appDelegate + visualIndex: delegateRoot.visualIndex + application: delegateRoot.application + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + + readonly property bool isLeftColumn: !root.twoColumn || ((visualIndex % 2) === 0) + readonly property bool isRightColumn: !root.twoColumn || ((visualIndex % 2) !== 0) + leftPadding: isLeftColumn ? root.leftMargin : 0 + rightPadding: isRightColumn ? root.rightMargin : 0 + + implicitWidth: root.cellWidth + implicitHeight: visible ? root.cellHeight : 0 + + states: [ + State { + when: appDelegate.drag.active + ParentChange { + target: appDelegate + parent: root + } + + AnchorChanges { + target: appDelegate + anchors.horizontalCenter: undefined + anchors.verticalCenter: undefined + } + } + ] + } } } - // open wallpaper menu when held on click - TapHandler { - onLongPressed: root.openConfigureRequested() + // animations + displaced: Transition { + NumberAnimation { + properties: "x,y" + easing.type: Easing.OutQuad + } } ColumnLayout { diff --git a/containments/homescreens/halcyon/pinnedmodel.cpp b/containments/homescreens/halcyon/pinnedmodel.cpp index 046bc1cd..b799b2cb 100644 --- a/containments/homescreens/halcyon/pinnedmodel.cpp +++ b/containments/homescreens/halcyon/pinnedmodel.cpp @@ -106,6 +106,40 @@ void PinnedModel::removeFolder(int row) save(); } +void PinnedModel::moveEntry(int fromRow, int toRow) +{ + if (fromRow < 0 || toRow < 0 || fromRow >= m_applications.length() || toRow >= m_applications.length() || fromRow == toRow) { + return; + } + if (toRow > fromRow) { + ++toRow; + } + + beginMoveRows(QModelIndex(), fromRow, fromRow, QModelIndex(), toRow); + if (toRow > fromRow) { + Application *app = m_applications.at(fromRow); + m_applications.insert(toRow, app); + m_applications.takeAt(fromRow); + + ApplicationFolder *folder = m_folders.at(fromRow); + m_folders.insert(toRow, folder); + m_folders.takeAt(fromRow); + + } else { + Application *app = m_applications.takeAt(fromRow); + m_applications.insert(toRow, app); + + ApplicationFolder *folder = m_folders.takeAt(fromRow); + m_folders.insert(toRow, folder); + } + endMoveRows(); + + save(); + + // HACK: didn't seem to persist + m_applet->config().sync(); +} + void PinnedModel::load() { if (!m_applet) { diff --git a/containments/homescreens/halcyon/pinnedmodel.h b/containments/homescreens/halcyon/pinnedmodel.h index 0ccf9216..e415f7bc 100644 --- a/containments/homescreens/halcyon/pinnedmodel.h +++ b/containments/homescreens/halcyon/pinnedmodel.h @@ -41,6 +41,8 @@ public: Q_INVOKABLE void addFolder(QString name, int row); Q_INVOKABLE void removeFolder(int row); + Q_INVOKABLE void moveEntry(int fromRow, int toRow); + Q_INVOKABLE void load(); void save();