homescreens/halcyon: Add drag and drop reordering of favourites

This commit is contained in:
Devin Lin 2022-06-29 00:22:42 -04:00
parent 6a0fcc83fd
commit b7f1a5b379
5 changed files with 214 additions and 79 deletions

View file

@ -19,6 +19,11 @@ QQC2.AbstractButton {
*/ */
property alias cursorShape: hoverHandler.cursorShape 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). * Whether a mouse is hovering over the button (not touch).
*/ */

View file

@ -17,11 +17,16 @@ import org.kde.phone.homescreen.halcyon 1.0 as Halcyon
import org.kde.kirigami 2.19 as Kirigami import org.kde.kirigami 2.19 as Kirigami
MobileShell.ExtendedAbstractButton { MouseArea {
id: delegate id: delegate
property int visualIndex: 0
property real leftPadding
property real rightPadding
property alias iconItem: icon property alias iconItem: icon
property Halcyon.Application application: model.application property var application
readonly property string applicationName: application ? application.name : "" readonly property string applicationName: application ? application.name : ""
readonly property string applicationStorageId: application ? application.storageId : "" 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 launch(int x, int y, var source, string title, string storageId)
signal dragStarted(string imageSource, int x, int y, string mimeData) signal dragStarted(string imageSource, int x, int y, string mimeData)
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
onLaunch: { onLaunch: {
if (icon !== "") { if (icon !== "") {
MobileShell.HomeScreenControls.openAppLaunchAnimation( MobileShell.HomeScreenControls.openAppLaunchAnimation(
@ -57,9 +67,27 @@ MobileShell.ExtendedAbstractButton {
} }
} }
onRightClickPressed: openContextMenu() property bool inDrag: false
onClicked: launchApp();
onPressAndHold: openContextMenu() 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 { Loader {
id: dialogLoader id: dialogLoader
@ -80,79 +108,86 @@ MobileShell.ExtendedAbstractButton {
} }
} }
Rectangle { Item {
id: baseItem
anchors.fill: parent 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") Rectangle {
} anchors.fill: parent
anchors.leftMargin: delegate.leftPadding
RowLayout { anchors.rightMargin: delegate.rightPadding
id: rowLayout radius: height / 2
anchors { color: delegate.pressed ? Qt.rgba(255, 255, 255, 0.2) : (hoverHandler.hovered ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
fill: parent
leftMargin: PlasmaCore.Units.smallSpacing * 2
topMargin: PlasmaCore.Units.smallSpacing
rightMargin: PlasmaCore.Units.smallSpacing * 2
bottomMargin: PlasmaCore.Units.smallSpacing
} }
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 { PlasmaCore.IconItem {
id: icon id: icon
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
Layout.minimumWidth: Layout.minimumHeight Layout.minimumWidth: Layout.minimumHeight
Layout.preferredWidth: Layout.minimumHeight Layout.preferredWidth: Layout.minimumHeight
Layout.minimumHeight: parent.height Layout.minimumHeight: parent.height
Layout.preferredHeight: Layout.minimumHeight Layout.preferredHeight: Layout.minimumHeight
usesPlasmaTheme: false usesPlasmaTheme: false
source: applicationIcon source: delegate.applicationIcon
Rectangle { Rectangle {
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
bottom: parent.bottom 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 { PlasmaComponents.Label {
id: label id: label
visible: text.length > 0 visible: text.length > 0
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: PlasmaCore.Units.smallSpacing * 2 Layout.leftMargin: PlasmaCore.Units.smallSpacing * 2
Layout.rightMargin: PlasmaCore.Units.largeSpacing Layout.rightMargin: PlasmaCore.Units.largeSpacing
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
maximumLineCount: 1 maximumLineCount: 1
elide: Text.ElideRight elide: Text.ElideRight
text: applicationName text: delegate.applicationName
font.pointSize: PlasmaCore.Theme.defaultFont.pointSize font.pointSize: PlasmaCore.Theme.defaultFont.pointSize
font.weight: Font.Bold font.weight: Font.Bold
color: "white" color: "white"
layer.enabled: true layer.enabled: true
layer.effect: DropShadow { layer.effect: DropShadow {
verticalOffset: 1 verticalOffset: 1
radius: 4 radius: 4
samples: 6 samples: 6
color: Qt.rgba(0, 0, 0, 0.5) color: Qt.rgba(0, 0, 0, 0.5)
}
} }
} }
} }

View file

@ -4,6 +4,7 @@
import QtQuick 2.12 import QtQuick 2.12
import QtQuick.Controls 2.15 as QQC2 import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQml.Models 2.15
import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore 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 { header: MobileShell.BaseItem {
topPadding: Math.round(swipeView.height * 0.2) topPadding: Math.round(swipeView.height * 0.2)
bottomPadding: PlasmaCore.Units.largeSpacing bottomPadding: PlasmaCore.Units.largeSpacing
@ -71,22 +76,76 @@ GridView {
contentItem: Clock {} contentItem: Clock {}
} }
delegate: MobileShell.BaseItem { model: DelegateModel {
id: baseItem id: visualModel
readonly property bool isLeftColumn: !root.twoColumn || ((model.index % 2) === 0) model: Halcyon.PinnedModel
readonly property bool isRightColumn: !root.twoColumn || ((model.index % 2) !== 0)
leftPadding: isLeftColumn ? root.leftMargin : 0
rightPadding: isRightColumn ? root.rightMargin : 0
contentItem: FavoritesAppDelegate { delegate: DropArea {
implicitWidth: root.cellWidth - (baseItem.isLeftColumn ? root.leftMargin : 0) - (baseItem.isRightColumn ? root.rightMargin : 0) id: delegateRoot
implicitHeight: visible ? root.cellHeight : 0 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 // animations
TapHandler { displaced: Transition {
onLongPressed: root.openConfigureRequested() NumberAnimation {
properties: "x,y"
easing.type: Easing.OutQuad
}
} }
ColumnLayout { ColumnLayout {

View file

@ -106,6 +106,40 @@ void PinnedModel::removeFolder(int row)
save(); 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() void PinnedModel::load()
{ {
if (!m_applet) { if (!m_applet) {

View file

@ -41,6 +41,8 @@ public:
Q_INVOKABLE void addFolder(QString name, int row); Q_INVOKABLE void addFolder(QString name, int row);
Q_INVOKABLE void removeFolder(int row); Q_INVOKABLE void removeFolder(int row);
Q_INVOKABLE void moveEntry(int fromRow, int toRow);
Q_INVOKABLE void load(); Q_INVOKABLE void load();
void save(); void save();