diff --git a/containments/homescreen/package/contents/ui/AppletsArea.qml b/containments/homescreen/package/contents/ui/AppletsArea.qml index 5074451..98b93ea 100644 --- a/containments/homescreen/package/contents/ui/AppletsArea.qml +++ b/containments/homescreen/package/contents/ui/AppletsArea.qml @@ -1,258 +1,259 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 MouseArea { id: headerItem z: 999 property Item layout: appletsLayout property Item lastSpacer: spacer property Item favoritesStrip: favoritesView width: root.width height: Math.max(applicationsView.height - stripe.height, mainLayout.Layout.minimumHeight) property int margin: stripe.height + units.gridUnit * 2 property Item draggingApplet property int startMouseX property int startMouseY property int oldMouseX property int oldMouseY drag.filterChildren: true EventGenerator { id: eventGenerator } SequentialAnimation { id: removeAnim property Item target property real to NumberAnimation { properties: "x" duration: units.longDuration easing.type: Easing.InOutQuad target: removeAnim.target to: removeAnim.to } ScriptAction { script: removeAnim.target.applet.action("remove").trigger(); } } onPressed: { startMouseX = mouse.x; startMouseY = mouse.y; } onPressAndHold: { var absolutePos = mapToItem(appletsLayout, mouse.x, mouse.y); var absoluteStartPos = mapToItem(appletsLayout, startMouseX, startMouseY); if (Math.abs(absolutePos.x - absoluteStartPos.x) > units.gridUnit*2 || Math.abs(absolutePos.y - absoluteStartPos.y) > units.gridUnit*2) { print("finger moved too much, press and hold canceled") return; } print(favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y))) if (!favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y))) { editOverlay.visible = true; var pos = mapToItem(appletsLayout, mouse.x, mouse.y); draggingApplet = appletsSpace.layout.childAt(absolutePos.x, absolutePos.y); editOverlay.applet = draggingApplet; oldMouseX = mouse.x; oldMouseY = mouse.y; eventGenerator.sendGrabEvent(draggingApplet, EventGenerator.UngrabMouse); eventGenerator.sendGrabEvent(headerItem, EventGenerator.GrabMouse); eventGenerator.sendMouseEvent(headerItem, EventGenerator.MouseButtonPress, mouse.x, mouse.y, Qt.LeftButton, Qt.LeftButton, 0) if (draggingApplet) { draggingApplet.animationsEnabled = false; dndSpacer.height = draggingApplet.height; root.layoutManager.insertBefore(draggingApplet, dndSpacer); draggingApplet.parent = headerItem; pos = mapToItem(headerItem, mouse.x, mouse.y); draggingApplet.y = pos.y - draggingApplet.height/2; applicationsView.interactive = false; } } } onPositionChanged: { if (!draggingApplet) { return; } applicationsView.interactive = false; if (Math.abs(mouse.x - startMouseX) > units.gridUnit || Math.abs(mouse.y - startMouseY) > units.gridUnit) { editOverlay.opacity = 0; } draggingApplet.x -= oldMouseX - mouse.x; draggingApplet.y -= oldMouseY - mouse.y; oldMouseX = mouse.x; oldMouseY = mouse.y; var pos = mapToItem(appletsLayout, mouse.x, mouse.y); var itemUnderMouse = appletsSpace.layout.childAt(pos.x, pos.y); if (itemUnderMouse && itemUnderMouse != dndSpacer) { dndSpacer.parent = colorScope; if (pos.y < itemUnderMouse.y + itemUnderMouse.height/2) { root.layoutManager.insertBefore(itemUnderMouse, dndSpacer); } else { root.layoutManager.insertAfter(itemUnderMouse, dndSpacer); } } pos = mapToItem(root, mouse.x, mouse.y); //SCROLL UP if (applicationsView.contentY > -applicationsView.headerItem.height + root.height && pos.y < root.height/4) { root.scrollUp(); //SCROLL DOWN } else if (applicationsView.contentY < 0 && pos.y > 3 * (root.height / 4)) { root.scrollDown(); //DON't SCROLL } else { root.stopScroll(); } } onReleased: { if (!draggingApplet) { return; } if (draggingApplet.x > -draggingApplet.width/3 && draggingApplet.x < draggingApplet.width/3) { draggingApplet.x = 0; root.layoutManager.insertBefore( dndSpacer, draggingApplet); } else { removeAnim.target = draggingApplet; removeAnim.to = (draggingApplet.x > 0) ? root.width : -root.width removeAnim.running = true; } applicationsView.interactive = true; dndSpacer.parent = colorScope; draggingApplet = null; } ColumnLayout { id: mainLayout anchors { fill: parent } Item { Layout.minimumHeight: krunner.inputHeight Layout.minimumWidth: Layout.minimumHeight } PlasmaCore.ColorScope { id: colorScope //TODO: decide what color we want applets colorGroup: PlasmaCore.Theme.NormalColorGroup Layout.fillWidth: true Layout.minimumHeight: appletsLayout.implicitHeight Layout.maximumHeight: appletsLayout.implicitHeight Column { id: appletsLayout width: parent.width move: Transition { NumberAnimation { properties: "x,y" duration: units.longDuration easing.type: Easing.InOutQuad } } } Item { id: dndSpacer width: parent.width } } } SatelliteStripe { id: stripe z: 99 property int viewPos: applicationsView.contentItem.height * applicationsView.visibleArea.yPosition y: Math.max(viewPos + krunner.inputHeight - units.smallSpacing, Math.min(parent.height, viewPos + plasmoid.availableScreenRect.height - height) ) PlasmaCore.IconItem { id: goUp source: "go-up" width: units.iconSizes.huge height: width colorGroup: PlasmaCore.Theme.ComplementaryColorGroup anchors { horizontalCenter: parent.horizontalCenter bottom: parent.top } MouseArea { anchors { fill: parent margins: -units.smallSpacing } onClicked: applicationsView.flick(0, -applicationsView.height/2) } } GridView { id: favoritesView //FIXME: QQuickItem has a contains, but seems to not work function contains(point) { return point.x > 0 && point.x < width && point.y > 0 && point.y < height; } anchors.fill: parent property int columns: 4 interactive: false flow: GridView.FlowTopToBottom cellWidth: root.width / 4 cellHeight: root.buttonHeight model: plasmoid.nativeInterface.applicationListModel delegate: HomeLauncher { maximumLineCount: 1 + iconSize: root.iconSize } move: Transition { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad properties: "x,y" } } moveDisplaced: Transition { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad properties: "x,y" } } } } } diff --git a/containments/homescreen/package/contents/ui/HomeLauncher.qml b/containments/homescreen/package/contents/ui/HomeLauncher.qml index f6cec7a..18c3ffe 100644 --- a/containments/homescreen/package/contents/ui/HomeLauncher.qml +++ b/containments/homescreen/package/contents/ui/HomeLauncher.qml @@ -1,54 +1,49 @@ -import QtQuick 2.0 +import QtQuick 2.5 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kio 1.0 as Kio import org.kde.plasma.components 2.0 as PlasmaComponents -Item { +Column { id: delegateRoot width: applicationsView.cellWidth height: applicationsView.cellHeight + property int iconSize property var modelData: model property bool isDropTarget: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole property alias maximumLineCount: label.maximumLineCount opacity: isDropTarget ? 0.3 : 1 PlasmaCore.IconItem { id: icon - anchors { - centerIn: parent - verticalCenterOffset: -theme.mSize(theme.defaultFont).height - } - width: parent.height - label.height - height: width + anchors.horizontalCenter: parent.horizontalCenter + width: delegateRoot.iconSize + height: delegateRoot.iconSize source: modelData.ApplicationIconRole scale: root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole != modelData.ApplicationStorageIdRole ? 0.6 : 1 Behavior on scale { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } } PlasmaComponents.Label { id: label visible: text.length > 0 - anchors { - top: icon.bottom - left: icon.left - right: icon.right - } + anchors.horizontalCenter: parent.horizontalCenter + width: icon.width wrapMode: Text.WordWrap horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter maximumLineCount: 2 text: modelData.ApplicationNameRole font.pixelSize: theme.defaultFont.pixelSize color: PlasmaCore.ColorScope.textColor } } diff --git a/containments/homescreen/package/contents/ui/main.qml b/containments/homescreen/package/contents/ui/main.qml index 1cde648..b78e386 100644 --- a/containments/homescreen/package/contents/ui/main.qml +++ b/containments/homescreen/package/contents/ui/main.qml @@ -1,518 +1,521 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 import "LayoutManager.js" as LayoutManager Item { id: root width: 480 height: 640 //BEGIN properties property Item toolBox property alias appletsSpace: applicationsView.headerItem - property int buttonHeight: units.iconSizes.large + units.gridUnit * 2 + readonly property int iconSize: units.iconSizes.large + property int buttonHeight: dragDelegate.height property bool reorderingApps: false property var layoutManager: LayoutManager //END properties //BEGIN functions function addApplet(applet, x, y) { var container = appletContainerComponent.createObject(appletsSpace.layout) container.visible = true print("Applet added: " + applet) var appletWidth = applet.width; var appletHeight = applet.height; applet.parent = container; container.applet = applet; applet.anchors.fill = container; applet.visible = true; container.visible = true; // If the provided position is valid, use it. if (x >= 0 && y >= 0) { var index = LayoutManager.insertAtCoordinates(container, x , y); // Fall through to determining an appropriate insert position. } else { var before = null; //container.animationsEnabled = false; if (before) { LayoutManager.insertBefore(before, container); // Fall through to adding at the end. } else { container.parent = appletsSpace.layout; } //event compress the enable of animations //startupTimer.restart(); } } //Autoscroll related functions function scrollUp() { autoScrollTimer.scrollDown = false; autoScrollTimer.running = true; scrollUpIndicator.opacity = 1; scrollDownIndicator.opacity = 0; } function scrollDown() { autoScrollTimer.scrollDown = true; autoScrollTimer.running = true; scrollUpIndicator.opacity = 0; scrollDownIndicator.opacity = 1; } function stopScroll() { autoScrollTimer.running = false; scrollUpIndicator.opacity = 0; scrollDownIndicator.opacity = 0; } //END functions //BEGIN slots Component.onCompleted: { LayoutManager.plasmoid = plasmoid; LayoutManager.root = root; LayoutManager.layout = appletsSpace.layout; LayoutManager.restore(); applicationsView.contentY = -applicationsView.headerItem.height*2; plasmoid.nativeInterface.applicationListModel.appOrder = plasmoid.configuration.AppOrder; plasmoid.nativeInterface.applicationListModel.loadApplications(); } Timer { interval: 200 running: true onTriggered: { applicationsView.contentY = -applicationsView.headerItem.height; } } Containment.onAppletAdded: { addApplet(applet, x, y); LayoutManager.save(); } Connections { target: plasmoid.nativeInterface.applicationListModel onAppOrderChanged: { plasmoid.configuration.AppOrder = plasmoid.nativeInterface.applicationListModel.appOrder; } } //END slots Timer { id: autoScrollTimer property bool scrollDown: true repeat: true interval: 1500 onTriggered: { //reordering launcher icons if (root.reorderingApps) { scrollAnim.to = scrollDown ? //Scroll down Math.min(applicationsView.contentItem.height - applicationsView.headerItem.height - root.height, applicationsView.contentY + root.height/2) : //Scroll up Math.max(0, applicationsView.contentY - root.height/2); //reordering applets } else { scrollAnim.to = scrollDown ? //Scroll down Math.min(-root.height, applicationsView.contentY + root.height/2) : //Scroll up Math.max(-applicationsView.headerItem.height + root.height, applicationsView.contentY - root.height/2); } scrollAnim.running = true; } } Component { id: appletContainerComponent MouseArea { id: appletContainer //not used yet property bool animationsEnabled: true property Item applet z: applet && applet.compactRepresentationItem && applet.expanded ? 99 : 0 opacity: 1 - Math.abs(x/(width/2)) Layout.fillWidth: true Layout.fillHeight: applet && applet.Layout.fillHeight Connections { target: plasmoid onAppletRemoved: { print("Applet removed Applet-" + applet.id) if (applet.id == appletContainer.applet.id) { appletContainer.destroy(); } } } onAppletChanged: { if (applet.backgroundHints == PlasmaCore.Types.StandardBackground) { applet.anchors.margins = background.margins.top; } } property int oldX: x property int oldY: y PlasmaCore.FrameSvgItem { id: background z: -1 anchors.fill: parent imagePath: "widgets/background" visible: applet.backgroundHints == PlasmaCore.Types.StandardBackground } width: parent.width height: Math.max(applet.switchHeight + 1 + background.margins.top + background.margins.bottom, Math.max(applet.Layout.minimumHeight, (root.height-applicationsView.headerItem.margin) / 2)) PlasmaComponents.BusyIndicator { z: 1000 visible: applet && applet.busy running: visible anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: width } } } SequentialAnimation { id: clickFedbackAnimation property Item target NumberAnimation { target: clickFedbackAnimation.target properties: "scale" to: 2 duration: units.longDuration easing.type: Easing.InOutQuad } PauseAnimation { duration: units.shortDuration } NumberAnimation { target: clickFedbackAnimation.target properties: "scale" to: 1 duration: units.longDuration easing.type: Easing.InOutQuad } } FeedbackWindow { id: feedbackWindow } KRunner { id: krunner z: 998 anchors { top: parent.top left: parent.left right: parent.right topMargin: plasmoid.availableScreenRect.y } } EditOverlay { id: editOverlay z: 999 } MouseEventListener { id: mainListener anchors.fill: parent //Events handling: those events are about clicking and reordering of app icons //applet related events are in AppeltsArea.qml onPressAndHold: { var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y); //in favorites area? var item; if (applicationsView.headerItem.favoritesStrip.contains(pos)) { item = applicationsView.headerItem.favoritesStrip.itemAt(pos.x, pos.y); } else { pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y); item = applicationsView.itemAt(pos.x, pos.y) } if (!item) { return; } applicationsView.dragData = new Object; applicationsView.dragData.ApplicationNameRole = item.modelData.ApplicationNameRole; applicationsView.dragData.ApplicationIconRole = item.modelData.ApplicationIconRole; applicationsView.dragData.ApplicationStorageIdRole = item.modelData.ApplicationStorageIdRole; applicationsView.dragData.ApplicationEntryPathRole = item.modelData.ApplicationEntryPathRole; applicationsView.dragData.ApplicationOriginalRowRole = item.modelData.ApplicationOriginalRowRole; dragDelegate.modelData = applicationsView.dragData; applicationsView.interactive = false; root.reorderingApps = true; dragDelegate.x = Math.floor(mouse.x / root.buttonHeight) * root.buttonHeight dragDelegate.y = Math.floor(mouse.y / root.buttonHeight) * root.buttonHeight dragDelegate.xTarget = mouse.x - dragDelegate.width/2; dragDelegate.yTarget = mouse.y - dragDelegate.width/2; dragDelegate.opacity = 1; } onPositionChanged: { if (!applicationsView.dragData) { return; } dragDelegate.x = mouse.x - dragDelegate.width/2; dragDelegate.y = mouse.y - dragDelegate.height/2; var pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y); //in favorites area? if (applicationsView.headerItem.favoritesStrip.contains(mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y))) { pos.y = 1; } var newRow = (Math.round(applicationsView.width / applicationsView.cellWidth) * Math.floor(pos.y / applicationsView.cellHeight) + Math.floor(pos.x / applicationsView.cellWidth)); if (applicationsView.dragData.ApplicationOriginalRowRole != newRow) { plasmoid.nativeInterface.applicationListModel.moveItem(applicationsView.dragData.ApplicationOriginalRowRole, newRow); applicationsView.dragData.ApplicationOriginalRowRole = newRow; } var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y); //FAVORITES if (applicationsView.headerItem.favoritesStrip.contains(pos)) { root.stopScroll(); //SCROLL UP } else if (applicationsView.contentY > 0 && mouse.y < root.buttonHeight + root.height / 4) { root.scrollUp(); //SCROLL DOWN } else if (!applicationsView.atYEnd && mouse.y > 3 * (root.height / 4)) { root.scrollDown(); //DON't SCROLL } else { root.stopScroll(); } } onReleased: { if (krunner.showingResults) { return; } applicationsView.interactive = true; dragDelegate.xTarget = Math.floor(mouse.x / root.buttonHeight) * root.buttonHeight; dragDelegate.yTarget = Math.floor(mouse.y / root.buttonHeight) * root.buttonHeight; dragDelegate.opacity = 0; if (dragDelegate.modelData) { dragDelegate.modelData.ApplicationIconRole = ""; dragDelegate.modelDataChanged(); } applicationsView.dragData = null; root.reorderingApps = false; applicationsView.forceLayout(); root.stopScroll(); } onClicked: { if (krunner.showingResults) { return; } var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y); //in favorites area? var item; if (applicationsView.headerItem.favoritesStrip.contains(pos)) { item = applicationsView.headerItem.favoritesStrip.itemAt(pos.x, pos.y); } else { pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y); item = applicationsView.itemAt(pos.x, pos.y) } if (!item) { return; } clickFedbackAnimation.target = item; clickFedbackAnimation.running = true; feedbackWindow.title = item.modelData.ApplicationNameRole; feedbackWindow.state = "open"; plasmoid.nativeInterface.applicationListModel.runApplication(item.modelData.ApplicationStorageIdRole); } PlasmaCore.ColorScope { anchors.fill: parent //TODO: decide what color we want applets colorGroup: PlasmaCore.Theme.ComplementaryColorGroup Rectangle { color: PlasmaCore.ColorScope.backgroundColor opacity: 0.9 * (Math.min(applicationsView.contentY + root.height, root.height) / root.height) anchors.fill: parent } PlasmaCore.Svg { id: arrowsSvg imagePath: "widgets/arrows" colorGroup: PlasmaCore.Theme.ComplementaryColorGroup } PlasmaCore.SvgItem { id: scrollUpIndicator anchors { horizontalCenter: parent.horizontalCenter top: parent.top topMargin: 300 } z: 2 opacity: 0 svg: arrowsSvg elementId: "up-arrow" width: units.iconSizes.large height: width Behavior on opacity { OpacityAnimator { duration: 1000 easing.type: Easing.InOutQuad } } } PlasmaCore.SvgItem { id: scrollDownIndicator anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom bottomMargin: units.gridUnit * 2 } z: 2 opacity: 0 svg: arrowsSvg elementId: "down-arrow" width: units.iconSizes.large height: width Behavior on opacity { OpacityAnimator { duration: 1000 easing.type: Easing.InOutQuad } } } //This HomeLauncher is the placeholder for the "drag" //delegate (that is not actual drag and drop HomeLauncher { id: dragDelegate z: 999 property int xTarget property int yTarget + iconSize: root.iconSize Behavior on opacity { ParallelAnimation { OpacityAnimator { duration: units.longDuration easing.type: Easing.InOutQuad } PropertyAnimation { properties: "x" to: dragDelegate.xTarget target: dragDelegate duration: units.longDuration easing.type: Easing.InOutQuad } PropertyAnimation { properties: "y" to: dragDelegate.yTarget target: dragDelegate duration: units.longDuration easing.type: Easing.InOutQuad } } } } GridView { id: applicationsView anchors { fill: parent topMargin: plasmoid.availableScreenRect.y bottomMargin: root.height - plasmoid.availableScreenRect.y - plasmoid.availableScreenRect.height } property var dragData cellWidth: root.width / 4 cellHeight: root.buttonHeight model: plasmoid.nativeInterface.applicationListModel snapMode: GridView.SnapToRow NumberAnimation { id: scrollAnim target: applicationsView properties: "contentY" duration: units.longDuration easing.type: Easing.InOutQuad } move: Transition { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad properties: "x,y" } } moveDisplaced: Transition { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad properties: "x,y" } } //clip: true delegate: HomeLauncher { visible: index > 3 + iconSize: root.iconSize } header: AppletsArea {} footer: Item { width: units. gridUnit * 4 height: width } } PlasmaComponents.ScrollBar { anchors { right: parent.right top: parent.top bottom: parent.bottom } interactive: false flickableItem: applicationsView } } } }