mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 07:03:08 +00:00
homescreens/halcyon: Add drag and drop reordering of favourites
This commit is contained in:
parent
6a0fcc83fd
commit
b7f1a5b379
5 changed files with 214 additions and 79 deletions
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue