mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03:09 +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
|
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).
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue