From fe6332b1d2fe0465a10e84ff41fb2615eca429e8 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 13 Mar 2015 15:31:13 +0100 Subject: [PATCH 01/86] use the new containment --- containments/homescreen/contents/ui/main.qml | 1 + shell/contents/defaults | 2 +- shell/contents/layout.js | 1 + shell/contents/views/Desktop.qml | 297 +------------------ 4 files changed, 18 insertions(+), 283 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index bc808af8..d52dfdf0 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -206,6 +206,7 @@ Item { Item { Layout.fillWidth: true Layout.minimumHeight: root.height + Layout.maximumHeight: root.height Clock { anchors { horizontalCenter: parent.horizontalCenter diff --git a/shell/contents/defaults b/shell/contents/defaults index c5458f35..8dc05ec7 100644 --- a/shell/contents/defaults +++ b/shell/contents/defaults @@ -2,7 +2,7 @@ LookAndFeelPackage=org.kde.satellite.phone [Desktop] -Containment=org.kde.desktopcontainment +Containment=org.kde.phone.homescreen ToolBox= [Desktop][ContainmentActions] diff --git a/shell/contents/layout.js b/shell/contents/layout.js index 5f3eace0..49acf7a7 100644 --- a/shell/contents/layout.js +++ b/shell/contents/layout.js @@ -14,3 +14,4 @@ for (var j = 0; j < desktopsArray.length; j++) { desktopsArray[j].writeConfig("Image", "org.kde.satellite.lockers"); } +desktopsForActivity(id)[0].addWidget("org.kde.phone.notifications"); diff --git a/shell/contents/views/Desktop.qml b/shell/contents/views/Desktop.qml index b6e914e4..24b11409 100644 --- a/shell/contents/views/Desktop.qml +++ b/shell/contents/views/Desktop.qml @@ -41,52 +41,25 @@ Item { property int notificationId: 0; property int buttonHeight: width/4 - /* - Notificadtion data object has the following properties: - appIcon - image - appName - summary - body - isPersistent - expireTimeout - urgency - appRealName - configurable - */ - function addNotification(source, data, actions) { - // Do not show duplicated notifications - // Remove notifications that are sent again (odd, but true) - for (var i = 0; i < notificationsModel.count; ++i) { - var tmp = notificationsModel.get(i); - var matches = (tmp.appName == data.appName && - tmp.summary == data.summary && - tmp.body == data.body); - var sameSource = tmp.source == source; + onContainmentChanged: { + containment.parent = homescreen; - if (sameSource && matches) { - return; - } - - if (sameSource || matches) { - notificationsModel.remove(i) - break; - } + if (containment != null) { + containment.visible = true; } - - data["id"] = ++notificationId; - data["source"] = source; - if (data["summary"].length < 1) { - data["summary"] = data["body"]; - data["body"] = ''; + if (containment != null) { + containment.anchors.left = homescreen.left; + containment.anchors.top = homescreen.top; + containment.anchors.right = homescreen.right; + containment.anchors.bottom = homescreen.bottom; } - data["actions"] = actions; + } - notificationsModel.insert(0, data); - if (!data["isPersistent"]) { - pendingRemovals.push(notificationId); - pendingTimer.start(); - } + PlasmaCore.DataSource { + id: timeSource + engine: "time" + connectedSources: ["Local"] + interval: 60 * 1000 } OfonoManager { @@ -190,91 +163,6 @@ Item { } } - Timer { - id: pendingTimer - interval: 5000 - repeat: false - onTriggered: { - for (var i = 0; i < pendingRemovals.length; ++i) { - var id = pendingRemovals[i]; - for (var j = 0; j < notificationsModel.count; ++j) { - if (notificationsModel.get(j).id == id) { - notificationsModel.remove(j); - } - } - } - pendingRemovals = []; - } - } - - Rectangle { - z: 1 - color: Qt.rgba(0, 0, 0, 0.9 * (Math.min(applications.contentY + homescreen.height, homescreen.height) / homescreen.height)) - anchors.fill: parent - } - - PlasmaCore.DataSource { - id: timeSource - engine: "time" - connectedSources: ["Local"] - interval: 60 * 1000 - } - PlasmaCore.DataSource { - id: notificationsSource - - engine: "notifications" - interval: 0 - - onSourceAdded: { - connectSource(source); - } - - onSourceRemoved: { - for (var i = 0; i < notificationsModel.count; ++i) { - if (notificationsModel.get(i) == source) { - notificationsModel.remove(i); - break; - } - } - } - - onNewData: { - var actions = new Array() - if (data["actions"] && data["actions"].length % 2 == 0) { - for (var i = 0; i < data["actions"].length; i += 2) { - var action = new Object(); - action["id"] = data["actions"][i]; - action["text"] = data["actions"][i+1]; - actions.push(action); - } - } - - homescreen.addNotification( - sourceName, - data, - actions); - } - - } - - ListModel { - id: notificationsModel - - ListElement { - appIcon: "call-start" - summary: "Missed call from Joe" - body: "Called at 8:42 from +41 56 373 37 31" - } - ListElement { - appIcon: "im-google" - summary: "July: Hey! Are you around?" - } - ListElement { - appIcon: "im-google" - summary: "July: Hello?" - } - } - Loader { id: dialerOverlay function open() { @@ -398,161 +286,6 @@ Item { height: homescreen.height } - - PlasmaCore.ColorScope { - z: 1 - anchors { - fill: parent - } - - colorGroup: PlasmaCore.Theme.ComplementaryColorGroup - - SatelliteComponents.ApplicationListModel { - id: appListModel - } - - GridView { - id: applications - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - right: parent.right - } - z: 1 - cellWidth: homescreen.buttonHeight - cellHeight: cellWidth - model: appListModel - snapMode: GridView.SnapToRow - clip: true - header: MouseArea { - z: 999 - width: homescreen.width - height: homescreen.height - units.iconSizes.medium - - onPressAndHold: { - containment.action("configure").trigger(); - } - - PlasmaComponents.Label { - id: bigClock - anchors { - horizontalCenter: parent.horizontalCenter - top: parent.top - bottom: notificationView.top - } - text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm") - color: PlasmaCore.ColorScope.textColor - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - font.pointSize: 40 - style: Text.Raised - styleColor: "black" - } - - ListView { - id: notificationView - spacing: units.smallSpacing - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - bottomMargin: stripe.height * 2 - } - height: parent.height / 3 - interactive: false - - z: 1 - verticalLayoutDirection: ListView.BottomToTop - model: notificationsModel - - add: Transition { - NumberAnimation { - properties: "x" - from: notificationView.width - duration: 100 - } - } - - remove: Transition { - NumberAnimation { - properties: "x" - to: notificationView.width - duration: 500 - } - NumberAnimation { - properties: "opacity" - to: 0 - duration: 500 - } - } - - removeDisplaced: Transition { - SequentialAnimation { - PauseAnimation { duration: 600 } - NumberAnimation { properties: "x,y"; duration: 100 } - } - } - - delegate: NotificationStripe {} - - } - SatelliteStripe { - id: stripe - z: 99 - y: Math.max(applications.contentY + parent.height, parent.height - height) - - PlasmaCore.Svg { - id: stripeIcons - imagePath: Qt.resolvedUrl("../images/homescreenicons.svg") - } - - Row { - anchors.fill: parent - property int columns: 4 - property alias buttonHeight: stripe.height - - HomeLauncherSvg { - id: phoneIcon - svg: stripeIcons - elementId: "phone" - callback: function() { - dialerOverlay.open() - } - } - - HomeLauncherSvg { - id: messagingIcon - svg: stripeIcons - elementId: "messaging" - callback: function() { console.log("Start messaging") } - } - - - HomeLauncherSvg { - id: emailIcon - svg: stripeIcons - elementId: "email" - callback: function() { console.log("Start email") } - } - - - HomeLauncherSvg { - id: webIcon - svg: stripeIcons - elementId: "web" - callback: function() { console.log("Start web") } - } - } - } - } - delegate: HomeLauncher {} - Component.onCompleted : { console.log("WTF " + width) } - - - } - } - Component.onCompleted: { //configure the view behavior if (desktop) { From 432deeff64933f68277a29ec9b5e15adc9375e3e Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 13 Mar 2015 17:17:24 +0100 Subject: [PATCH 02/86] prettier top panel --- shell/contents/views/Desktop.qml | 36 +++++++------ shell/contents/views/SlidingPanel.qml | 76 ++++++++++++++++++++++----- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/shell/contents/views/Desktop.qml b/shell/contents/views/Desktop.qml index 24b11409..c85eda6b 100644 --- a/shell/contents/views/Desktop.qml +++ b/shell/contents/views/Desktop.qml @@ -205,7 +205,7 @@ Item { Rectangle { anchors.fill: parent - color: Qt.rgba(0, 0, 0, 0.9) + color: PlasmaCore.ColorScope.backgroundColor PlasmaCore.IconItem { id: strengthIcon @@ -235,21 +235,7 @@ Item { verticalAlignment: Qt.AlignVCenter font.pixelSize: height / 2 } - MouseArea { - property int oldMouseY: 0 - - anchors.fill: parent - enabled: !dialerOverlay.item.visible - onPressed: { - oldMouseY = mouse.y; - slidingPanel.visible = true; - } - onPositionChanged: { - slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY); - oldMouseY = mouse.y; - } - onReleased: slidingPanel.updateState(); - } + PlasmaWorkspace.BatteryIcon { @@ -278,6 +264,24 @@ Item { } } } + MouseArea { + property int oldMouseY: 0 + + anchors.fill: parent + enabled: !dialerOverlay.item.visible + onPressed: { + oldMouseY = mouse.y; + slidingPanel.visible = true; + } + onPositionChanged: { + //var factor = (mouse.y - oldMouseY > 0) ? (1 - Math.max(0, (slidingArea.y + slidingPanel.overShoot) / slidingPanel.overShoot)) : 1 + var factor = 1; + print(slidingPanel.offset +" "+ slidingPanel.height) + slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY) * factor; + oldMouseY = mouse.y; + } + onReleased: slidingPanel.updateState(); + } } SlidingPanel { diff --git a/shell/contents/views/SlidingPanel.qml b/shell/contents/views/SlidingPanel.qml index 4ca70124..3cd03700 100644 --- a/shell/contents/views/SlidingPanel.qml +++ b/shell/contents/views/SlidingPanel.qml @@ -26,8 +26,10 @@ Window { flags: Qt.WindowDoesNotAcceptFocus property int offset: 0 + property int overShoot: units.gridUnit * 2 color: "transparent" + property alias contents: contentArea.data function updateState() { var delta = offset - mouseArea.startOffset; @@ -59,6 +61,7 @@ Window { property int oldMouseY: 0 property int startOffset: units.iconSizes.large; property string startState: "closed" + onPressed: { startState = state; startOffset = window.offset; @@ -67,26 +70,71 @@ Window { window.offset = startOffset; } onPositionChanged: { - window.offset = window.offset + (mouse.y - oldMouseY); + var factor = (mouse.y - oldMouseY > 0) ? (1 - Math.max(0, (slidingArea.y + overShoot) / overShoot)) : 1 + + window.offset = window.offset + (mouse.y - oldMouseY) * factor; oldMouseY = mouse.y; } - onReleased: window.updateState() + onReleased: { + if (Math.abs(window.offset - mouseArea.startOffset) < units.gridUnit && + slidingArea.y + slidingArea.height < mouse.y) { + mouseArea.state = "closed"; + } else { + window.updateState(); + } + } Rectangle { + anchors.fill: parent + color: Qt.rgba(0, 0, 0, 0.6-Math.abs(slidingArea.y/slidingArea.height)) + } + PlasmaCore.ColorScope { id: slidingArea width: parent.width - height: parent.height + height: parent.height/1.5 y: Math.min(0, -height + window.offset) - - color: Qt.rgba(0, 0, 0, 0.7) + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup Rectangle { - width: parent.width / 4 - height: units.gridUnit/2 - color: "yellow" - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom - bottomMargin: units.gridUnit/2 + anchors.fill: parent + + Item { + id: contentArea + anchors { + fill: parent + topMargin: overShoot + } + } + color: PlasmaCore.ColorScope.backgroundColor + Rectangle { + height: units.smallSpacing/2 + color: PlasmaCore.ColorScope.highlightColor + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + Rectangle { + height: units.gridUnit + anchors { + left: parent.left + right: parent.right + top: parent.bottom + } + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.rgba(0, 0, 0, 0.6) + } + GradientStop { + position: 0.5 + color: Qt.rgba(0, 0, 0, 0.2) + } + GradientStop { + position: 1.0 + color: "transparent" + } + } } } } @@ -103,7 +151,7 @@ Window { name: "open" PropertyChanges { target: window - offset: slidingArea.height + offset: slidingArea.height - overShoot } }, State { @@ -127,7 +175,7 @@ Window { PropertyAnimation { target: window duration: units.longDuration - easing: Easing.InOutQuad + easing.type: Easing.InOutQuad properties: "offset" } ScriptAction { From b6122d7e7a660041536c89232d79bcd9ed49376f Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 13 Mar 2015 17:25:57 +0100 Subject: [PATCH 03/86] reparent the panel when dragging this creates the impression that the user is dragging the top panel even if there is no window moving --- shell/contents/views/Desktop.qml | 21 ++++++++++++++++++++- shell/contents/views/SlidingPanel.qml | 12 ++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/shell/contents/views/Desktop.qml b/shell/contents/views/Desktop.qml index c85eda6b..f9879faf 100644 --- a/shell/contents/views/Desktop.qml +++ b/shell/contents/views/Desktop.qml @@ -204,7 +204,13 @@ Item { colorGroup: PlasmaCore.Theme.ComplementaryColorGroup Rectangle { - anchors.fill: parent + parent: slidingPanel.visible ? panelContents : statusPanel + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: units.iconSizes.small color: PlasmaCore.ColorScope.backgroundColor PlasmaCore.IconItem { @@ -263,6 +269,15 @@ Item { } } } + Rectangle { + height: units.smallSpacing/2 + color: PlasmaCore.ColorScope.highlightColor + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } } MouseArea { property int oldMouseY: 0 @@ -288,6 +303,10 @@ Item { id: slidingPanel width: homescreen.width height: homescreen.height + contents: Item { + id: panelContents + anchors.fill: parent + } } Component.onCompleted: { diff --git a/shell/contents/views/SlidingPanel.qml b/shell/contents/views/SlidingPanel.qml index 3cd03700..600eba2f 100644 --- a/shell/contents/views/SlidingPanel.qml +++ b/shell/contents/views/SlidingPanel.qml @@ -52,7 +52,7 @@ Window { MouseArea { id: mouseArea - y: units.iconSizes.small + y: 0 width: window.width height: window.height - y clip: true @@ -105,15 +105,7 @@ Window { } } color: PlasmaCore.ColorScope.backgroundColor - Rectangle { - height: units.smallSpacing/2 - color: PlasmaCore.ColorScope.highlightColor - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } + Rectangle { height: units.gridUnit anchors { From 6aafa9329a641dc4ace53cea2c5a64725006b3ca Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 13 Mar 2015 22:16:19 +0100 Subject: [PATCH 04/86] use an actual panel sliding panel logic moved to the phone panel containment things in the panel are actually justy qml, the applets are in a tabbar in the sliding thing --- containments/CMakeLists.txt | 1 + .../panel/contents/code/LayoutManager.js | 189 +++++++++++++ containments/panel/contents/config/main.xml | 14 + .../panel/contents/ui}/SlidingPanel.qml | 1 + containments/panel/contents/ui/main.qml | 263 ++++++++++++++++++ containments/panel/metadata.desktop | 18 ++ shell/contents/applet/AppletError.qml | 51 ++++ shell/contents/applet/CompactApplet.qml | 153 ++++++++++ .../applet/DefaultCompactRepresentation.qml | 61 ++++ shell/contents/layout.js | 4 + shell/contents/views/Desktop.qml | 157 ----------- shell/contents/views/Panel.qml | 41 +++ 12 files changed, 796 insertions(+), 157 deletions(-) create mode 100644 containments/panel/contents/code/LayoutManager.js create mode 100644 containments/panel/contents/config/main.xml rename {shell/contents/views => containments/panel/contents/ui}/SlidingPanel.qml (99%) create mode 100644 containments/panel/contents/ui/main.qml create mode 100644 containments/panel/metadata.desktop create mode 100644 shell/contents/applet/AppletError.qml create mode 100644 shell/contents/applet/CompactApplet.qml create mode 100644 shell/contents/applet/DefaultCompactRepresentation.qml create mode 100644 shell/contents/views/Panel.qml diff --git a/containments/CMakeLists.txt b/containments/CMakeLists.txt index 7e680eeb..7f2eaffd 100644 --- a/containments/CMakeLists.txt +++ b/containments/CMakeLists.txt @@ -1,2 +1,3 @@ plasma_install_package(homescreen org.kde.phone.homescreen) +plasma_install_package(panel org.kde.phone.panel) diff --git a/containments/panel/contents/code/LayoutManager.js b/containments/panel/contents/code/LayoutManager.js new file mode 100644 index 00000000..c3dc37ee --- /dev/null +++ b/containments/panel/contents/code/LayoutManager.js @@ -0,0 +1,189 @@ +/* + * Copyright 2013 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +.pragma library + + +var layout; +var root; +var plasmoid; +var lastSpacer; + + +function restore() { + var configString = String(plasmoid.configuration.AppletOrder) + + //array, a cell for encoded item order + var itemsArray = configString.split(";"); + + //map applet id->order in panel + var idsOrder = new Object(); + //map order in panel -> applet pointer + var appletsOrder = new Object(); + + for (var i = 0; i < itemsArray.length; i++) { + //property name: applet id + //property value: order + idsOrder[itemsArray[i]] = i; + } + + for (var i = 0; i < plasmoid.applets.length; ++i) { + if (idsOrder[plasmoid.applets[i].id] !== undefined) { + appletsOrder[idsOrder[plasmoid.applets[i].id]] = plasmoid.applets[i]; + //ones that weren't saved in AppletOrder go to the end + } else { + appletsOrder["unordered"+i] = plasmoid.applets[i]; + } + } + + //finally, restore the applets in the correct order + for (var i in appletsOrder) { + root.addApplet(appletsOrder[i], -1, -1) + } + //rewrite, so if in the orders there were now invalid ids or if some were missing creates a correct list instead + save(); +} + +function save() { + var ids = new Array(); + for (var i = 0; i < layout.children.length; ++i) { + var child = layout.children[i]; + + if (child.applet) { + ids.push(child.applet.id); + } + } + plasmoid.configuration.AppletOrder = ids.join(';'); +} + +function removeApplet (applet) { + for (var i = layout.children.length - 1; i >= 0; --i) { + var child = layout.children[i]; + if (child.applet === applet) { + child.destroy(); + } + } +} + +//insert item2 before item1 +function insertBefore(item1, item2) { + if (item1 === item2) { + return; + } + var removed = new Array(); + + var child; + + var i; + for (i = layout.children.length - 1; i >= 0; --i) { + child = layout.children[i]; + removed.push(child); + child.parent = root; + + if (child === item1) { + break; + } + } + + item2.parent = layout; + + for (var j = removed.length - 1; j >= 0; --j) { + removed[j].parent = layout; + } + return i; +} + +//insert item2 after item1 +function insertAfter(item1, item2) { + if (item1 === item2) { + return; + } + var removed = new Array(); + + var child; + + var i; + for (i = layout.children.length - 1; i >= 0; --i) { + child = layout.children[i]; + //never ever insert after lastSpacer + if (child === lastSpacer && item1 === lastSpacer) { + removed.push(child); + child.parent = root; + break; + } else if (child === item1) { + break; + } + + removed.push(child); + child.parent = root; + } + + item2.parent = layout; + + for (var j = removed.length - 1; j >= 0; --j) { + removed[j].parent = layout; + } + return i; +} + +function insertAtIndex(item, position) { + if (position < 0 || position >= layout.children.length) { + return; + } + + //never ever insert after lastSpacer + if (layout.children[position] === lastSpacer) { + --position; + } + + var removedItems = new Array(); + + for (var i = position; i < layout.children.length; ++i) { + var child = layout.children[position]; + child.parent = root; + removedItems.push(child); + } + + item.parent = layout; + for (var i in removedItems) { + removedItems[i].parent = layout; + } +} + +function insertAtCoordinates(item, x, y) { + if (root.isHorizontal) { + y = layout.height / 2; + } else { + x = layout.width / 2; + } + var child = layout.childAt(x, y); + + if (!child || child === item) { + child = layout.children[0]; + } + item.parent = root; + + //PlasmaCore.Types.Vertical = 3 + if ((plasmoid.formFactor === 3 && y < child.y + child.height/2) || + (plasmoid.formFactor !== 3 && x < child.x + child.width/2)) { + return insertBefore(child, item); + } else { + return insertAfter(child, item); + } +} diff --git a/containments/panel/contents/config/main.xml b/containments/panel/contents/config/main.xml new file mode 100644 index 00000000..61a525b9 --- /dev/null +++ b/containments/panel/contents/config/main.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/shell/contents/views/SlidingPanel.qml b/containments/panel/contents/ui/SlidingPanel.qml similarity index 99% rename from shell/contents/views/SlidingPanel.qml rename to containments/panel/contents/ui/SlidingPanel.qml index 600eba2f..61444b39 100644 --- a/shell/contents/views/SlidingPanel.qml +++ b/containments/panel/contents/ui/SlidingPanel.qml @@ -57,6 +57,7 @@ Window { height: window.height - y clip: true state: "closed" + drag.filterChildren: true property int oldMouseY: 0 property int startOffset: units.iconSizes.large; diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml new file mode 100644 index 00000000..0d520d41 --- /dev/null +++ b/containments/panel/contents/ui/main.qml @@ -0,0 +1,263 @@ +/* + * 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.1 +import QtQuick.Layouts 1.1 + +import MeeGo.QOfono 0.2 + +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.plasma.workspace.components 2.0 as PlasmaWorkspace + +import "plasmapackage:/code/LayoutManager.js" as LayoutManager + +PlasmaCore.ColorScope { + id: root + width: 480 + height: 640 + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + + property Item toolBox + property int buttonHeight: width/4 + property bool reorderingApps: false + + Containment.onAppletAdded: { + addApplet(applet, x, y); + LayoutManager.save(); + } + + function addApplet(applet, x, y) { + var container = appletContainerComponent.createObject(tabGroup) + 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; + + //container.parent = tabs; + + //The tab + var tab = tabComponent.createObject(tabBar.layout); + tab.iconSource = applet.icon; + tab.tab = container; + } + + Component.onCompleted: { + LayoutManager.plasmoid = plasmoid; + LayoutManager.root = root; + LayoutManager.layout = appletsLayout; + LayoutManager.restore(); + } + + RowLayout { + id: appletsLayout + Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height) + } + + Component { + id: appletContainerComponent + Item { + //not used yet + property bool animationsEnabled: false + property Item applet + Layout.fillWidth: true + Layout.fillHeight: true + } + } + + Component { + id: tabComponent + PlasmaComponents.TabButton { + width: parent.width / parent.children.length + height: units.iconSizes.huge + } + } + + + PlasmaCore.DataSource { + id: timeSource + engine: "time" + connectedSources: ["Local"] + interval: 60 * 1000 + } + + OfonoNetworkRegistration { + id: netreg + Component.onCompleted: { + netreg.scan() + updateStrengthIcon() + } + + onNetworkOperatorsChanged : { + console.log("operators :"+netreg.currentOperator["Name"].toString()) + } + modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" + function updateStrengthIcon() { + if (netreg.strength >= 100) { + strengthIcon.source = "network-mobile-100"; + } else if (netreg.strength >= 80) { + strengthIcon.source = "network-mobile-80"; + } else if (netreg.strength >= 60) { + strengthIcon.source = "network-mobile-60"; + } else if (netreg.strength >= 40) { + strengthIcon.source = "network-mobile-40"; + } else if (netreg.strength >= 20) { + strengthIcon.source = "network-mobile-20"; + } else { + strengthIcon.source = "network-mobile-0"; + } + } + + onStrengthChanged: { + console.log("Strength changed to " + netreg.strength) + updateStrengthIcon() + } + } + + Rectangle { + parent: slidingPanel.visible ? panelContents : root + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: root.height + color: PlasmaCore.ColorScope.backgroundColor + + PlasmaCore.IconItem { + id: strengthIcon + colorGroup: PlasmaCore.ColorScope.colorGroup + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + width: height + height: parent.height + } + PlasmaComponents.Label { + anchors { + left: strengthIcon.right + verticalCenter: parent.verticalCenter + } + text: netreg.strength + "% " + netreg.name + color: PlasmaCore.ColorScope.textColor + font.pixelSize: parent.height / 2 + } + PlasmaComponents.Label { + id: clock + anchors.fill: parent + text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm") + color: PlasmaCore.ColorScope.textColor + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pixelSize: height / 2 + } + + + PlasmaWorkspace.BatteryIcon { + id: batteryIcon + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + } + width: height + height: parent.height + hasBattery: pmSource.data["Battery"]["Has Battery"] + // batteryType: "Phone" + percent: pmSource.data["Battery0"] ? pmSource.data["Battery0"]["Percent"] : 0 + + PlasmaCore.DataSource { + id: pmSource + engine: "powermanagement" + connectedSources: sources + onSourceAdded: { + disconnectSource(source); + connectSource(source); + } + onSourceRemoved: { + disconnectSource(source); + } + } + } + Rectangle { + height: units.smallSpacing/2 + color: PlasmaCore.ColorScope.highlightColor + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + } + MouseArea { + property int oldMouseY: 0 + + anchors.fill: parent + onPressed: { + oldMouseY = mouse.y; + slidingPanel.visible = true; + } + onPositionChanged: { + //var factor = (mouse.y - oldMouseY > 0) ? (1 - Math.max(0, (slidingArea.y + slidingPanel.overShoot) / slidingPanel.overShoot)) : 1 + var factor = 1; + print(slidingPanel.offset +" "+ slidingPanel.height) + slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY) * factor; + oldMouseY = mouse.y; + } + onReleased: slidingPanel.updateState(); + } + + SlidingPanel { + id: slidingPanel + width: plasmoid.availableScreenRect.width + height: plasmoid.availableScreenRect.height + contents: Item { + id: panelContents + anchors.fill: parent + + PlasmaComponents.TabBar { + id: tabBar + anchors { + left: parent.left + top: parent.top + right: parent.right + margins: units.smallSpacing + } + height: units.iconSizes.huge + } + PlasmaComponents.TabGroup { + id: tabGroup + anchors { + left: parent.left + top: tabBar.bottom + right: parent.right + bottom: parent.bottom + margins: units.smallSpacing + } + } + } + } +} diff --git a/containments/panel/metadata.desktop b/containments/panel/metadata.desktop new file mode 100644 index 00000000..34195117 --- /dev/null +++ b/containments/panel/metadata.desktop @@ -0,0 +1,18 @@ +[Desktop Entry] +Encoding=UTF-8 +Keywords= +Name=Phone Panel +Type=Service + +X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment +X-Plasma-API=declarativeappletscript +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Marco Martin +X-KDE-PluginInfo-Category= +X-KDE-PluginInfo-Email=mart@kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-PluginInfo-Name=org.kde.phone.panel +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website= +X-Plasma-MainScript=ui/main.qml +X-Plasma-ContainmentType=Panel diff --git a/shell/contents/applet/AppletError.qml b/shell/contents/applet/AppletError.qml new file mode 100644 index 00000000..cfa83607 --- /dev/null +++ b/shell/contents/applet/AppletError.qml @@ -0,0 +1,51 @@ +/* + * Copyright 2013 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.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +RowLayout { + id: root + Layout.minimumWidth: units.gridUnit * 20 + Layout.minimumHeight: units.gridUnit * 8 + + property alias reason: messageText.text + + clip: true + + PlasmaCore.IconItem { + id: icon + anchors.verticalCenter: parent.verticalCenter + Layout.minimumWidth: units.iconSizes.huge + Layout.minimumHeight: units.iconSizes.huge + source: "dialog-error" + } + + PlasmaComponents.TextArea { + id: messageText + Layout.fillWidth: true + Layout.fillHeight: true + verticalAlignment: TextEdit.AlignVCenter + backgroundVisible: false + readOnly: true + width: parent.width - icon.width + wrapMode: Text.Wrap + } +} diff --git a/shell/contents/applet/CompactApplet.qml b/shell/contents/applet/CompactApplet.qml new file mode 100644 index 00000000..3405338c --- /dev/null +++ b/shell/contents/applet/CompactApplet.qml @@ -0,0 +1,153 @@ +/* + * Copyright 2013 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.0 +import QtQuick.Layouts 1.1 +import QtQuick.Window 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 + +PlasmaCore.ToolTipArea { + id: root + objectName: "org.kde.desktop-CompactApplet" + anchors.fill: parent + + icon: plasmoid.icon + mainText: plasmoid.toolTipMainText + subText: plasmoid.toolTipSubText + location: plasmoid.location + active: !plasmoid.expanded + property Item fullRepresentation + property Item compactRepresentation + property Item expandedFeedback: expandedItem + + onCompactRepresentationChanged: { + if (compactRepresentation) { + compactRepresentation.parent = root; + compactRepresentation.anchors.fill = root; + compactRepresentation.visible = true; + } + root.visible = true; + } + + onFullRepresentationChanged: { + + if (!fullRepresentation) { + return; + } + //if the fullRepresentation size was restored to a stored size, or if is dragged from the desktop, restore popup size + if (fullRepresentation.width > 0) { + popupWindow.mainItem.width = fullRepresentation.width; + } else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredWidth > 0) { + popupWindow.mainItem.width = fullRepresentation.Layout.preferredWidth + } else if (fullRepresentation.implicitWidth > 0) { + popupWindow.mainItem.width = fullRepresentation.implicitWidth + } else { + popupWindow.mainItem.width = theme.mSize(theme.defaultFont).width * 35 + } + + if (fullRepresentation.height > 0) { + popupWindow.mainItem.height = fullRepresentation.height; + } else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredHeight > 0) { + popupWindow.mainItem.height = fullRepresentation.Layout.preferredHeight + } else if (fullRepresentation.implicitHeight > 0) { + popupWindow.mainItem.height = fullRepresentation.implicitHeight + } else { + popupWindow.mainItem.height = theme.mSize(theme.defaultFont).height * 25 + } + + fullRepresentation.parent = appletParent; + fullRepresentation.anchors.fill = fullRepresentation.parent; + } + + PlasmaCore.FrameSvgItem { + id: expandedItem + anchors.fill: parent + imagePath: "widgets/tabbar" + visible: fromCurrentTheme + prefix: { + var prefix; + switch (plasmoid.location) { + case PlasmaCore.Types.LeftEdge: + prefix = "west-active-tab"; + break; + case PlasmaCore.Types.TopEdge: + prefix = "north-active-tab"; + break; + case PlasmaCore.Types.RightEdge: + prefix = "east-active-tab"; + break; + default: + prefix = "south-active-tab"; + } + if (!hasElementPrefix(prefix)) { + prefix = "active-tab"; + } + return prefix; + } + opacity: plasmoid.expanded ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: units.shortDuration + easing.type: Easing.InOutQuad + } + } + } + + Timer { + id: expandedSync + interval: 100 + onTriggered: plasmoid.expanded = popupWindow.visible; + } + + PlasmaCore.Dialog { + id: popupWindow + objectName: "popupWindow" + flags: Qt.WindowStaysOnTopHint + visible: plasmoid.expanded && fullRepresentation + visualParent: compactRepresentation ? compactRepresentation : null + location: plasmoid.location + hideOnWindowDeactivate: plasmoid.hideOnWindowDeactivate + + property var oldStatus: PlasmaCore.Types.UnknownStatus + + //It's a MouseEventListener to get all the events, so the eventfilter will be able to catch them + mainItem: MouseEventListener { + id: appletParent + Layout.minimumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumWidth : 0 + Layout.minimumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumHeight: 0 + Layout.maximumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumWidth : Infinity + Layout.maximumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumHeight: Infinity + } + + onVisibleChanged: { + if (!visible) { + expandedSync.restart(); + plasmoid.status = oldStatus; + } else { + oldStatus = plasmoid.status; + plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus; + // This call currently fails and complains at runtime: + // QWindow::setWindowState: QWindow::setWindowState does not accept Qt::WindowActive + popupWindow.requestActivate(); + } + } + + } +} diff --git a/shell/contents/applet/DefaultCompactRepresentation.qml b/shell/contents/applet/DefaultCompactRepresentation.qml new file mode 100644 index 00000000..f1611053 --- /dev/null +++ b/shell/contents/applet/DefaultCompactRepresentation.qml @@ -0,0 +1,61 @@ +/* + * Copyright 2013 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.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.IconItem { + id: icon + + Layout.minimumWidth: { + switch (plasmoid.formFactor) { + case PlasmaCore.Types.Vertical: + return 0; + case PlasmaCore.Types.Horizontal: + return height; + default: + return units.gridUnit * 3; + } + } + + Layout.minimumHeight: { + switch (plasmoid.formFactor) { + case PlasmaCore.Types.Vertical: + return width; + case PlasmaCore.Types.Horizontal: + return 0; + default: + return units.gridUnit * 3; + } + } + + source: plasmoid.icon ? plasmoid.icon : "plasma" + active: mouseArea.containsMouse + + MouseArea { + id: mouseArea + + property bool wasExpanded: false + + anchors.fill: parent + hoverEnabled: true + onPressed: wasExpanded = plasmoid.expanded + onClicked: plasmoid.expanded = !wasExpanded + } +} \ No newline at end of file diff --git a/shell/contents/layout.js b/shell/contents/layout.js index 49acf7a7..df22c90d 100644 --- a/shell/contents/layout.js +++ b/shell/contents/layout.js @@ -15,3 +15,7 @@ for (var j = 0; j < desktopsArray.length; j++) { } desktopsForActivity(id)[0].addWidget("org.kde.phone.notifications"); + +var panel = new Panel("org.kde.phone.panel"); +panel.addWidget("org.kde.plasma.battery"); +panel.addWidget("org.kde.plasma.devicenotifier"); \ No newline at end of file diff --git a/shell/contents/views/Desktop.qml b/shell/contents/views/Desktop.qml index f9879faf..5f371f01 100644 --- a/shell/contents/views/Desktop.qml +++ b/shell/contents/views/Desktop.qml @@ -55,13 +55,6 @@ Item { } } - PlasmaCore.DataSource { - id: timeSource - engine: "time" - connectedSources: ["Local"] - interval: 60 * 1000 - } - OfonoManager { id: ofonoManager onAvailableChanged: { @@ -104,39 +97,6 @@ Item { modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" } - OfonoNetworkRegistration { - id: netreg - Component.onCompleted: { - netreg.scan() - updateStrengthIcon() - } - - onNetworkOperatorsChanged : { - console.log("operators :"+netreg.currentOperator["Name"].toString()) - } - modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" - function updateStrengthIcon() { - if (netreg.strength >= 100) { - strengthIcon.source = "network-mobile-100"; - } else if (netreg.strength >= 80) { - strengthIcon.source = "network-mobile-80"; - } else if (netreg.strength >= 60) { - strengthIcon.source = "network-mobile-60"; - } else if (netreg.strength >= 40) { - strengthIcon.source = "network-mobile-40"; - } else if (netreg.strength >= 20) { - strengthIcon.source = "network-mobile-20"; - } else { - strengthIcon.source = "network-mobile-0"; - } - } - - onStrengthChanged: { - console.log("Strength changed to " + netreg.strength) - updateStrengthIcon() - } - } - OfonoNetworkOperator { id: netop } @@ -192,123 +152,6 @@ Item { source: simManager.pinRequired != OfonoSimManager.NoPin ? Qt.resolvedUrl("Pin.qml") : "" } - PlasmaCore.ColorScope { - id: statusPanel - anchors { - top: parent.top - left: parent.left - right: parent.right - } - height: units.iconSizes.small - z: 2 - colorGroup: PlasmaCore.Theme.ComplementaryColorGroup - - Rectangle { - parent: slidingPanel.visible ? panelContents : statusPanel - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: units.iconSizes.small - color: PlasmaCore.ColorScope.backgroundColor - - PlasmaCore.IconItem { - id: strengthIcon - colorGroup: PlasmaCore.ColorScope.colorGroup - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - width: units.iconSizes.small - height: width - } - PlasmaComponents.Label { - anchors { - left: strengthIcon.right - verticalCenter: parent.verticalCenter - } - text: netreg.strength + "% " + netreg.name - color: PlasmaCore.ColorScope.textColor - font.pixelSize: parent.height / 2 - } - PlasmaComponents.Label { - id: clock - anchors.fill: parent - text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm") - color: PlasmaCore.ColorScope.textColor - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - font.pixelSize: height / 2 - } - - - - PlasmaWorkspace.BatteryIcon { - id: batteryIcon - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - } - width: units.iconSizes.small - height: width - hasBattery: pmSource.data["Battery"]["Has Battery"] - batteryType: "Phone" - percent: pmSource.data["Battery0"] ? pmSource.data["Battery0"]["Percent"] : 0 - - PlasmaCore.DataSource { - id: pmSource - engine: "powermanagement" - connectedSources: sources - onSourceAdded: { - disconnectSource(source); - connectSource(source); - } - onSourceRemoved: { - disconnectSource(source); - } - } - } - Rectangle { - height: units.smallSpacing/2 - color: PlasmaCore.ColorScope.highlightColor - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - } - MouseArea { - property int oldMouseY: 0 - - anchors.fill: parent - enabled: !dialerOverlay.item.visible - onPressed: { - oldMouseY = mouse.y; - slidingPanel.visible = true; - } - onPositionChanged: { - //var factor = (mouse.y - oldMouseY > 0) ? (1 - Math.max(0, (slidingArea.y + slidingPanel.overShoot) / slidingPanel.overShoot)) : 1 - var factor = 1; - print(slidingPanel.offset +" "+ slidingPanel.height) - slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY) * factor; - oldMouseY = mouse.y; - } - onReleased: slidingPanel.updateState(); - } - } - - SlidingPanel { - id: slidingPanel - width: homescreen.width - height: homescreen.height - contents: Item { - id: panelContents - anchors.fill: parent - } - } - Component.onCompleted: { //configure the view behavior if (desktop) { diff --git a/shell/contents/views/Panel.qml b/shell/contents/views/Panel.qml new file mode 100644 index 00000000..01cdbd2f --- /dev/null +++ b/shell/contents/views/Panel.qml @@ -0,0 +1,41 @@ +/* + * Copyright 2012 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.0 +import QtQuick.Layouts 1.1 + +import org.kde.plasma.core 2.0 as PlasmaCore + + +PlasmaCore.FrameSvgItem { + id: root + + visible: false //adjust borders is run during setup. We want to avoid painting till completed + + property Item containment + + onContainmentChanged: { + containment.parent = root; + containment.visible = true; + containment.anchors.fill = root; + } + + Component.onCompleted: { + visible = true + } +} From d0756040adecc497c4812bc6984f4e65110938b3 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 18 Mar 2015 13:49:28 +0100 Subject: [PATCH 05/86] try to make the sliding panel fullscreen --- .../panel/contents/ui/SlidingPanel.qml | 2 +- containments/panel/contents/ui/main.qml | 25 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/containments/panel/contents/ui/SlidingPanel.qml b/containments/panel/contents/ui/SlidingPanel.qml index 61444b39..8629edd1 100644 --- a/containments/panel/contents/ui/SlidingPanel.qml +++ b/containments/panel/contents/ui/SlidingPanel.qml @@ -18,7 +18,7 @@ */ import QtQuick 2.0 -import QtQuick.Window 2.0 +import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore Window { diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml index 0d520d41..fc1000c6 100644 --- a/containments/panel/contents/ui/main.qml +++ b/containments/panel/contents/ui/main.qml @@ -177,17 +177,17 @@ PlasmaCore.ColorScope { } - PlasmaWorkspace.BatteryIcon { - id: batteryIcon - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - } - width: height - height: parent.height - hasBattery: pmSource.data["Battery"]["Has Battery"] - // batteryType: "Phone" - percent: pmSource.data["Battery0"] ? pmSource.data["Battery0"]["Percent"] : 0 + PlasmaWorkspace.BatteryIcon { + id: batteryIcon + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + } + width: height + height: parent.height + hasBattery: pmSource.data["Battery"]["Has Battery"] + // batteryType: "Phone" + percent: pmSource.data["Battery0"] ? pmSource.data["Battery0"]["Percent"] : 0 PlasmaCore.DataSource { id: pmSource @@ -218,12 +218,11 @@ PlasmaCore.ColorScope { anchors.fill: parent onPressed: { oldMouseY = mouse.y; - slidingPanel.visible = true; + slidingPanel.visibility = Qt.WindowFullScreen; } onPositionChanged: { //var factor = (mouse.y - oldMouseY > 0) ? (1 - Math.max(0, (slidingArea.y + slidingPanel.overShoot) / slidingPanel.overShoot)) : 1 var factor = 1; - print(slidingPanel.offset +" "+ slidingPanel.height) slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY) * factor; oldMouseY = mouse.y; } From 25e1e12c4dce309c8df81a8d4743c79cfbeedf9e Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 18 Mar 2015 14:34:05 +0100 Subject: [PATCH 06/86] save and restore app order by default apps are in alphabetical order but the user can drag and drop them around for personalized order --- .../homescreen/contents/config/main.xml | 3 ++ containments/homescreen/contents/ui/main.qml | 6 +++ qmlcomponents/applicationlistmodel.cpp | 53 ++++++++++++++++++- qmlcomponents/applicationlistmodel.h | 11 +++- 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/containments/homescreen/contents/config/main.xml b/containments/homescreen/contents/config/main.xml index 61a525b9..1f27dcef 100644 --- a/containments/homescreen/contents/config/main.xml +++ b/containments/homescreen/contents/config/main.xml @@ -9,6 +9,9 @@ + + + diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index d52dfdf0..6582cac6 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -92,10 +92,16 @@ Item { LayoutManager.lastSpacer = appletsSpace.lastSpacer; LayoutManager.restore(); applicationsView.contentY = -root.height; + + appListModel.appOrder = plasmoid.configuration.AppOrder; + appListModel.loadApplications(); } SatelliteComponents.ApplicationListModel { id: appListModel + onAppOrderChanged: { + plasmoid.configuration.AppOrder = appListModel.appOrder; + } } Timer { diff --git a/qmlcomponents/applicationlistmodel.cpp b/qmlcomponents/applicationlistmodel.cpp index 9cd69bd7..f8627c86 100644 --- a/qmlcomponents/applicationlistmodel.cpp +++ b/qmlcomponents/applicationlistmodel.cpp @@ -36,7 +36,6 @@ ApplicationListModel::ApplicationListModel(QObject *parent) : QAbstractListModel(parent) { - loadApplications(); } ApplicationListModel::~ApplicationListModel() @@ -55,6 +54,10 @@ QHash ApplicationListModel::roleNames() const return roleNames; } +bool appNameLessThan(const ApplicationData &a1, const ApplicationData &a2) +{ + return a1.name.toLower() < a2.name.toLower(); +} void ApplicationListModel::loadApplications() { @@ -64,6 +67,9 @@ void ApplicationListModel::loadApplications() if (!group || !group->isValid()) return; KServiceGroup::List subGroupList = group->entries(true); + QMap orderedList; + QList unorderedList; + // Iterate over all entries in the group for(KServiceGroup::List::ConstIterator it = subGroupList.begin();it != subGroupList.end(); it++) { KSycocaEntry::Ptr groupEntry = (*it); @@ -85,7 +91,12 @@ void ApplicationListModel::loadApplications() data.icon = service->icon(); data.storageId = service->storageId(); data.entryPath = service->exec(); - m_applicationList << data; + + if (m_appPositions.contains(service->storageId())) { + orderedList[m_appPositions.value(service->storageId())] = data; + } else { + unorderedList << data; + } } } } @@ -93,6 +104,10 @@ void ApplicationListModel::loadApplications() } } + std::sort(unorderedList.begin(), unorderedList.end(), appNameLessThan); + m_applicationList << orderedList.values(); + m_applicationList << unorderedList; + endResetModel(); emit countChanged(); } @@ -143,6 +158,19 @@ Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination) beginMoveRows(QModelIndex(), row, row, QModelIndex(), destination); ApplicationData data = m_applicationList.takeAt(row); m_applicationList.insert(destination, data); + + + m_appOrder.clear(); + m_appPositions.clear(); + int i = 0; + for (auto app : m_applicationList) { + m_appOrder << app.storageId; + m_appPositions[app.storageId] = i; + ++i; + } + + + emit appOrderChanged(); endMoveRows(); } @@ -157,4 +185,25 @@ void ApplicationListModel::runApplication(const QString &storageId) QProcess::startDetached(service->exec()); } +QStringList ApplicationListModel::appOrder() const +{ + return m_appOrder; +} + +void ApplicationListModel::setAppOrder(const QStringList &order) +{ + if (m_appOrder == order) { + return; + } + + m_appOrder = order; + m_appPositions.clear(); + int i = 0; + for (auto app : m_appOrder) { + m_appPositions[app] = i; + ++i; + } + emit appOrderChanged(); +} + #include "applicationlistmodel.moc" diff --git a/qmlcomponents/applicationlistmodel.h b/qmlcomponents/applicationlistmodel.h index 8e4f82ee..5c508888 100644 --- a/qmlcomponents/applicationlistmodel.h +++ b/qmlcomponents/applicationlistmodel.h @@ -38,6 +38,7 @@ class ApplicationListModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QStringList appOrder READ appOrder WRITE setAppOrder NOTIFY appOrderChanged) public: ApplicationListModel(QObject *parent = 0); @@ -59,16 +60,24 @@ public: ApplicationOriginalRowRole = Qt::UserRole + 6 }; + QStringList appOrder() const; + void setAppOrder(const QStringList &order); + Q_INVOKABLE void moveItem(int row, int order); Q_INVOKABLE void runApplication(const QString &storageId); + Q_INVOKABLE void loadApplications(); + Q_SIGNALS: void countChanged(); + void appOrderChanged(); private: QList m_applicationList; - void loadApplications(); + + QStringList m_appOrder; + QHash m_appPositions; }; #endif // APPLICATIONLISTMODEL_H From ffa4137cd08fbeb0f944a5afb02134dce7e58a07 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 18 Mar 2015 14:57:54 +0100 Subject: [PATCH 07/86] watch for sycoca changes when a new app is installed --- qmlcomponents/applicationlistmodel.cpp | 17 +++++++++++++++++ qmlcomponents/applicationlistmodel.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/qmlcomponents/applicationlistmodel.cpp b/qmlcomponents/applicationlistmodel.cpp index f8627c86..639a30f2 100644 --- a/qmlcomponents/applicationlistmodel.cpp +++ b/qmlcomponents/applicationlistmodel.cpp @@ -30,12 +30,16 @@ #include #include #include +#include #include #include ApplicationListModel::ApplicationListModel(QObject *parent) : QAbstractListModel(parent) { + //can't use the new syntax as this signal is overloaded + connect(KSycoca::self(), SIGNAL(databaseChanged(const QStringList &)), + this, SLOT(sycocaDbChanged(const QStringList &))); } ApplicationListModel::~ApplicationListModel() @@ -54,6 +58,17 @@ QHash ApplicationListModel::roleNames() const return roleNames; } +void ApplicationListModel::sycocaDbChanged(const QStringList &changes) +{ + if (!changes.contains("apps") && !changes.contains("xdgdata-apps")) { + return; + } + + m_applicationList.clear(); + + loadApplications(); +} + bool appNameLessThan(const ApplicationData &a1, const ApplicationData &a2) { return a1.name.toLower() < a2.name.toLower(); @@ -63,6 +78,8 @@ void ApplicationListModel::loadApplications() { beginResetModel(); + m_applicationList.clear(); + KServiceGroup::Ptr group = KServiceGroup::root(); if (!group || !group->isValid()) return; KServiceGroup::List subGroupList = group->entries(true); diff --git a/qmlcomponents/applicationlistmodel.h b/qmlcomponents/applicationlistmodel.h index 5c508888..15cc5272 100644 --- a/qmlcomponents/applicationlistmodel.h +++ b/qmlcomponents/applicationlistmodel.h @@ -69,6 +69,9 @@ public: Q_INVOKABLE void loadApplications(); +public Q_SLOTS: + void sycocaDbChanged(const QStringList &change); + Q_SIGNALS: void countChanged(); void appOrderChanged(); From f9f28f75ed68ce3c003c4b224451e3c91aa73e1b Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 18 Mar 2015 15:24:27 +0100 Subject: [PATCH 08/86] better layout with multiple applets --- containments/homescreen/contents/ui/main.qml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 6582cac6..4d297bd4 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -65,7 +65,8 @@ Item { container.animationsEnabled = false; if (appletsSpace.lastSpacer.parent === appletsSpace.layout) { - before = appletsSpace.lastSpacer; + //Uncomment to make the spacer the last element again + // before = appletsSpace.lastSpacer; } if (before) { @@ -172,13 +173,13 @@ Item { onFlickingChanged: { if (!draggingVertically && contentY < -headerItem.height + root.height) { - scrollAnim.to = Math.round(contentY/root.height) * root.height + scrollAnim.to = Math.round(contentY/root.height) * root.height - headerItem.margin scrollAnim.running = true; } } onDraggingVerticallyChanged: { if (!draggingVertically && contentY < -headerItem.height + root.height) { - scrollAnim.to = Math.round(contentY/root.height) * root.height + scrollAnim.to = Math.round(contentY/root.height) * root.height - headerItem.margin scrollAnim.running = true; } } @@ -197,7 +198,8 @@ Item { property Item layout: appletsLayout property Item lastSpacer: spacer width: root.width - height: mainLayout.Layout.minimumHeight + height: mainLayout.Layout.minimumHeight + stripe.height + units.gridUnit + property int margin: stripe.height + units.gridUnit * 2 onPressAndHold: { plasmoid.action("configure").trigger(); @@ -233,11 +235,13 @@ Item { } ColumnLayout { id: appletsLayout - Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height) + //Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height) Item { id: spacer Layout.fillWidth: true Layout.fillHeight: true + Layout.minimumHeight: plasmoid.applets.length % 2 == 0 ? 0 : root.height/2 + Layout.maximumHeight: Layout.minimumHeight } } } From e130d3a24fc5fc8ba5de3178ce9a9b82c1e5dd6c Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 18 Mar 2015 15:41:38 +0100 Subject: [PATCH 09/86] better layout in case of single applet --- containments/homescreen/contents/ui/main.qml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 4d297bd4..31ce38ce 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -135,7 +135,7 @@ Item { } Layout.minimumWidth: root.width - Layout.minimumHeight: Math.max(applet.Layout.minimumHeight, root.height / 2) + Layout.minimumHeight: Math.max(applet.Layout.minimumHeight, (root.height-applicationsView.headerItem.margin) / 2) Layout.preferredWidth: root.width Layout.preferredHeight: Layout.minimumHeight @@ -173,13 +173,13 @@ Item { onFlickingChanged: { if (!draggingVertically && contentY < -headerItem.height + root.height) { - scrollAnim.to = Math.round(contentY/root.height) * root.height - headerItem.margin + scrollAnim.to = Math.round(contentY/root.height) * root.height scrollAnim.running = true; } } onDraggingVerticallyChanged: { if (!draggingVertically && contentY < -headerItem.height + root.height) { - scrollAnim.to = Math.round(contentY/root.height) * root.height - headerItem.margin + scrollAnim.to = Math.round(contentY/root.height) * root.height scrollAnim.running = true; } } @@ -198,7 +198,7 @@ Item { property Item layout: appletsLayout property Item lastSpacer: spacer width: root.width - height: mainLayout.Layout.minimumHeight + stripe.height + units.gridUnit + height: mainLayout.Layout.minimumHeight property int margin: stripe.height + units.gridUnit * 2 onPressAndHold: { @@ -209,7 +209,6 @@ Item { id: mainLayout anchors { fill: parent - bottomMargin: stripe.height + units.gridUnit * 2 } Item { Layout.fillWidth: true @@ -235,15 +234,20 @@ Item { } ColumnLayout { id: appletsLayout - //Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height) Item { id: spacer Layout.fillWidth: true Layout.fillHeight: true - Layout.minimumHeight: plasmoid.applets.length % 2 == 0 ? 0 : root.height/2 + Layout.minimumHeight: plasmoid.applets.length % 2 == 0 ? 0 : (root.height - margin)/2 Layout.maximumHeight: Layout.minimumHeight } } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: margin + Layout.maximumHeight: Layout.minimumHeight + } } SatelliteStripe { id: stripe From 243be5ad03b4d6e411dd1c4ba1265b05852a4e60 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 18 Mar 2015 15:45:48 +0100 Subject: [PATCH 10/86] use color from theme --- containments/homescreen/contents/ui/main.qml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 31ce38ce..c5a95e0c 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -145,15 +145,16 @@ Item { } } - Rectangle { - color: Qt.rgba(0, 0, 0, 0.9 * (Math.min(applicationsView.contentY + root.height, root.height) / root.height)) - anchors.fill: parent - } - PlasmaCore.ColorScope { anchors.fill: parent 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 + } + GridView { id: applicationsView anchors { From f7a6894587e26d4a28e90ddfda2972d398a5f7dc Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 18 Mar 2015 18:41:26 +0100 Subject: [PATCH 11/86] new notification delegate --- .../contents/ui/NotificationStripe.qml | 151 +++++++----------- applets/notifications/contents/ui/main.qml | 38 +++++ 2 files changed, 93 insertions(+), 96 deletions(-) diff --git a/applets/notifications/contents/ui/NotificationStripe.qml b/applets/notifications/contents/ui/NotificationStripe.qml index c9406ac8..5a6374c9 100644 --- a/applets/notifications/contents/ui/NotificationStripe.qml +++ b/applets/notifications/contents/ui/NotificationStripe.qml @@ -18,6 +18,8 @@ */ import QtQuick 2.0 +import QtQuick.Layouts 1.1 + import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents @@ -25,18 +27,13 @@ MouseArea { id: root - height: units.gridUnit * 2 + height: units.gridUnit * (expanded ? 4 : 2) + background.margins.top + background.margins.bottom width: parent.width anchors.bottomMargin: 10 drag.axis: Drag.XAxis drag.target: root property bool expanded: false - property var textGradient: Gradient { - GradientStop { position: 1.0; color: "#FF00000C" } - GradientStop { position: 0.0; color: "#00000C00" } - } - property color textGradientOverlay: "#9900000C" Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } @@ -46,14 +43,6 @@ MouseArea { SpringAnimation { spring: 5; damping: 0.3 } } - onExpandedChanged: { - if (expanded && body) { - height = units.gridUnit * 4; - } else { - height = units.gridUnit * 2; - } - } - onReleased: { if (drag.active) { if (x > width / 4 || x < width / -4) { @@ -67,91 +56,61 @@ MouseArea { } + PlasmaCore.FrameSvgItem { + id: background + imagePath: "widgets/background" + anchors { + fill: parent + rightMargin: -root.width + leftMargin: units.gridUnit + } + colorGroup: PlasmaCore.ColorScope.colorGroup + } + + PlasmaComponents.ToolButton { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: units.gridUnit / 2 + } + iconSource: "window-close" + flat: false + onClicked: { + notificationsModel.remove(index); + } + } + + PlasmaComponents.Label { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: units.gridUnit * 3 + } + color: PlasmaCore.ColorScope.textColor + text: model.appName + } + + PlasmaComponents.Label { + id: summaryText + anchors { + right: icon.left + verticalCenter: parent.verticalCenter + } + horizontalAlignment: Qt.AlignRight + verticalAlignment: Qt.AlignVCenter + color: PlasmaCore.ColorScope.textColor + text: summary + (root.expanded ? (body ? "\n" + body : '') : + (body ? '...' : '')) + } + PlasmaCore.IconItem { id: icon + anchors { + right: root.right + verticalCenter: parent.verticalCenter + } width: units.iconSizes.medium height: width - visible: !root.expanded - anchors.verticalCenter: parent.verticalCenter - x: units.largeSpacing - y: 0 source: appIcon && appIcon.length > 0 ? appIcon : "im-user" } - - Item { - id: rounded - clip: true - height: parent.height - width: height / 2 - visible: !root.expanded - anchors { - left: icon.right - leftMargin: units.largeSpacing - } - - Rectangle { - id: roundedRect - height: parent.height - width: parent.width * 2 - radius: height //Math.max(height, units.gridUnit) - anchors { - left: parent.left - top: parent.top - } - - gradient: root.textGradient - - Rectangle { - anchors.fill: parent - radius: height / 2 - color: textGradientOverlay - } - } - } - - Rectangle { - id: summaryArea - width: parent.width - icon.width - rounded.width - (units.largeSpacing * 2) - height: parent.height - anchors { - left: root.expanded ? root.left : rounded.right - right: root.right - top: parent.top - } - - gradient: root.textGradient - Rectangle { - anchors.fill: parent - color: textGradientOverlay - } - - PlasmaComponents.Label { - id: summaryText - anchors.fill: parent - clip: true - horizontalAlignment: root.expanded ? Qt.AlignHCenter : Qt.AlignLeft - verticalAlignment: Qt.AlignVCenter - color: PlasmaCore.ColorScope.textColor - text: summary + (root.expanded ? (body ? "\n" + body : '') : - (body ? '...' : '')) - } - - } - - Rectangle { - id: extraArea - width: parent.width - height: parent.width - anchors { - left: summaryArea.right - top: parent.top - bottom: parent.bottom - } - - gradient: root.textGradient - Rectangle { - anchors.fill: parent - color: textGradientOverlay - } - } -} \ No newline at end of file +} diff --git a/applets/notifications/contents/ui/main.qml b/applets/notifications/contents/ui/main.qml index c38ab798..976ccc8b 100644 --- a/applets/notifications/contents/ui/main.qml +++ b/applets/notifications/contents/ui/main.qml @@ -28,6 +28,41 @@ Item { Layout.minimumHeight: notificationView.contentsHeight + function addNotification(source, data, actions) { + // Do not show duplicated notifications + // Remove notifications that are sent again (odd, but true) + for (var i = 0; i < notificationsModel.count; ++i) { + var tmp = notificationsModel.get(i); + var matches = (tmp.appName == data.appName && + tmp.summary == data.summary && + tmp.body == data.body); + var sameSource = tmp.source == source; + + if (sameSource && matches) { + return; + } + + if (sameSource || matches) { + notificationsModel.remove(i) + break; + } + } + + data["id"] = ++notificationId; + data["source"] = source; + if (data["summary"].length < 1) { + data["summary"] = data["body"]; + data["body"] = ''; + } + data["actions"] = actions; + + notificationsModel.insert(0, data); + if (!data["isPersistent"]) { + pendingRemovals.push(notificationId); + pendingTimer.start(); + } + } + PlasmaCore.DataSource { id: notificationsSource @@ -72,14 +107,17 @@ Item { ListElement { appIcon: "call-start" summary: "Missed call from Joe" + appName: "Phone" body: "Called at 8:42 from +41 56 373 37 31" } ListElement { appIcon: "im-google" + appName: "Message" summary: "July: Hey! Are you around?" } ListElement { appIcon: "im-google" + appName: "Message" summary: "July: Hello?" } } From f1ce156ce9dcdbc602daff972a7d010b2d7a41a4 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 19 Mar 2015 11:31:21 +0100 Subject: [PATCH 12/86] notifications actually work --- applets/notifications/contents/ui/main.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applets/notifications/contents/ui/main.qml b/applets/notifications/contents/ui/main.qml index 976ccc8b..692c377e 100644 --- a/applets/notifications/contents/ui/main.qml +++ b/applets/notifications/contents/ui/main.qml @@ -25,6 +25,7 @@ import org.kde.plasma.components 2.0 as PlasmaComponents Item { id: root + property int notificationId: 0 Layout.minimumHeight: notificationView.contentsHeight @@ -93,7 +94,7 @@ Item { } } - homescreen.addNotification( + root.addNotification( sourceName, data, actions); From 34434ff36777ef2cf64a7ec36e63d2280a730680 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 19 Mar 2015 12:11:17 +0100 Subject: [PATCH 13/86] make actions work --- .../contents/ui/NotificationStripe.qml | 37 ++++++++++++++++--- applets/notifications/contents/ui/main.qml | 20 ++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/applets/notifications/contents/ui/NotificationStripe.qml b/applets/notifications/contents/ui/NotificationStripe.qml index 5a6374c9..2023446d 100644 --- a/applets/notifications/contents/ui/NotificationStripe.qml +++ b/applets/notifications/contents/ui/NotificationStripe.qml @@ -24,16 +24,18 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents MouseArea { - id: root + id: notificationItem - height: units.gridUnit * (expanded ? 4 : 2) + background.margins.top + background.margins.bottom + height: units.gridUnit * (expanded ? (actionsLayout.visible ? 6 : 4) : 2) + background.margins.top + background.margins.bottom width: parent.width anchors.bottomMargin: 10 drag.axis: Drag.XAxis - drag.target: root + drag.target: notificationItem property bool expanded: false + property string source: model.source + property var actions: model.actions Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } @@ -61,7 +63,7 @@ MouseArea { imagePath: "widgets/background" anchors { fill: parent - rightMargin: -root.width + rightMargin: -notificationItem.width leftMargin: units.gridUnit } colorGroup: PlasmaCore.ColorScope.colorGroup @@ -95,22 +97,45 @@ MouseArea { anchors { right: icon.left verticalCenter: parent.verticalCenter + rightMargin: units.smallSpacing } horizontalAlignment: Qt.AlignRight verticalAlignment: Qt.AlignVCenter color: PlasmaCore.ColorScope.textColor - text: summary + (root.expanded ? (body ? "\n" + body : '') : + text: summary + (notificationItem.expanded ? (body ? "\n" + body : '') : (body ? '...' : '')) } PlasmaCore.IconItem { id: icon anchors { - right: root.right + right: notificationItem.right verticalCenter: parent.verticalCenter } width: units.iconSizes.medium height: width source: appIcon && appIcon.length > 0 ? appIcon : "im-user" } + RowLayout { + id: actionsLayout + anchors { + right: summaryText.right + top: summaryText.bottom + topMargin: units.smallSpacing + } + opacity: notificationItem.expanded && notificationItem.actions && notificationItem.actions.count > 0 ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: units.shortDuration + easing.type: Easing.InOutQuad + } + } + Repeater { + model: notificationItem.actions + delegate: PlasmaComponents.Button { + text: model.text + onClicked: root.executeAction(notificationItem.source, model.id) + } + } + } } diff --git a/applets/notifications/contents/ui/main.qml b/applets/notifications/contents/ui/main.qml index 692c377e..27199ed5 100644 --- a/applets/notifications/contents/ui/main.qml +++ b/applets/notifications/contents/ui/main.qml @@ -64,6 +64,20 @@ Item { } } + function executeAction(source, id) { + //try to use the service + if (source.indexOf("notification") !== -1) { + var service = notificationsSource.serviceForSource(source) + var op = service.operationDescription("invokeAction") + op["actionId"] = id + + service.startOperationCall(op) + //try to open the id as url + } else if (source.indexOf("Job") !== -1) { + Qt.openUrlExternally(id) + } + } + PlasmaCore.DataSource { id: notificationsSource @@ -106,20 +120,26 @@ Item { id: notificationsModel ListElement { + source: "call1Source" appIcon: "call-start" summary: "Missed call from Joe" appName: "Phone" body: "Called at 8:42 from +41 56 373 37 31" + actions: [] } ListElement { + source: "im1Source" appIcon: "im-google" appName: "Message" summary: "July: Hey! Are you around?" + actions: [] } ListElement { + source: "im2Source" appIcon: "im-google" appName: "Message" summary: "July: Hello?" + actions: [] } } From 59b57b5e7409be3f995855ebe0a5a1cb4846b030 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 19 Mar 2015 16:11:14 +0100 Subject: [PATCH 14/86] a new prototype for a multitasking thing useful for the compositor or the browser --- prototypes/gridmultitasking/main.qml | 198 +++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 prototypes/gridmultitasking/main.qml diff --git a/prototypes/gridmultitasking/main.qml b/prototypes/gridmultitasking/main.qml new file mode 100644 index 00000000..66c4102a --- /dev/null +++ b/prototypes/gridmultitasking/main.qml @@ -0,0 +1,198 @@ + +import QtQuick 2.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.kquickcontrolsaddons 2.0 + +Rectangle { + id: root + width: 300 + height: 500 + property Item currentApp + state: "switcher" + + Text { + anchors.centerIn: parent + text: "Homescreen" + } + + Flickable { + id: mainFlickable + width: root.width * 2 + 5 + height: root.height * 2 + 5 + + scale: 0.5 + contentWidth: width + contentHeight: mainContent.height + Behavior on scale { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + onMovingChanged: { + if (!moving && contentY < root.height * 2) { + root.state = "dragging"; + } + if (contentY < root.height) { + root.state = "homescreen" + } else { + root.state = "switcher" + } + } + + Item { + id: mainContent + width: parent.width + height: flow.y + flow.height + Flow { + id: flow + anchors { + left: parent.left + right: parent.right + } + y: root.height*2 + spacing: 5 + Repeater { + model: 5 + delegate: Rectangle { + id: appRect + color: "red" + width: root.width + height: root.height + MouseArea { + anchors.fill: parent + onClicked: { + root.currentApp = appRect + root.state = "app" + } + Text { + anchors.centerIn: parent + text: "App " + modelData + } + } + } + } + } + } + } + Rectangle { + z: 99 + color: "blue" + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 50 + MouseArea { + anchors.fill: parent + property int oldY + onClicked: { + root.state = "switcher" + } + onPressed: { + if (root.state == "app") { + return; + } + root.state = "dragging"; + oldY = mouse.y; + } + onPositionChanged: { + if (root.state == "app") { + return; + } + mainFlickable.contentY += oldY - mouse.y; + oldY = mouse.y; + } + onReleased: { + if (root.state == "app") { + return; + } + if (mainFlickable.contentY < root.height) { + root.state = "homescreen" + } else { + root.state = "switcher" + } + } + } + } + states: [ + State { + name: "switcher" + PropertyChanges { + target: mainFlickable + scale: 0.5 + x: -root.width / 2 + y: -root.height / 2 + interactive: true + contentY: root.height*2 + visible: true + } + }, + State { + name: "dragging" + PropertyChanges { + target: mainFlickable + scale: 0.5 + x: -root.width / 2 + y: -root.height / 2 + interactive: true + contentY: contentY + visible: true + } + }, + State { + name: "app" + PropertyChanges { + target: mainFlickable + scale: 1 + x: -root.currentApp.x + y: 0 + interactive: false + contentY: root.height*2 + root.currentApp.y + visible: true + } + }, + State { + name: "homescreen" + PropertyChanges { + target: mainFlickable + scale: 0.5 + x: -root.width / 2 + y: -root.height / 2 + interactive: true + contentY: 0 + visible: true + } + } + ] + transitions: [ + Transition { + to: "dragging" + }, + Transition { + SequentialAnimation { + ScriptAction { + script: { + if (root.state != "homescreen") { + mainFlickable.visible = true; + } + } + } + PropertyAnimation { + target: mainFlickable + duration: units.longDuration + easing.type: Easing.InOutQuad + properties: "x,y,scale,contentY" + } + ScriptAction { + script: { + if (root.state == "homescreen") { + mainFlickable.visible = false; + } + } + } + } + } + ] +} \ No newline at end of file From 9acba2675d052ff2f11c538fbe3d1da410b95cdc Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 19 Mar 2015 18:12:51 +0100 Subject: [PATCH 15/86] slide zoom too --- prototypes/gridmultitasking/main.qml | 54 ++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/prototypes/gridmultitasking/main.qml b/prototypes/gridmultitasking/main.qml index 66c4102a..f1dcea47 100644 --- a/prototypes/gridmultitasking/main.qml +++ b/prototypes/gridmultitasking/main.qml @@ -43,7 +43,7 @@ Rectangle { Item { id: mainContent width: parent.width - height: flow.y + flow.height + height: flow.y + flow.height + root.height Flow { id: flow anchors { @@ -87,31 +87,41 @@ Rectangle { MouseArea { anchors.fill: parent property int oldY + property real startY onClicked: { root.state = "switcher" } onPressed: { if (root.state == "app") { - return; - } - root.state = "dragging"; + root.state = "zooming"; + } else { + root.state = "dragging"; + } oldY = mouse.y; + startY = mouse.y; } onPositionChanged: { - if (root.state == "app") { - return; + if (root.state == "app" || root.state == "zooming") { + mainFlickable.scale = (1 - (startY - mouse.y) / root.height); +// mainFlickable.contentY -= oldY - mouse.y; + } else { + mainFlickable.contentY += oldY - mouse.y; } - mainFlickable.contentY += oldY - mouse.y; oldY = mouse.y; } onReleased: { - if (root.state == "app") { - return; - } - if (mainFlickable.contentY < root.height) { - root.state = "homescreen" + if (root.state == "app" || root.state == "zooming") { + if (mainFlickable.scale < 0.7) { + root.state = "switcher" + } else { + root.state = "app" + } } else { - root.state = "switcher" + if (mainFlickable.contentY < root.height) { + root.state = "homescreen" + } else { + root.state = "switcher" + } } } } @@ -125,7 +135,7 @@ Rectangle { x: -root.width / 2 y: -root.height / 2 interactive: true - contentY: root.height*2 + contentY: root.height*2 + (root.currentApp ? root.currentApp.y : 0) visible: true } }, @@ -141,6 +151,18 @@ Rectangle { visible: true } }, + State { + name: "zooming" + PropertyChanges { + target: mainFlickable + scale: scale + x: (-root.currentApp.x * (mainFlickable.scale/0.5 - 1) ) + (2 - mainFlickable.scale/0.5) * (-root.width / 2) + y: (-root.height / 2) * (2 - mainFlickable.scale/0.5) + interactive: true + contentY: (root.height*2 + (root.currentApp ? root.currentApp.y : 0)) * (2 - mainFlickable.scale/0.5) + (root.height*2 + root.currentApp.y) * (mainFlickable.scale/0.5 - 1) + visible: true + } + }, State { name: "app" PropertyChanges { @@ -170,6 +192,9 @@ Rectangle { Transition { to: "dragging" }, + Transition { + to: "zooming" + }, Transition { SequentialAnimation { ScriptAction { @@ -189,6 +214,7 @@ Rectangle { script: { if (root.state == "homescreen") { mainFlickable.visible = false; + root.currentApp = null; } } } From 21a93553675e9a26f7627c9e95e40d871872af17 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 19 Mar 2015 18:13:52 +0100 Subject: [PATCH 16/86] dead code -- --- prototypes/gridmultitasking/main.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/prototypes/gridmultitasking/main.qml b/prototypes/gridmultitasking/main.qml index f1dcea47..d9f6e5f2 100644 --- a/prototypes/gridmultitasking/main.qml +++ b/prototypes/gridmultitasking/main.qml @@ -103,7 +103,6 @@ Rectangle { onPositionChanged: { if (root.state == "app" || root.state == "zooming") { mainFlickable.scale = (1 - (startY - mouse.y) / root.height); -// mainFlickable.contentY -= oldY - mouse.y; } else { mainFlickable.contentY += oldY - mouse.y; } From b720bbcb5c8255b80494743f0ec76b32c373ce69 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 20 Mar 2015 17:24:10 +0100 Subject: [PATCH 17/86] finish the behavior --- prototypes/gridmultitasking/main.qml | 38 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/prototypes/gridmultitasking/main.qml b/prototypes/gridmultitasking/main.qml index d9f6e5f2..27873997 100644 --- a/prototypes/gridmultitasking/main.qml +++ b/prototypes/gridmultitasking/main.qml @@ -1,7 +1,7 @@ import QtQuick 2.1 import org.kde.plasma.core 2.0 as PlasmaCore -import org.kde.kquickcontrolsaddons 2.0 +import org.kde.plasma.components 2.0 as PlasmaComponents Rectangle { id: root @@ -15,8 +15,16 @@ Rectangle { text: "Homescreen" } + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.6 * Math.min(1, mainFlickable.contentY/(root.height*2)) + } + Flickable { id: mainFlickable + //Scale adjusted in the 0-1 range + property real zoomFactor: Math.max(mainFlickable.scale/0.5, 1) - 1 width: root.width * 2 + 5 height: root.height * 2 + 5 @@ -62,6 +70,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { + root.state = "scrolling" root.currentApp = appRect root.state = "app" } @@ -76,6 +85,7 @@ Rectangle { } } Rectangle { + id: bottomBar z: 99 color: "blue" anchors { @@ -123,6 +133,21 @@ Rectangle { } } } + + Row { + PlasmaComponents.ToolButton { + height: bottomBar.height + width: height + iconSource: "applications-other" + onClicked: root.state = "switcher" + } + PlasmaComponents.ToolButton { + height: bottomBar.height + width: height + iconSource: "go-home" + onClicked: root.state = "homescreen" + } + } } } states: [ @@ -155,10 +180,10 @@ Rectangle { PropertyChanges { target: mainFlickable scale: scale - x: (-root.currentApp.x * (mainFlickable.scale/0.5 - 1) ) + (2 - mainFlickable.scale/0.5) * (-root.width / 2) - y: (-root.height / 2) * (2 - mainFlickable.scale/0.5) + x: (-root.currentApp.x * mainFlickable.zoomFactor ) + (1 - mainFlickable.zoomFactor) * (-root.width / 2) + y: (-root.height / 2) * (1 - mainFlickable.zoomFactor) interactive: true - contentY: (root.height*2 + (root.currentApp ? root.currentApp.y : 0)) * (2 - mainFlickable.scale/0.5) + (root.height*2 + root.currentApp.y) * (mainFlickable.scale/0.5 - 1) + contentY: (root.height*2 + (root.currentApp ? root.currentApp.y : 0)) * (1 - mainFlickable.zoomFactor) + (root.height*2 + root.currentApp.y) * mainFlickable.zoomFactor visible: true } }, @@ -190,6 +215,11 @@ Rectangle { transitions: [ Transition { to: "dragging" + ScriptAction { + script: { + root.currentApp = null; + } + } }, Transition { to: "zooming" From 013d99a4939e4861464b51b287e4741a447a4320 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 27 Mar 2015 17:59:32 +0100 Subject: [PATCH 18/86] port the existing dialer in a standalone app is qml-only at the moment, but may need to be a c++ half-daemon in order to be always running and show the "ringning" ui at the right moment --- CMakeLists.txt | 3 + dialer/contents/ui/Desktop.qml | 563 ++++++++++++++++++++++++ dialer/contents/ui/Dialer.qml | 251 +++++++++++ dialer/contents/ui/DialerButton.qml | 48 ++ dialer/contents/ui/DialerIconButton.qml | 37 ++ dialer/contents/ui/main.qml | 114 +++++ dialer/metadata.desktop | 20 + 7 files changed, 1036 insertions(+) create mode 100644 dialer/contents/ui/Desktop.qml create mode 100644 dialer/contents/ui/Dialer.qml create mode 100644 dialer/contents/ui/DialerButton.qml create mode 100644 dialer/contents/ui/DialerIconButton.qml create mode 100644 dialer/contents/ui/main.qml create mode 100644 dialer/metadata.desktop diff --git a/CMakeLists.txt b/CMakeLists.txt index d8edfb84..fb403166 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,9 @@ plasma_install_package(look-and-feel org.kde.satellite.phone look-and-feel) plasma_install_package(shell org.kde.satellite.phone shells) install(DIRECTORY wallpaper/ DESTINATION "${WALLPAPER_INSTALL_DIR}/org.kde.satellite.lockers") +kpackage_install_package(dialer org.kde.phone.dialer genericqml) +install(FILES dialer/metadata.desktop DESTINATION "${XDG_APPS_INSTALL_DIR}/org.kde.phone.dialer.desktop") + install(DIRECTORY compositor/ DESTINATION ${DATA_INSTALL_DIR}/greenisland/org.kde.satellite.compositor.phone PATTERN .svn EXCLUDE diff --git a/dialer/contents/ui/Desktop.qml b/dialer/contents/ui/Desktop.qml new file mode 100644 index 00000000..b6e914e4 --- /dev/null +++ b/dialer/contents/ui/Desktop.qml @@ -0,0 +1,563 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2012 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtGraphicalEffects 1.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.shell 2.0 as Shell +import org.kde.satellite.components 0.1 as SatelliteComponents +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.workspace.components 2.0 as PlasmaWorkspace +import org.nemomobile.voicecall 1.0 +import org.kde.kquickcontrolsaddons 2.0 +import MeeGo.QOfono 0.2 +import "../components" + +Item { + id: homescreen + width: 1080 + height: 1920 + + property Item containment; + property Item wallpaper; + property var pendingRemovals: []; + property int notificationId: 0; + property int buttonHeight: width/4 + + /* + Notificadtion data object has the following properties: + appIcon + image + appName + summary + body + isPersistent + expireTimeout + urgency + appRealName + configurable + */ + function addNotification(source, data, actions) { + // Do not show duplicated notifications + // Remove notifications that are sent again (odd, but true) + for (var i = 0; i < notificationsModel.count; ++i) { + var tmp = notificationsModel.get(i); + var matches = (tmp.appName == data.appName && + tmp.summary == data.summary && + tmp.body == data.body); + var sameSource = tmp.source == source; + + if (sameSource && matches) { + return; + } + + if (sameSource || matches) { + notificationsModel.remove(i) + break; + } + } + + data["id"] = ++notificationId; + data["source"] = source; + if (data["summary"].length < 1) { + data["summary"] = data["body"]; + data["body"] = ''; + } + data["actions"] = actions; + + notificationsModel.insert(0, data); + if (!data["isPersistent"]) { + pendingRemovals.push(notificationId); + pendingTimer.start(); + } + } + + OfonoManager { + id: ofonoManager + onAvailableChanged: { + console.log("Ofono is " + available) + } + onModemAdded: { + console.log("modem added " + modem) + } + onModemRemoved: console.log("modem removed") + } + + OfonoConnMan { + id: ofono1 + Component.onCompleted: { + console.log(ofonoManager.modems) + } + modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" + } + + OfonoModem { + id: modem1 + modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" + + } + + OfonoContextConnection { + id: context1 + contextPath : ofono1.contexts.length > 0 ? ofono1.contexts[0] : "" + Component.onCompleted: { + print("Context Active: " + context1.active) + } + onActiveChanged: { + print("Context Active: " + context1.active) + } + } + + property OfonoSimManager simManager: ofonoSimManager + OfonoSimManager { + id: ofonoSimManager + modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" + } + + OfonoNetworkRegistration { + id: netreg + Component.onCompleted: { + netreg.scan() + updateStrengthIcon() + } + + onNetworkOperatorsChanged : { + console.log("operators :"+netreg.currentOperator["Name"].toString()) + } + modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" + function updateStrengthIcon() { + if (netreg.strength >= 100) { + strengthIcon.source = "network-mobile-100"; + } else if (netreg.strength >= 80) { + strengthIcon.source = "network-mobile-80"; + } else if (netreg.strength >= 60) { + strengthIcon.source = "network-mobile-60"; + } else if (netreg.strength >= 40) { + strengthIcon.source = "network-mobile-40"; + } else if (netreg.strength >= 20) { + strengthIcon.source = "network-mobile-20"; + } else { + strengthIcon.source = "network-mobile-0"; + } + } + + onStrengthChanged: { + console.log("Strength changed to " + netreg.strength) + updateStrengthIcon() + } + } + + OfonoNetworkOperator { + id: netop + } + + property VoiceCallManager manager: VoiceCallManager { + id: manager + + onActiveVoiceCallChanged: { + if (activeVoiceCall) { + dialerOverlay.open(); + //main.activeVoiceCallPerson = people.personByPhoneNumber(activeVoiceCall.lineId); + dialerOverlay.item.numberEntryText = activeVoiceCall.lineId; + + } else { + dialerOverlay.close(); + dialerOverlay.item.numberEntryText = ''; + + //main.activeVoiceCallPerson = null; + } + } + + onError: { + console.log('*** QML *** VCM ERROR: ' + message); + } + } + + Timer { + id: pendingTimer + interval: 5000 + repeat: false + onTriggered: { + for (var i = 0; i < pendingRemovals.length; ++i) { + var id = pendingRemovals[i]; + for (var j = 0; j < notificationsModel.count; ++j) { + if (notificationsModel.get(j).id == id) { + notificationsModel.remove(j); + } + } + } + pendingRemovals = []; + } + } + + Rectangle { + z: 1 + color: Qt.rgba(0, 0, 0, 0.9 * (Math.min(applications.contentY + homescreen.height, homescreen.height) / homescreen.height)) + anchors.fill: parent + } + + PlasmaCore.DataSource { + id: timeSource + engine: "time" + connectedSources: ["Local"] + interval: 60 * 1000 + } + PlasmaCore.DataSource { + id: notificationsSource + + engine: "notifications" + interval: 0 + + onSourceAdded: { + connectSource(source); + } + + onSourceRemoved: { + for (var i = 0; i < notificationsModel.count; ++i) { + if (notificationsModel.get(i) == source) { + notificationsModel.remove(i); + break; + } + } + } + + onNewData: { + var actions = new Array() + if (data["actions"] && data["actions"].length % 2 == 0) { + for (var i = 0; i < data["actions"].length; i += 2) { + var action = new Object(); + action["id"] = data["actions"][i]; + action["text"] = data["actions"][i+1]; + actions.push(action); + } + } + + homescreen.addNotification( + sourceName, + data, + actions); + } + + } + + ListModel { + id: notificationsModel + + ListElement { + appIcon: "call-start" + summary: "Missed call from Joe" + body: "Called at 8:42 from +41 56 373 37 31" + } + ListElement { + appIcon: "im-google" + summary: "July: Hey! Are you around?" + } + ListElement { + appIcon: "im-google" + summary: "July: Hello?" + } + } + + Loader { + id: dialerOverlay + function open() { + source = Qt.resolvedUrl("Dialer.qml") + dialerOverlay.item.open(); + } + function close() { + dialerOverlay.item.close(); + } + anchors { + left: parent.left + top: statusPanel.bottom + right: parent.right + bottom: parent.bottom + } + z: 20 + } + Loader { + id: pinOverlay + anchors { + left: parent.left + top: statusPanel.bottom + right: parent.right + bottom: parent.bottom + } + z: 21 + source: simManager.pinRequired != OfonoSimManager.NoPin ? Qt.resolvedUrl("Pin.qml") : "" + } + + PlasmaCore.ColorScope { + id: statusPanel + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: units.iconSizes.small + z: 2 + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + + Rectangle { + anchors.fill: parent + color: Qt.rgba(0, 0, 0, 0.9) + + PlasmaCore.IconItem { + id: strengthIcon + colorGroup: PlasmaCore.ColorScope.colorGroup + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + width: units.iconSizes.small + height: width + } + PlasmaComponents.Label { + anchors { + left: strengthIcon.right + verticalCenter: parent.verticalCenter + } + text: netreg.strength + "% " + netreg.name + color: PlasmaCore.ColorScope.textColor + font.pixelSize: parent.height / 2 + } + PlasmaComponents.Label { + id: clock + anchors.fill: parent + text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm") + color: PlasmaCore.ColorScope.textColor + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pixelSize: height / 2 + } + MouseArea { + property int oldMouseY: 0 + + anchors.fill: parent + enabled: !dialerOverlay.item.visible + onPressed: { + oldMouseY = mouse.y; + slidingPanel.visible = true; + } + onPositionChanged: { + slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY); + oldMouseY = mouse.y; + } + onReleased: slidingPanel.updateState(); + } + + + PlasmaWorkspace.BatteryIcon { + id: batteryIcon + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + } + width: units.iconSizes.small + height: width + hasBattery: pmSource.data["Battery"]["Has Battery"] + batteryType: "Phone" + percent: pmSource.data["Battery0"] ? pmSource.data["Battery0"]["Percent"] : 0 + + PlasmaCore.DataSource { + id: pmSource + engine: "powermanagement" + connectedSources: sources + onSourceAdded: { + disconnectSource(source); + connectSource(source); + } + onSourceRemoved: { + disconnectSource(source); + } + } + } + } + } + + SlidingPanel { + id: slidingPanel + width: homescreen.width + height: homescreen.height + } + + + PlasmaCore.ColorScope { + z: 1 + anchors { + fill: parent + } + + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + + SatelliteComponents.ApplicationListModel { + id: appListModel + } + + GridView { + id: applications + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: parent.right + } + z: 1 + cellWidth: homescreen.buttonHeight + cellHeight: cellWidth + model: appListModel + snapMode: GridView.SnapToRow + clip: true + header: MouseArea { + z: 999 + width: homescreen.width + height: homescreen.height - units.iconSizes.medium + + onPressAndHold: { + containment.action("configure").trigger(); + } + + PlasmaComponents.Label { + id: bigClock + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + bottom: notificationView.top + } + text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm") + color: PlasmaCore.ColorScope.textColor + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pointSize: 40 + style: Text.Raised + styleColor: "black" + } + + ListView { + id: notificationView + spacing: units.smallSpacing + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + bottomMargin: stripe.height * 2 + } + height: parent.height / 3 + interactive: false + + z: 1 + verticalLayoutDirection: ListView.BottomToTop + model: notificationsModel + + add: Transition { + NumberAnimation { + properties: "x" + from: notificationView.width + duration: 100 + } + } + + remove: Transition { + NumberAnimation { + properties: "x" + to: notificationView.width + duration: 500 + } + NumberAnimation { + properties: "opacity" + to: 0 + duration: 500 + } + } + + removeDisplaced: Transition { + SequentialAnimation { + PauseAnimation { duration: 600 } + NumberAnimation { properties: "x,y"; duration: 100 } + } + } + + delegate: NotificationStripe {} + + } + SatelliteStripe { + id: stripe + z: 99 + y: Math.max(applications.contentY + parent.height, parent.height - height) + + PlasmaCore.Svg { + id: stripeIcons + imagePath: Qt.resolvedUrl("../images/homescreenicons.svg") + } + + Row { + anchors.fill: parent + property int columns: 4 + property alias buttonHeight: stripe.height + + HomeLauncherSvg { + id: phoneIcon + svg: stripeIcons + elementId: "phone" + callback: function() { + dialerOverlay.open() + } + } + + HomeLauncherSvg { + id: messagingIcon + svg: stripeIcons + elementId: "messaging" + callback: function() { console.log("Start messaging") } + } + + + HomeLauncherSvg { + id: emailIcon + svg: stripeIcons + elementId: "email" + callback: function() { console.log("Start email") } + } + + + HomeLauncherSvg { + id: webIcon + svg: stripeIcons + elementId: "web" + callback: function() { console.log("Start web") } + } + } + } + } + delegate: HomeLauncher {} + Component.onCompleted : { console.log("WTF " + width) } + + + } + } + + Component.onCompleted: { + //configure the view behavior + if (desktop) { + desktop.width = width; + desktop.height = height; + } + } +} diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer.qml new file mode 100644 index 00000000..30287990 --- /dev/null +++ b/dialer/contents/ui/Dialer.qml @@ -0,0 +1,251 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.nemomobile.voicecall 1.0 + +Item { + id: dialer + + state: manager.activeVoiceCall ? manager.activeVoiceCall.statusText : "disconnected" + property color textColor: "white" + property bool calling: false // needs to be connected to a system service + property bool enableButtons: calling + property alias numberEntryText: status.text + property VoiceCallManager manager: root.manager + + property string providerId: manager.providers.id(0) + + function addNumber(number) { + status.text = status.text + number + } + + function call() { + if (!calling) { + console.log("Calling: " + status.text); + dialer.calling = true; + manager.dial(providerId, status.text); + + } else { + console.log("Hanging up: " + status.text); + status.text = ''; + dialer.calling = false; + var call = manager.activeVoiceCall; + if (call) { + call.hangup(); + } + } + } + + function fromContacts() { + console.log("Should get from contacts!"); + status.text = "+41 76 555 5555" + } + + function secondsToTimeString(seconds) { + seconds = Math.floor(seconds/1000) + var h = Math.floor(seconds / 3600); + var m = Math.floor((seconds - (h * 3600)) / 60); + var s = seconds - h * 3600 - m * 60; + if(h < 10) h = '0' + h; + if(m < 10) m = '0' + m; + if(s < 10) s = '0' + s; + return '' + h + ':' + m + ':' + s; + } + + Behavior on opacity { + NumberAnimation { properties: "opacity"; duration: 100 } + } + + MouseArea { + anchors.fill: parent + } + + ColumnLayout { + id: dialPadArea + visible: dialer.state == "disconnected" + + anchors { + fill: parent + margins: 20 + } + Text { + id: status + Layout.fillWidth: true + horizontalAlignment: Qt.AlignRight + verticalAlignment: Qt.AlignVCenter + font.pixelSize: one.font.pixelSize + color: textColor + } + + Grid { + id: pad + columns: 3 + spacing: 0 + property int buttonHeight: height / 5 + + Layout.fillWidth: true + Layout.fillHeight: true + + height: parent.height - status.height + width: parent.width + + DialerButton { id: one; text: "1" } + DialerButton { text: "2" } + DialerButton { text: "3" } + + DialerButton { text: "4" } + DialerButton { text: "5" } + DialerButton { text: "6" } + + DialerButton { text: "7" } + DialerButton { text: "8" } + DialerButton { text: "9" } + + DialerButton { text: "*"; } + DialerButton { text: "0"; sub: "+"; } + DialerButton { text: "#" } + + DialerIconButton { + source: "im-user" + callback: fromContacts + } + DialerIconButton { + id: callButton + source: dialer.calling ? "call-stop" : "call-start" + callback: call + } + DialerIconButton { + source: "edit-clear" + callback: function() { + if (status.text.length > 0) { + status.text = status.text.substr(0, status.text.length - 1); + } else { + dialer.calling = true; + dialer.calling = false; + } + } + } + } + } + + ColumnLayout { + id: activeCallUi + spacing: 10 + visible: dialer.state != "disconnected" + + anchors { + fill: parent + margins: 20 + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: parent.height/2 + Rectangle { + height: Math.min(parent.width, parent.height) + width: height + radius: 5 + anchors.centerIn: parent + PlasmaCore.IconItem { + anchors { + fill: parent + centerIn: parent + margins: 20 + } + source: "im-user" + } + } + } + Text { + Layout.fillWidth: true + Layout.minimumHeight: implicitHeight + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pixelSize: one.font.pixelSize + color: textColor + text: manager.activeVoiceCall ? manager.activeVoiceCall.lineId : "" + } + Text { + Layout.fillWidth: true + Layout.minimumHeight: implicitHeight + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pixelSize: theme.smallestFont.pixelSize + color: textColor + text: manager.activeVoiceCall ? secondsToTimeString(manager.activeVoiceCall.duration) : '' + } + RowLayout { + height: parent.height / 3 + + Layout.fillWidth: true + Layout.fillHeight: true + + DialerIconButton { + Layout.fillWidth: true + Layout.fillHeight: true + source: dialer.state == "incoming" ? "call-start" : (manager.isMicrophoneMuted ? "audio-volume-muted" : "audio-volume-high") + Rectangle { + z: -1 + color: dialer.state == "incoming" ? "green" : "white" + opacity: 0.5 + radius: 5 + anchors { + fill: parent + } + } + + callback: function () { + if (dialer.state == "incoming") { + if (manager.activeVoiceCall) { + manager.activeVoiceCall.answer(); + } + } else { + manager.isMicrophoneMuted = !manager.isMicrophoneMuted; + } + } + } + + DialerIconButton { + Layout.fillWidth: true + Layout.fillHeight: true + source: "call-stop" + Rectangle { + z: -1 + color: "red" + opacity: 0.5 + radius: 5 + anchors { + fill: parent + } + } + + callback: function () { + if (manager.activeVoiceCall) { + manager.activeVoiceCall.hangup(); + } + } + } + } + } +} diff --git a/dialer/contents/ui/DialerButton.qml b/dialer/contents/ui/DialerButton.qml new file mode 100644 index 00000000..784cb55f --- /dev/null +++ b/dialer/contents/ui/DialerButton.qml @@ -0,0 +1,48 @@ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore + +Text { + width: parent.width / parent.columns + height: parent.buttonHeight + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + color: dialer.textColor + font.pixelSize: Math.floor((width - (units.largeSpacing)) / 2) + property alias sub: longHold.text + property var callback + + MouseArea { + anchors.fill: parent + onClicked: { + if (callback) { + callback(); + } else { + addNumber(parent.text); + } + } + + onPressAndHold: { + if (longHold.visible) { + addNumber(longHold.text); + } else { + addNumber(parent.text); + } + } + } + + Text { + id: longHold + anchors { + top: parent.top + right: parent.right + } + height: parent.height + width: parent.width / 3 + verticalAlignment: Qt.AlignVCenter + visible: text.length > 0 + opacity: 0.7 + + font.pixelSize: parent.pixelSize * .8 + color: parent.color + } +} diff --git a/dialer/contents/ui/DialerIconButton.qml b/dialer/contents/ui/DialerIconButton.qml new file mode 100644 index 00000000..254fb0e0 --- /dev/null +++ b/dialer/contents/ui/DialerIconButton.qml @@ -0,0 +1,37 @@ +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore + +Item { + width: parent.width / parent.columns + height: parent.buttonHeight + property var callback + property string text + property string sub + property alias source: icon.source + + PlasmaCore.IconItem { + id: icon + width: units.iconSizes.medium + height: width + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (callback) { + callback(); + } else { + addNumber(parent.text); + } + } + + onPressAndHold: { + if (parent.sub.length > 0) { + addNumber(parent.sub); + } else { + addNumber(parent.text); + } + } + } +} diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml new file mode 100644 index 00000000..c6007c0f --- /dev/null +++ b/dialer/contents/ui/main.qml @@ -0,0 +1,114 @@ +/* + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.1 +import org.nemomobile.voicecall 1.0 +import MeeGo.QOfono 0.2 + +ApplicationWindow { + id: root + width: 600 + height: 800 + visible: true + color: Qt.rgba(0, 0, 0, 0.9) + + OfonoManager { + id: ofonoManager + onAvailableChanged: { + console.log("Ofono is " + available) + } + onModemAdded: { + console.log("modem added " + modem) + } + onModemRemoved: console.log("modem removed") + } + + OfonoConnMan { + id: ofono1 + Component.onCompleted: { + console.log(ofonoManager.modems) + } + modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" + } + + OfonoModem { + id: modem1 + modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" + + } + + OfonoContextConnection { + id: context1 + contextPath : ofono1.contexts.length > 0 ? ofono1.contexts[0] : "" + Component.onCompleted: { + print("Context Active: " + context1.active) + } + onActiveChanged: { + print("Context Active: " + context1.active) + } + } + + property OfonoSimManager simManager: ofonoSimManager + OfonoSimManager { + id: ofonoSimManager + modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" + } + + OfonoNetworkRegistration { + id: netreg + Component.onCompleted: { + netreg.scan() + } + + onNetworkOperatorsChanged : { + console.log("operators :"+netreg.currentOperator["Name"].toString()) + } + modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" + } + + OfonoNetworkOperator { + id: netop + } + + property VoiceCallManager manager: VoiceCallManager { + id: manager + + onActiveVoiceCallChanged: { + if (activeVoiceCall) { + //main.activeVoiceCallPerson = people.personByPhoneNumber(activeVoiceCall.lineId); + dialerOverlay.item.numberEntryText = activeVoiceCall.lineId; + + } else { + dialerOverlay.item.numberEntryText = ''; + + //main.activeVoiceCallPerson = null; + } + } + + onError: { + console.log('*** QML *** VCM ERROR: ' + message); + } + } + Dialer { + id: dialerOverlay + anchors.fill: parent + } +} diff --git a/dialer/metadata.desktop b/dialer/metadata.desktop new file mode 100644 index 00000000..cf835d33 --- /dev/null +++ b/dialer/metadata.desktop @@ -0,0 +1,20 @@ +[Desktop Entry] +Comment=Plasma phone dialer +Encoding=UTF-8 +Keywords= +Name=Phone +Type=Application +Icon=call-start +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Marco Martin +X-KDE-PluginInfo-Category=Communications +X-KDE-PluginInfo-Email=mart@kde.org +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=org.kde.phone.dialer +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website= +X-KDE-ServiceTypes=KPackage/Generic +Exec=kpackagelauncherqml -a org.kde.phone.dialer + +X-Plasma-MainScript=ui/main.qml +X-Plasma-RemoteLocation= From 173e295eba07af53b1b476526c79ee99e78da5b5 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 27 Mar 2015 18:54:09 +0100 Subject: [PATCH 19/86] reset scroll position when window deactivated --- containments/homescreen/contents/ui/main.qml | 6 + dialer/contents/ui/Desktop.qml | 563 ------------------- shell/contents/views/Desktop.qml | 8 + 3 files changed, 14 insertions(+), 563 deletions(-) delete mode 100644 dialer/contents/ui/Desktop.qml diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index c5a95e0c..55148195 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -42,6 +42,12 @@ Item { LayoutManager.save(); } + Plasmoid.onFocusChanged: { + if (!plasmoid.focus && applicationsView.contentY > -(applicationsView.headerItem.height - root.height/2)) { + applicationsView.contentY = -root.height; + } + } + function addApplet(applet, x, y) { var container = appletContainerComponent.createObject(appletsSpace.layout) container.visible = true diff --git a/dialer/contents/ui/Desktop.qml b/dialer/contents/ui/Desktop.qml deleted file mode 100644 index b6e914e4..00000000 --- a/dialer/contents/ui/Desktop.qml +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright 2014 Aaron Seigo - * Copyright 2012 Marco Martin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.0 -import QtGraphicalEffects 1.0 -import org.kde.plasma.core 2.0 as PlasmaCore -import org.kde.plasma.shell 2.0 as Shell -import org.kde.satellite.components 0.1 as SatelliteComponents -import org.kde.plasma.components 2.0 as PlasmaComponents -import org.kde.plasma.workspace.components 2.0 as PlasmaWorkspace -import org.nemomobile.voicecall 1.0 -import org.kde.kquickcontrolsaddons 2.0 -import MeeGo.QOfono 0.2 -import "../components" - -Item { - id: homescreen - width: 1080 - height: 1920 - - property Item containment; - property Item wallpaper; - property var pendingRemovals: []; - property int notificationId: 0; - property int buttonHeight: width/4 - - /* - Notificadtion data object has the following properties: - appIcon - image - appName - summary - body - isPersistent - expireTimeout - urgency - appRealName - configurable - */ - function addNotification(source, data, actions) { - // Do not show duplicated notifications - // Remove notifications that are sent again (odd, but true) - for (var i = 0; i < notificationsModel.count; ++i) { - var tmp = notificationsModel.get(i); - var matches = (tmp.appName == data.appName && - tmp.summary == data.summary && - tmp.body == data.body); - var sameSource = tmp.source == source; - - if (sameSource && matches) { - return; - } - - if (sameSource || matches) { - notificationsModel.remove(i) - break; - } - } - - data["id"] = ++notificationId; - data["source"] = source; - if (data["summary"].length < 1) { - data["summary"] = data["body"]; - data["body"] = ''; - } - data["actions"] = actions; - - notificationsModel.insert(0, data); - if (!data["isPersistent"]) { - pendingRemovals.push(notificationId); - pendingTimer.start(); - } - } - - OfonoManager { - id: ofonoManager - onAvailableChanged: { - console.log("Ofono is " + available) - } - onModemAdded: { - console.log("modem added " + modem) - } - onModemRemoved: console.log("modem removed") - } - - OfonoConnMan { - id: ofono1 - Component.onCompleted: { - console.log(ofonoManager.modems) - } - modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" - } - - OfonoModem { - id: modem1 - modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" - - } - - OfonoContextConnection { - id: context1 - contextPath : ofono1.contexts.length > 0 ? ofono1.contexts[0] : "" - Component.onCompleted: { - print("Context Active: " + context1.active) - } - onActiveChanged: { - print("Context Active: " + context1.active) - } - } - - property OfonoSimManager simManager: ofonoSimManager - OfonoSimManager { - id: ofonoSimManager - modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" - } - - OfonoNetworkRegistration { - id: netreg - Component.onCompleted: { - netreg.scan() - updateStrengthIcon() - } - - onNetworkOperatorsChanged : { - console.log("operators :"+netreg.currentOperator["Name"].toString()) - } - modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : "" - function updateStrengthIcon() { - if (netreg.strength >= 100) { - strengthIcon.source = "network-mobile-100"; - } else if (netreg.strength >= 80) { - strengthIcon.source = "network-mobile-80"; - } else if (netreg.strength >= 60) { - strengthIcon.source = "network-mobile-60"; - } else if (netreg.strength >= 40) { - strengthIcon.source = "network-mobile-40"; - } else if (netreg.strength >= 20) { - strengthIcon.source = "network-mobile-20"; - } else { - strengthIcon.source = "network-mobile-0"; - } - } - - onStrengthChanged: { - console.log("Strength changed to " + netreg.strength) - updateStrengthIcon() - } - } - - OfonoNetworkOperator { - id: netop - } - - property VoiceCallManager manager: VoiceCallManager { - id: manager - - onActiveVoiceCallChanged: { - if (activeVoiceCall) { - dialerOverlay.open(); - //main.activeVoiceCallPerson = people.personByPhoneNumber(activeVoiceCall.lineId); - dialerOverlay.item.numberEntryText = activeVoiceCall.lineId; - - } else { - dialerOverlay.close(); - dialerOverlay.item.numberEntryText = ''; - - //main.activeVoiceCallPerson = null; - } - } - - onError: { - console.log('*** QML *** VCM ERROR: ' + message); - } - } - - Timer { - id: pendingTimer - interval: 5000 - repeat: false - onTriggered: { - for (var i = 0; i < pendingRemovals.length; ++i) { - var id = pendingRemovals[i]; - for (var j = 0; j < notificationsModel.count; ++j) { - if (notificationsModel.get(j).id == id) { - notificationsModel.remove(j); - } - } - } - pendingRemovals = []; - } - } - - Rectangle { - z: 1 - color: Qt.rgba(0, 0, 0, 0.9 * (Math.min(applications.contentY + homescreen.height, homescreen.height) / homescreen.height)) - anchors.fill: parent - } - - PlasmaCore.DataSource { - id: timeSource - engine: "time" - connectedSources: ["Local"] - interval: 60 * 1000 - } - PlasmaCore.DataSource { - id: notificationsSource - - engine: "notifications" - interval: 0 - - onSourceAdded: { - connectSource(source); - } - - onSourceRemoved: { - for (var i = 0; i < notificationsModel.count; ++i) { - if (notificationsModel.get(i) == source) { - notificationsModel.remove(i); - break; - } - } - } - - onNewData: { - var actions = new Array() - if (data["actions"] && data["actions"].length % 2 == 0) { - for (var i = 0; i < data["actions"].length; i += 2) { - var action = new Object(); - action["id"] = data["actions"][i]; - action["text"] = data["actions"][i+1]; - actions.push(action); - } - } - - homescreen.addNotification( - sourceName, - data, - actions); - } - - } - - ListModel { - id: notificationsModel - - ListElement { - appIcon: "call-start" - summary: "Missed call from Joe" - body: "Called at 8:42 from +41 56 373 37 31" - } - ListElement { - appIcon: "im-google" - summary: "July: Hey! Are you around?" - } - ListElement { - appIcon: "im-google" - summary: "July: Hello?" - } - } - - Loader { - id: dialerOverlay - function open() { - source = Qt.resolvedUrl("Dialer.qml") - dialerOverlay.item.open(); - } - function close() { - dialerOverlay.item.close(); - } - anchors { - left: parent.left - top: statusPanel.bottom - right: parent.right - bottom: parent.bottom - } - z: 20 - } - Loader { - id: pinOverlay - anchors { - left: parent.left - top: statusPanel.bottom - right: parent.right - bottom: parent.bottom - } - z: 21 - source: simManager.pinRequired != OfonoSimManager.NoPin ? Qt.resolvedUrl("Pin.qml") : "" - } - - PlasmaCore.ColorScope { - id: statusPanel - anchors { - top: parent.top - left: parent.left - right: parent.right - } - height: units.iconSizes.small - z: 2 - colorGroup: PlasmaCore.Theme.ComplementaryColorGroup - - Rectangle { - anchors.fill: parent - color: Qt.rgba(0, 0, 0, 0.9) - - PlasmaCore.IconItem { - id: strengthIcon - colorGroup: PlasmaCore.ColorScope.colorGroup - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - width: units.iconSizes.small - height: width - } - PlasmaComponents.Label { - anchors { - left: strengthIcon.right - verticalCenter: parent.verticalCenter - } - text: netreg.strength + "% " + netreg.name - color: PlasmaCore.ColorScope.textColor - font.pixelSize: parent.height / 2 - } - PlasmaComponents.Label { - id: clock - anchors.fill: parent - text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm") - color: PlasmaCore.ColorScope.textColor - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - font.pixelSize: height / 2 - } - MouseArea { - property int oldMouseY: 0 - - anchors.fill: parent - enabled: !dialerOverlay.item.visible - onPressed: { - oldMouseY = mouse.y; - slidingPanel.visible = true; - } - onPositionChanged: { - slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY); - oldMouseY = mouse.y; - } - onReleased: slidingPanel.updateState(); - } - - - PlasmaWorkspace.BatteryIcon { - id: batteryIcon - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - } - width: units.iconSizes.small - height: width - hasBattery: pmSource.data["Battery"]["Has Battery"] - batteryType: "Phone" - percent: pmSource.data["Battery0"] ? pmSource.data["Battery0"]["Percent"] : 0 - - PlasmaCore.DataSource { - id: pmSource - engine: "powermanagement" - connectedSources: sources - onSourceAdded: { - disconnectSource(source); - connectSource(source); - } - onSourceRemoved: { - disconnectSource(source); - } - } - } - } - } - - SlidingPanel { - id: slidingPanel - width: homescreen.width - height: homescreen.height - } - - - PlasmaCore.ColorScope { - z: 1 - anchors { - fill: parent - } - - colorGroup: PlasmaCore.Theme.ComplementaryColorGroup - - SatelliteComponents.ApplicationListModel { - id: appListModel - } - - GridView { - id: applications - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - right: parent.right - } - z: 1 - cellWidth: homescreen.buttonHeight - cellHeight: cellWidth - model: appListModel - snapMode: GridView.SnapToRow - clip: true - header: MouseArea { - z: 999 - width: homescreen.width - height: homescreen.height - units.iconSizes.medium - - onPressAndHold: { - containment.action("configure").trigger(); - } - - PlasmaComponents.Label { - id: bigClock - anchors { - horizontalCenter: parent.horizontalCenter - top: parent.top - bottom: notificationView.top - } - text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm") - color: PlasmaCore.ColorScope.textColor - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - font.pointSize: 40 - style: Text.Raised - styleColor: "black" - } - - ListView { - id: notificationView - spacing: units.smallSpacing - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - bottomMargin: stripe.height * 2 - } - height: parent.height / 3 - interactive: false - - z: 1 - verticalLayoutDirection: ListView.BottomToTop - model: notificationsModel - - add: Transition { - NumberAnimation { - properties: "x" - from: notificationView.width - duration: 100 - } - } - - remove: Transition { - NumberAnimation { - properties: "x" - to: notificationView.width - duration: 500 - } - NumberAnimation { - properties: "opacity" - to: 0 - duration: 500 - } - } - - removeDisplaced: Transition { - SequentialAnimation { - PauseAnimation { duration: 600 } - NumberAnimation { properties: "x,y"; duration: 100 } - } - } - - delegate: NotificationStripe {} - - } - SatelliteStripe { - id: stripe - z: 99 - y: Math.max(applications.contentY + parent.height, parent.height - height) - - PlasmaCore.Svg { - id: stripeIcons - imagePath: Qt.resolvedUrl("../images/homescreenicons.svg") - } - - Row { - anchors.fill: parent - property int columns: 4 - property alias buttonHeight: stripe.height - - HomeLauncherSvg { - id: phoneIcon - svg: stripeIcons - elementId: "phone" - callback: function() { - dialerOverlay.open() - } - } - - HomeLauncherSvg { - id: messagingIcon - svg: stripeIcons - elementId: "messaging" - callback: function() { console.log("Start messaging") } - } - - - HomeLauncherSvg { - id: emailIcon - svg: stripeIcons - elementId: "email" - callback: function() { console.log("Start email") } - } - - - HomeLauncherSvg { - id: webIcon - svg: stripeIcons - elementId: "web" - callback: function() { console.log("Start web") } - } - } - } - } - delegate: HomeLauncher {} - Component.onCompleted : { console.log("WTF " + width) } - - - } - } - - Component.onCompleted: { - //configure the view behavior - if (desktop) { - desktop.width = width; - desktop.height = height; - } - } -} diff --git a/shell/contents/views/Desktop.qml b/shell/contents/views/Desktop.qml index 5f371f01..60c331b9 100644 --- a/shell/contents/views/Desktop.qml +++ b/shell/contents/views/Desktop.qml @@ -123,6 +123,14 @@ Item { } } + //pass the focus to the containment, so it can react to homescreen activate/inactivate + Connections { + target: desktop + onActiveChanged: { + containment.focus = desktop.active; + } + } + Loader { id: dialerOverlay function open() { From 5730bae979c7f3a593de7e0c5cec440c24c64e25 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 9 Apr 2015 14:42:09 +0200 Subject: [PATCH 20/86] find KI18n --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb403166..83a5cc6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Gui Widgets Qml Quick) -find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative) +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n) find_package(KF5 REQUIRED COMPONENTS PlasmaQuick) From 5d4061efcf66c1711280f3764e4eb449a389301b Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 9 Apr 2015 14:58:49 +0200 Subject: [PATCH 21/86] proxy model with first 4 items used as favorites --- qmlcomponents/CMakeLists.txt | 1 + qmlcomponents/applicationlistmodel.cpp | 9 ++++ qmlcomponents/applicationlistmodel.h | 5 +++ qmlcomponents/favoritesmodel.cpp | 38 ++++++++++++++++ qmlcomponents/favoritesmodel.h | 48 +++++++++++++++++++++ qmlcomponents/satellitecomponentsplugin.cpp | 2 + 6 files changed, 103 insertions(+) create mode 100644 qmlcomponents/favoritesmodel.cpp create mode 100644 qmlcomponents/favoritesmodel.h diff --git a/qmlcomponents/CMakeLists.txt b/qmlcomponents/CMakeLists.txt index b25c4777..e4a4b561 100644 --- a/qmlcomponents/CMakeLists.txt +++ b/qmlcomponents/CMakeLists.txt @@ -3,6 +3,7 @@ project(satellitecomponents) set(satellitecomponents_SRCS satellitecomponentsplugin.cpp applicationlistmodel.cpp + favoritesmodel.cpp ) add_library(satellitecomponentsplugin SHARED ${satellitecomponents_SRCS}) diff --git a/qmlcomponents/applicationlistmodel.cpp b/qmlcomponents/applicationlistmodel.cpp index 639a30f2..8866ecb8 100644 --- a/qmlcomponents/applicationlistmodel.cpp +++ b/qmlcomponents/applicationlistmodel.cpp @@ -19,6 +19,7 @@ // Self #include "applicationlistmodel.h" +#include "favoritesmodel.h" // Qt #include @@ -37,6 +38,9 @@ ApplicationListModel::ApplicationListModel(QObject *parent) : QAbstractListModel(parent) { + m_favoritesModel = new FavoritesModel(this); + m_favoritesModel->setSourceModel(this); + //can't use the new syntax as this signal is overloaded connect(KSycoca::self(), SIGNAL(databaseChanged(const QStringList &)), this, SLOT(sycocaDbChanged(const QStringList &))); @@ -46,6 +50,11 @@ ApplicationListModel::~ApplicationListModel() { } +FavoritesModel *ApplicationListModel::favoritesModel() +{ + return m_favoritesModel; +} + QHash ApplicationListModel::roleNames() const { QHash roleNames; diff --git a/qmlcomponents/applicationlistmodel.h b/qmlcomponents/applicationlistmodel.h index 15cc5272..95b0b45e 100644 --- a/qmlcomponents/applicationlistmodel.h +++ b/qmlcomponents/applicationlistmodel.h @@ -27,6 +27,8 @@ class QString; +class FavoritesModel; + struct ApplicationData { QString name; QString icon; @@ -39,11 +41,13 @@ class ApplicationListModel : public QAbstractListModel { Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(QStringList appOrder READ appOrder WRITE setAppOrder NOTIFY appOrderChanged) + Q_PROPERTY(FavoritesModel *favoritesModel READ favoritesModel CONSTANT) public: ApplicationListModel(QObject *parent = 0); virtual ~ApplicationListModel(); + FavoritesModel *favoritesModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int count() { return m_applicationList.count(); } @@ -81,6 +85,7 @@ private: QStringList m_appOrder; QHash m_appPositions; + FavoritesModel *m_favoritesModel; }; #endif // APPLICATIONLISTMODEL_H diff --git a/qmlcomponents/favoritesmodel.cpp b/qmlcomponents/favoritesmodel.cpp new file mode 100644 index 00000000..be4c2fab --- /dev/null +++ b/qmlcomponents/favoritesmodel.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2015 by Marco Martin + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "favoritesmodel.h" + +#include + +FavoritesModel::FavoritesModel(QObject *parent) + : QIdentityProxyModel(parent) +{ +} + +FavoritesModel::~FavoritesModel() +{ +} + +QHash FavoritesModel::roleNames() const +{ + return sourceModel()->roleNames(); +} + +#include "moc_favoritesmodel.cpp" diff --git a/qmlcomponents/favoritesmodel.h b/qmlcomponents/favoritesmodel.h new file mode 100644 index 00000000..606ad1eb --- /dev/null +++ b/qmlcomponents/favoritesmodel.h @@ -0,0 +1,48 @@ +/* + * Copyright 2015 by Marco MArtin + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FAVORITESMODEL_H +#define FAVORITESMODEL_H + +#include + + +class QTimer; + + +class FavoritesModel : public QIdentityProxyModel +{ + Q_OBJECT + +public: + FavoritesModel(QObject *parent = 0); + ~FavoritesModel(); + + QHash roleNames() const; + + int count() const + { + return qMin(rowCount(), 4); + } + +private: + +}; + +#endif diff --git a/qmlcomponents/satellitecomponentsplugin.cpp b/qmlcomponents/satellitecomponentsplugin.cpp index df97eab3..8ca7311c 100644 --- a/qmlcomponents/satellitecomponentsplugin.cpp +++ b/qmlcomponents/satellitecomponentsplugin.cpp @@ -28,6 +28,7 @@ #include #include "applicationlistmodel.h" +#include "favoritesmodel.h" void SatelliteComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { @@ -45,6 +46,7 @@ void SatelliteComponentsPlugin::registerTypes(const char *uri) Q_ASSERT(uri == QLatin1String("org.kde.satellite.components")); qmlRegisterType(uri, 0, 1, "ApplicationListModel"); + qmlRegisterType(); } From 33547e57b09d7fa1b9cb04315b219b55b29aa462 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 10 Apr 2015 23:59:52 +0200 Subject: [PATCH 22/86] refactor of the drag and drop completely managed by a top level MouseEventListener --- .../homescreen/contents/ui/HomeLauncher.qml | 164 +++-------------- .../contents/ui/SatelliteStripe.qml | 15 +- containments/homescreen/contents/ui/main.qml | 171 ++++++++++++++---- qmlcomponents/favoritesmodel.cpp | 5 + qmlcomponents/favoritesmodel.h | 5 +- 5 files changed, 179 insertions(+), 181 deletions(-) diff --git a/containments/homescreen/contents/ui/HomeLauncher.qml b/containments/homescreen/contents/ui/HomeLauncher.qml index a7b8ae93..69d3ab60 100644 --- a/containments/homescreen/contents/ui/HomeLauncher.qml +++ b/containments/homescreen/contents/ui/HomeLauncher.qml @@ -8,150 +8,42 @@ Item { width: applicationsView.cellWidth height: width - property int idx: index - property int oldIdx: -1 + property var modelData: model - Rectangle { - anchors.fill: parent - color: PlasmaCore.ColorScope.textColor - radius: units.gridUnit - opacity: (delegateItem.drag.target != null) ? 0.4 : 0 - Behavior on opacity { - NumberAnimation { + opacity: root.reorderingApps && delegateRoot.GridView.view.dragData && delegateRoot.GridView.view.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole ? 0.3 : 1 + + PlasmaCore.IconItem { + id: icon + anchors.centerIn: parent + width: parent.height / 2 + height: width + source: modelData.ApplicationIconRole + scale: root.reorderingApps && delegateRoot.GridView.view.dragData && delegateRoot.GridView.view.dragData.ApplicationStorageIdRole != modelData.ApplicationStorageIdRole ? 0.6 : 1 + Behavior on scale { + NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } } - //animate index change - onIdxChanged: { - if (delegateItem.drag.target != null) { - return; + + PlasmaComponents.Label { + id: label + visible: text.length > 0 + + anchors { + top: icon.bottom + left: icon.left + right: icon.right } - if (oldIdx < 0) { - oldIdx = idx; - return; - } + wrapMode: Text.WordWrap + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + maximumLineCount: 2 - delegateItem.x = ((oldIdx % 4) * GridView.view.cellWidth) - ((idx % 4) * GridView.view.cellWidth); - delegateItem.y = (Math.floor(oldIdx / 4) * GridView.view.cellHeight) - (Math.floor(idx / 4) * GridView.view.cellHeight); - - translAnim.running = true; - - oldIdx = idx; - } - - NumberAnimation { - id: translAnim - duration: units.longDuration - easing.type: Easing.InOutQuad - target: delegateItem - properties: "x,y" - to: 0 - } - MouseArea { - id: delegateItem - - width: applicationsView.cellWidth - height: width - - states: [ - State { - when: delegateItem.drag.target != null - ParentChange { - target: delegateItem - parent: delegateRoot.parent - } - PropertyChanges { - target: delegateItem - z: 9999 - } - } - ] - function updateRow() { - var pos = mapToItem(delegateRoot.parent, 0, 0); - - var newRow = (Math.round(delegateRoot.GridView.view.width / delegateRoot.GridView.view.cellWidth) * Math.round(pos.y / delegateRoot.GridView.view.cellHeight) + Math.round(pos.x / delegateRoot.GridView.view.cellWidth)); - - if (model.ApplicationOriginalRowRole != newRow) { - appListModel.moveItem(model.ApplicationOriginalRowRole, newRow); - } - - } - - onClicked: { - console.log("Clicked: " + model.ApplicationStorageIdRole) - appListModel.runApplication(model.ApplicationStorageIdRole) - oldX = x - oldY = y - } - onPressAndHold: { - delegateRoot.GridView.view.draggingItem = delegateItem; - delegateItem.drag.target = delegateItem; - root.reorderingApps = true; - } - onReleased: { - delegateRoot.GridView.view.draggingItem = delegateItem; - delegateItem.drag.target = null; - root.reorderingApps = false; - - translAnim.running = true; - autoScrollTimer.running = false; - } - onPositionChanged: { - if (!autoScrollTimer.running && delegateItem.drag.target) { - updateRow(); - - var screenPos = mapToItem(delegateRoot.GridView.view, 0, 0); - - if (applicationsView.contentY > 0 && screenPos.y < root.height / 4) { - autoScrollTimer.scrollDown = false; - autoScrollTimer.running = true; - } else if (!applicationsView.atYEnd && screenPos.y > 3 * (root.height / 4)) { - autoScrollTimer.scrollDown = true; - autoScrollTimer.running = true; - } else { - autoScrollTimer.running = false; - } - } else { - autoScrollTimer.running = false; - } - } - - PlasmaCore.IconItem { - id: icon - anchors.centerIn: parent - width: parent.height / 2 - height: width - source: model.ApplicationIconRole - scale: root.reorderingApps && !delegateItem.drag.target ? 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 - } - - wrapMode: Text.WordWrap - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - maximumLineCount: 2 - - text: model.ApplicationNameRole - font.pixelSize: theme.smallestFont.pixelSize - color: PlasmaCore.ColorScope.textColor - } + text: modelData.ApplicationNameRole + font.pixelSize: theme.smallestFont.pixelSize + color: PlasmaCore.ColorScope.textColor } } diff --git a/containments/homescreen/contents/ui/SatelliteStripe.qml b/containments/homescreen/contents/ui/SatelliteStripe.qml index 813576f6..74c1df11 100644 --- a/containments/homescreen/contents/ui/SatelliteStripe.qml +++ b/containments/homescreen/contents/ui/SatelliteStripe.qml @@ -1,7 +1,9 @@ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore -Item { +PlasmaCore.ColorScope { + colorGroup: PlasmaCore.Theme.NormalColorGroup + PlasmaCore.FrameSvgItem { z: -1 imagePath: "widgets/background" @@ -11,10 +13,17 @@ Item { topMargin: -margins.top bottomMargin: -margins.bottom } + Rectangle { + anchors { + fill: parent + topMargin: parent.margins.top + bottomMargin: parent.margins.bottom + } + color: PlasmaCore.ColorScope.backgroundColor + } } - opacity: 0.6 - height: Math.max(100, units.gridUnit * 2.5) + height: applicationsView.cellWidth width: parent.width y: parent.height / 2 - height / 2 x: 0 diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 55148195..e506af37 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -22,12 +22,12 @@ 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 org.kde.satellite.components 0.1 as SatelliteComponents import "plasmapackage:/code/LayoutManager.js" as LayoutManager -Item { +MouseEventListener { id: root width: 480 height: 640 @@ -118,10 +118,8 @@ Item { interval: 10 onTriggered: { applicationsView.contentY += scrollDown ? 8 : -8; - if (applicationsView.draggingItem) { - applicationsView.draggingItem.y += scrollDown ? 8 : -8; - - applicationsView.draggingItem.updateRow(); + if (applicationsView.dragData) { + dragDelegate.updateRow(); } } } @@ -151,6 +149,77 @@ Item { } } + 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; + dragDelegate.x = mouse.x - dragDelegate.width/2; + dragDelegate.y = mouse.y - dragDelegate.height/2; + root.reorderingApps = true; + dragDelegate.visible = true; + } + onPositionChanged: { + if (!applicationsView.dragData) { + return; + } + dragDelegate.x = mouse.x - dragDelegate.width/2; + dragDelegate.y = mouse.y - dragDelegate.height/2; + + dragDelegate.updateRow(); + + if (!autoScrollTimer.running) { + + + if (mouse.y < root.height / 4) { + autoScrollTimer.running = false; + } else if (applicationsView.contentY > 0 && mouse.y < root.buttonHeight + root.height / 4) { + autoScrollTimer.scrollDown = false; + autoScrollTimer.running = true; + } else if (!applicationsView.atYEnd && mouse.y > 3 * (root.height / 4)) { + autoScrollTimer.scrollDown = true; + autoScrollTimer.running = true; + } else { + autoScrollTimer.running = false; + } + } else { + autoScrollTimer.running = false; + } + } + onReleased: { + applicationsView.interactive = true; + dragDelegate.visible = false; + applicationsView.dragData = null; + root.reorderingApps = false; + applicationsView.forceLayout(); + autoScrollTimer.running = false; + } + onClicked: { + var pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y); + var item = applicationsView.itemAt(pos.x, pos.y) + if (!item) { + return; + } + appListModel.runApplication(item.modelData.ApplicationStorageIdRole) + } PlasmaCore.ColorScope { anchors.fill: parent colorGroup: PlasmaCore.Theme.ComplementaryColorGroup @@ -161,6 +230,29 @@ Item { anchors.fill: parent } + HomeLauncher { + id: dragDelegate + z: 999 + function updateRow() { + if (!applicationsView.dragData) { + return; + } + + var pos = root.mapToItem(applicationsView.contentItem, x, y); + + //in favorites area? + if (applicationsView.headerItem.favoritesStrip.contains(root.mapToItem(applicationsView.headerItem.favoritesStrip, x, y))) { + pos.y = 1; + } + + var newRow = (Math.round(applicationsView.width / applicationsView.cellWidth) * Math.round(pos.y / applicationsView.cellHeight) + Math.round(pos.x / applicationsView.cellWidth)); + + if (applicationsView.dragData.ApplicationOriginalRowRole != newRow) { + appListModel.moveItem(applicationsView.dragData.ApplicationOriginalRowRole, newRow); + applicationsView.dragData.ApplicationOriginalRowRole = newRow; + } + } + } GridView { id: applicationsView anchors { @@ -171,6 +263,7 @@ Item { } property Item draggingItem + property var dragData cellWidth: root.buttonHeight cellHeight: cellWidth @@ -197,13 +290,30 @@ Item { 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 {} + delegate: HomeLauncher { + visible: index > 3 + } header: MouseArea { z: 999 property Item layout: appletsLayout property Item lastSpacer: spacer + property Item favoritesStrip: favoritesView width: root.width height: mainLayout.Layout.minimumHeight property int margin: stripe.height + units.gridUnit * 2 @@ -262,49 +372,30 @@ Item { property int viewPos: applicationsView.contentItem.height * applicationsView.visibleArea.yPosition y: Math.max(viewPos, - Math.min(parent.height, viewPos + root.height) - height + Math.max(0, -(parent.height - height + applicationsView.contentY))) + Math.min(parent.height, viewPos + root.height - height) + Math.max(0, -(parent.height - height + applicationsView.contentY))) PlasmaCore.Svg { id: stripeIcons imagePath: Qt.resolvedUrl("../images/homescreenicons.svg") } - Row { + 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 - property alias buttonHeight: stripe.height + interactive: false + flow: GridView.FlowTopToBottom + cellWidth: root.buttonHeight + cellHeight: cellWidth + property Item draggingItem - HomeLauncherSvg { - id: phoneIcon - svg: stripeIcons - elementId: "phone" - callback: function() { - console.log("Start phone") - } - } + model: appListModel + delegate: HomeLauncher {} - HomeLauncherSvg { - id: messagingIcon - svg: stripeIcons - elementId: "messaging" - callback: function() { console.log("Start messaging") } - } - - - HomeLauncherSvg { - id: emailIcon - svg: stripeIcons - elementId: "email" - callback: function() { console.log("Start email") } - } - - - HomeLauncherSvg { - id: webIcon - svg: stripeIcons - elementId: "web" - callback: function() { console.log("Start web") } - } } } } diff --git a/qmlcomponents/favoritesmodel.cpp b/qmlcomponents/favoritesmodel.cpp index be4c2fab..d37fb2a0 100644 --- a/qmlcomponents/favoritesmodel.cpp +++ b/qmlcomponents/favoritesmodel.cpp @@ -35,4 +35,9 @@ QHash FavoritesModel::roleNames() const return sourceModel()->roleNames(); } +int FavoritesModel::rowCount(const QModelIndex &parent) const +{return QIdentityProxyModel::rowCount(parent); + return qMin(QIdentityProxyModel::rowCount(parent), 4); +} + #include "moc_favoritesmodel.cpp" diff --git a/qmlcomponents/favoritesmodel.h b/qmlcomponents/favoritesmodel.h index 606ad1eb..68125b68 100644 --- a/qmlcomponents/favoritesmodel.h +++ b/qmlcomponents/favoritesmodel.h @@ -34,11 +34,12 @@ public: FavoritesModel(QObject *parent = 0); ~FavoritesModel(); - QHash roleNames() const; + QHash roleNames() const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int count() const { - return qMin(rowCount(), 4); + return rowCount(); } private: From 0dca5a4a3f794ad46780816168199a2880b5b7a3 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 00:23:49 +0200 Subject: [PATCH 23/86] fix reordering logic --- containments/homescreen/contents/ui/main.qml | 2 -- qmlcomponents/applicationlistmodel.cpp | 10 ++++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index e506af37..0f29cfd6 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -262,7 +262,6 @@ MouseEventListener { right: parent.right } - property Item draggingItem property var dragData cellWidth: root.buttonHeight @@ -391,7 +390,6 @@ MouseEventListener { flow: GridView.FlowTopToBottom cellWidth: root.buttonHeight cellHeight: cellWidth - property Item draggingItem model: appListModel delegate: HomeLauncher {} diff --git a/qmlcomponents/applicationlistmodel.cpp b/qmlcomponents/applicationlistmodel.cpp index 8866ecb8..c7373448 100644 --- a/qmlcomponents/applicationlistmodel.cpp +++ b/qmlcomponents/applicationlistmodel.cpp @@ -182,8 +182,14 @@ Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination) } beginMoveRows(QModelIndex(), row, row, QModelIndex(), destination); - ApplicationData data = m_applicationList.takeAt(row); - m_applicationList.insert(destination, data); + if (destination > row) { + ApplicationData data = m_applicationList.at(row); + m_applicationList.insert(destination, data); + m_applicationList.takeAt(row); + } else { + ApplicationData data = m_applicationList.takeAt(row); + m_applicationList.insert(destination, data); + } m_appOrder.clear(); From fa2d6b0258b99c2d8ed721873601bd6258c386d1 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 00:50:14 +0200 Subject: [PATCH 24/86] get rid of favoritesmodel --- qmlcomponents/CMakeLists.txt | 1 - qmlcomponents/applicationlistmodel.cpp | 9 ---- qmlcomponents/applicationlistmodel.h | 5 --- qmlcomponents/favoritesmodel.cpp | 43 ------------------ qmlcomponents/favoritesmodel.h | 49 --------------------- qmlcomponents/satellitecomponentsplugin.cpp | 2 - 6 files changed, 109 deletions(-) delete mode 100644 qmlcomponents/favoritesmodel.cpp delete mode 100644 qmlcomponents/favoritesmodel.h diff --git a/qmlcomponents/CMakeLists.txt b/qmlcomponents/CMakeLists.txt index e4a4b561..b25c4777 100644 --- a/qmlcomponents/CMakeLists.txt +++ b/qmlcomponents/CMakeLists.txt @@ -3,7 +3,6 @@ project(satellitecomponents) set(satellitecomponents_SRCS satellitecomponentsplugin.cpp applicationlistmodel.cpp - favoritesmodel.cpp ) add_library(satellitecomponentsplugin SHARED ${satellitecomponents_SRCS}) diff --git a/qmlcomponents/applicationlistmodel.cpp b/qmlcomponents/applicationlistmodel.cpp index c7373448..77f54e0d 100644 --- a/qmlcomponents/applicationlistmodel.cpp +++ b/qmlcomponents/applicationlistmodel.cpp @@ -19,7 +19,6 @@ // Self #include "applicationlistmodel.h" -#include "favoritesmodel.h" // Qt #include @@ -38,9 +37,6 @@ ApplicationListModel::ApplicationListModel(QObject *parent) : QAbstractListModel(parent) { - m_favoritesModel = new FavoritesModel(this); - m_favoritesModel->setSourceModel(this); - //can't use the new syntax as this signal is overloaded connect(KSycoca::self(), SIGNAL(databaseChanged(const QStringList &)), this, SLOT(sycocaDbChanged(const QStringList &))); @@ -50,11 +46,6 @@ ApplicationListModel::~ApplicationListModel() { } -FavoritesModel *ApplicationListModel::favoritesModel() -{ - return m_favoritesModel; -} - QHash ApplicationListModel::roleNames() const { QHash roleNames; diff --git a/qmlcomponents/applicationlistmodel.h b/qmlcomponents/applicationlistmodel.h index 95b0b45e..15cc5272 100644 --- a/qmlcomponents/applicationlistmodel.h +++ b/qmlcomponents/applicationlistmodel.h @@ -27,8 +27,6 @@ class QString; -class FavoritesModel; - struct ApplicationData { QString name; QString icon; @@ -41,13 +39,11 @@ class ApplicationListModel : public QAbstractListModel { Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(QStringList appOrder READ appOrder WRITE setAppOrder NOTIFY appOrderChanged) - Q_PROPERTY(FavoritesModel *favoritesModel READ favoritesModel CONSTANT) public: ApplicationListModel(QObject *parent = 0); virtual ~ApplicationListModel(); - FavoritesModel *favoritesModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int count() { return m_applicationList.count(); } @@ -85,7 +81,6 @@ private: QStringList m_appOrder; QHash m_appPositions; - FavoritesModel *m_favoritesModel; }; #endif // APPLICATIONLISTMODEL_H diff --git a/qmlcomponents/favoritesmodel.cpp b/qmlcomponents/favoritesmodel.cpp deleted file mode 100644 index d37fb2a0..00000000 --- a/qmlcomponents/favoritesmodel.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2015 by Marco Martin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, 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 Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "favoritesmodel.h" - -#include - -FavoritesModel::FavoritesModel(QObject *parent) - : QIdentityProxyModel(parent) -{ -} - -FavoritesModel::~FavoritesModel() -{ -} - -QHash FavoritesModel::roleNames() const -{ - return sourceModel()->roleNames(); -} - -int FavoritesModel::rowCount(const QModelIndex &parent) const -{return QIdentityProxyModel::rowCount(parent); - return qMin(QIdentityProxyModel::rowCount(parent), 4); -} - -#include "moc_favoritesmodel.cpp" diff --git a/qmlcomponents/favoritesmodel.h b/qmlcomponents/favoritesmodel.h deleted file mode 100644 index 68125b68..00000000 --- a/qmlcomponents/favoritesmodel.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015 by Marco MArtin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, 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 Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef FAVORITESMODEL_H -#define FAVORITESMODEL_H - -#include - - -class QTimer; - - -class FavoritesModel : public QIdentityProxyModel -{ - Q_OBJECT - -public: - FavoritesModel(QObject *parent = 0); - ~FavoritesModel(); - - QHash roleNames() const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - - int count() const - { - return rowCount(); - } - -private: - -}; - -#endif diff --git a/qmlcomponents/satellitecomponentsplugin.cpp b/qmlcomponents/satellitecomponentsplugin.cpp index 8ca7311c..df97eab3 100644 --- a/qmlcomponents/satellitecomponentsplugin.cpp +++ b/qmlcomponents/satellitecomponentsplugin.cpp @@ -28,7 +28,6 @@ #include #include "applicationlistmodel.h" -#include "favoritesmodel.h" void SatelliteComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { @@ -46,7 +45,6 @@ void SatelliteComponentsPlugin::registerTypes(const char *uri) Q_ASSERT(uri == QLatin1String("org.kde.satellite.components")); qmlRegisterType(uri, 0, 1, "ApplicationListModel"); - qmlRegisterType(); } From f9b30c1f95f6bc9c05d0ecb80ac0a6f58388f6f1 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 10:28:05 +0200 Subject: [PATCH 25/86] better detection if we are on the fav strip --- containments/homescreen/contents/ui/main.qml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 0f29cfd6..c569faa8 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -116,7 +116,7 @@ MouseEventListener { property bool scrollDown: true repeat: true interval: 10 - onTriggered: { + onTriggered: {return; applicationsView.contentY += scrollDown ? 8 : -8; if (applicationsView.dragData) { dragDelegate.updateRow(); @@ -188,15 +188,19 @@ MouseEventListener { if (!autoScrollTimer.running) { - - if (mouse.y < root.height / 4) { + var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y); + //FAVORITES + if (applicationsView.headerItem.favoritesStrip.contains(pos)) { autoScrollTimer.running = false; + //SCROLL UP } else if (applicationsView.contentY > 0 && mouse.y < root.buttonHeight + root.height / 4) { autoScrollTimer.scrollDown = false; autoScrollTimer.running = true; + //SCROLL DOWN } else if (!applicationsView.atYEnd && mouse.y > 3 * (root.height / 4)) { autoScrollTimer.scrollDown = true; autoScrollTimer.running = true; + //DON't SCROLL } else { autoScrollTimer.running = false; } From 1efb009b5e0de7e7158ebbb1b4d980476eb83a6e Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 11:26:55 +0200 Subject: [PATCH 26/86] better autoscroll behavior --- containments/homescreen/contents/ui/main.qml | 103 ++++++++++++++----- 1 file changed, 77 insertions(+), 26 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index c569faa8..973950c3 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -16,7 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ -import QtQuick 2.1 +import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 @@ -115,12 +115,12 @@ MouseEventListener { id: autoScrollTimer property bool scrollDown: true repeat: true - interval: 10 - onTriggered: {return; - applicationsView.contentY += scrollDown ? 8 : -8; - if (applicationsView.dragData) { - dragDelegate.updateRow(); - } + interval: 1500 + onTriggered: { + scrollAnim.to = scrollDown ? + Math.min(applicationsView.contentItem.height - applicationsView.headerItem.height - root.height, applicationsView.contentY + root.height/2) : + Math.max(0, applicationsView.contentY - root.height/2); + scrollAnim.running = true; } } @@ -186,27 +186,31 @@ MouseEventListener { dragDelegate.updateRow(); - if (!autoScrollTimer.running) { - - var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y); - //FAVORITES - if (applicationsView.headerItem.favoritesStrip.contains(pos)) { - autoScrollTimer.running = false; - //SCROLL UP - } else if (applicationsView.contentY > 0 && mouse.y < root.buttonHeight + root.height / 4) { - autoScrollTimer.scrollDown = false; - autoScrollTimer.running = true; - //SCROLL DOWN - } else if (!applicationsView.atYEnd && mouse.y > 3 * (root.height / 4)) { - autoScrollTimer.scrollDown = true; - autoScrollTimer.running = true; - //DON't SCROLL - } else { - autoScrollTimer.running = false; - } + var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y); + //FAVORITES + if (applicationsView.headerItem.favoritesStrip.contains(pos)) { + autoScrollTimer.running = false; + scrollUpIndicator.opacity = 0; + scrollDownIndicator.opacity = 0; + //SCROLL UP + } else if (applicationsView.contentY > 0 && mouse.y < root.buttonHeight + root.height / 4) { + autoScrollTimer.scrollDown = false; + autoScrollTimer.running = true; + scrollUpIndicator.opacity = 1; + scrollDownIndicator.opacity = 0; + //SCROLL DOWN + } else if (!applicationsView.atYEnd && mouse.y > 3 * (root.height / 4)) { + autoScrollTimer.scrollDown = true; + autoScrollTimer.running = true; + scrollUpIndicator.opacity = 0; + scrollDownIndicator.opacity = 1; + //DON't SCROLL } else { autoScrollTimer.running = false; + scrollUpIndicator.opacity = 0; + scrollDownIndicator.opacity = 0; } + } onReleased: { applicationsView.interactive = true; @@ -215,6 +219,8 @@ MouseEventListener { root.reorderingApps = false; applicationsView.forceLayout(); autoScrollTimer.running = false; + scrollUpIndicator.opacity = 0; + scrollDownIndicator.opacity = 0; } onClicked: { var pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y); @@ -234,6 +240,51 @@ MouseEventListener { 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: 200 + } + 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 + } + 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 + } + } + } + HomeLauncher { id: dragDelegate z: 999 @@ -425,7 +476,7 @@ MouseEventListener { radius: width anchors.right: parent.right y: applicationsView.height * applicationsView.visibleArea.yPosition - opacity: scrollbarMouse.pressed || applicationsView.flicking ? 0.8 : 0 + opacity: scrollbarMouse.pressed || applicationsView.flicking || scrollDownIndicator.opacity > 0 || scrollUpIndicator.opacity > 0 ? 0.8 : 0 Behavior on opacity { NumberAnimation { duration: units.longDuration From c036b12f7b7236b856287e6f30458bd04e45787a Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 11:36:17 +0200 Subject: [PATCH 27/86] nimations in favorites bar drag --- .../homescreen/contents/ui/HomeLauncher.qml | 4 +-- containments/homescreen/contents/ui/main.qml | 26 +++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/containments/homescreen/contents/ui/HomeLauncher.qml b/containments/homescreen/contents/ui/HomeLauncher.qml index 69d3ab60..d8859125 100644 --- a/containments/homescreen/contents/ui/HomeLauncher.qml +++ b/containments/homescreen/contents/ui/HomeLauncher.qml @@ -10,7 +10,7 @@ Item { property var modelData: model - opacity: root.reorderingApps && delegateRoot.GridView.view.dragData && delegateRoot.GridView.view.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole ? 0.3 : 1 + opacity: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole ? 0.3 : 1 PlasmaCore.IconItem { id: icon @@ -18,7 +18,7 @@ Item { width: parent.height / 2 height: width source: modelData.ApplicationIconRole - scale: root.reorderingApps && delegateRoot.GridView.view.dragData && delegateRoot.GridView.view.dragData.ApplicationStorageIdRole != modelData.ApplicationStorageIdRole ? 0.6 : 1 + scale: root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole != modelData.ApplicationStorageIdRole ? 0.6 : 1 Behavior on scale { NumberAnimation { duration: units.longDuration diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 973950c3..504a2f3d 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -223,11 +223,19 @@ MouseEventListener { scrollDownIndicator.opacity = 0; } onClicked: { - var pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y); - var item = applicationsView.itemAt(pos.x, pos.y) + 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; } + appListModel.runApplication(item.modelData.ApplicationStorageIdRole) } PlasmaCore.ColorScope { @@ -449,6 +457,20 @@ MouseEventListener { model: appListModel delegate: HomeLauncher {} + 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" + } + } } } } From 6c326621231fa10386630da0711ef1b65663d999 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 12:40:54 +0200 Subject: [PATCH 28/86] animation effect for the drag delegate --- .../homescreen/contents/ui/HomeLauncher.qml | 15 +++++- containments/homescreen/contents/ui/main.qml | 50 +++++++++++++++++-- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/containments/homescreen/contents/ui/HomeLauncher.qml b/containments/homescreen/contents/ui/HomeLauncher.qml index d8859125..b755c206 100644 --- a/containments/homescreen/contents/ui/HomeLauncher.qml +++ b/containments/homescreen/contents/ui/HomeLauncher.qml @@ -9,8 +9,21 @@ Item { height: width property var modelData: model + property bool isDropTarget: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole - opacity: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole ? 0.3 : 1 + opacity: isDropTarget ? 0.3 : 1 + + /*function syncDropTarget() { + if (isDropTarget) { + var pos = mapToItem(root, x, y); + dragDelegate.xTarget = pos.x; + dragDelegate.yTarget = pos.y; + print("AAAAA"+pos.x+" "+pos.x+" "+delegateRoot) + } + } + onIsDropTargetChanged: syncDropTarget(); + onXChanged: syncDropTarget(); + onYChanged: syncDropTarget();*/ PlasmaCore.IconItem { id: icon diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 504a2f3d..a00c26eb 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -172,10 +172,12 @@ MouseEventListener { dragDelegate.modelData = applicationsView.dragData; applicationsView.interactive = false; - dragDelegate.x = mouse.x - dragDelegate.width/2; - dragDelegate.y = mouse.y - dragDelegate.height/2; root.reorderingApps = true; - dragDelegate.visible = 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) { @@ -184,7 +186,19 @@ MouseEventListener { dragDelegate.x = mouse.x - dragDelegate.width/2; dragDelegate.y = mouse.y - dragDelegate.height/2; - dragDelegate.updateRow(); + 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) { + appListModel.moveItem(applicationsView.dragData.ApplicationOriginalRowRole, newRow); + applicationsView.dragData.ApplicationOriginalRowRole = newRow; + } var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y); //FAVORITES @@ -214,7 +228,9 @@ MouseEventListener { } onReleased: { applicationsView.interactive = true; - dragDelegate.visible = false; + dragDelegate.xTarget = Math.floor(mouse.x / root.buttonHeight) * root.buttonHeight + dragDelegate.yTarget = Math.floor(mouse.y / root.buttonHeight) * root.buttonHeight + dragDelegate.opacity = 0; applicationsView.dragData = null; root.reorderingApps = false; applicationsView.forceLayout(); @@ -296,6 +312,8 @@ MouseEventListener { HomeLauncher { id: dragDelegate z: 999 + property int xTarget + property int yTarget function updateRow() { if (!applicationsView.dragData) { return; @@ -315,6 +333,28 @@ MouseEventListener { applicationsView.dragData.ApplicationOriginalRowRole = newRow; } } + 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 From 46f8a90371b0f3bfcdeb1c1dfe80df8cf8f83aa3 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 12:44:55 +0200 Subject: [PATCH 29/86] remove dead code --- containments/homescreen/contents/ui/main.qml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index a00c26eb..78febd21 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -314,25 +314,7 @@ MouseEventListener { z: 999 property int xTarget property int yTarget - function updateRow() { - if (!applicationsView.dragData) { - return; - } - - var pos = root.mapToItem(applicationsView.contentItem, x, y); - //in favorites area? - if (applicationsView.headerItem.favoritesStrip.contains(root.mapToItem(applicationsView.headerItem.favoritesStrip, x, y))) { - pos.y = 1; - } - - var newRow = (Math.round(applicationsView.width / applicationsView.cellWidth) * Math.round(pos.y / applicationsView.cellHeight) + Math.round(pos.x / applicationsView.cellWidth)); - - if (applicationsView.dragData.ApplicationOriginalRowRole != newRow) { - appListModel.moveItem(applicationsView.dragData.ApplicationOriginalRowRole, newRow); - applicationsView.dragData.ApplicationOriginalRowRole = newRow; - } - } Behavior on opacity { ParallelAnimation { OpacityAnimator { From e3913eb107c88668467a573f5baae458b678bf90 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 12:59:52 +0200 Subject: [PATCH 30/86] don't trigger wallpaper config when pressing on favorites --- .../homescreen/contents/ui/HomeLauncher.qml | 14 +------------- containments/homescreen/contents/ui/main.qml | 11 ++++++++--- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/containments/homescreen/contents/ui/HomeLauncher.qml b/containments/homescreen/contents/ui/HomeLauncher.qml index b755c206..a12fecef 100644 --- a/containments/homescreen/contents/ui/HomeLauncher.qml +++ b/containments/homescreen/contents/ui/HomeLauncher.qml @@ -12,19 +12,7 @@ Item { property bool isDropTarget: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole opacity: isDropTarget ? 0.3 : 1 - - /*function syncDropTarget() { - if (isDropTarget) { - var pos = mapToItem(root, x, y); - dragDelegate.xTarget = pos.x; - dragDelegate.yTarget = pos.y; - print("AAAAA"+pos.x+" "+pos.x+" "+delegateRoot) - } - } - onIsDropTargetChanged: syncDropTarget(); - onXChanged: syncDropTarget(); - onYChanged: syncDropTarget();*/ - +onModelDataChanged:print("AAAA"); PlasmaCore.IconItem { id: icon anchors.centerIn: parent diff --git a/containments/homescreen/contents/ui/main.qml b/containments/homescreen/contents/ui/main.qml index 78febd21..8a34a15a 100644 --- a/containments/homescreen/contents/ui/main.qml +++ b/containments/homescreen/contents/ui/main.qml @@ -228,9 +228,11 @@ MouseEventListener { } onReleased: { applicationsView.interactive = true; - dragDelegate.xTarget = Math.floor(mouse.x / root.buttonHeight) * root.buttonHeight - dragDelegate.yTarget = Math.floor(mouse.y / root.buttonHeight) * root.buttonHeight + dragDelegate.xTarget = Math.floor(mouse.x / root.buttonHeight) * root.buttonHeight; + dragDelegate.yTarget = Math.floor(mouse.y / root.buttonHeight) * root.buttonHeight; dragDelegate.opacity = 0; + dragDelegate.modelData.ApplicationIconRole = ""; + dragDelegate.modelDataChanged(); applicationsView.dragData = null; root.reorderingApps = false; applicationsView.forceLayout(); @@ -403,7 +405,10 @@ MouseEventListener { property int margin: stripe.height + units.gridUnit * 2 onPressAndHold: { - plasmoid.action("configure").trigger(); + print(favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y))) + if (!favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y))) { + plasmoid.action("configure").trigger(); + } } ColumnLayout { From 2d909f69f4872e9ce9252ddd28cb92497820b93b Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 14:48:24 +0200 Subject: [PATCH 31/86] ignore parameters like %u in executing apps --- containments/homescreen/contents/ui/HomeLauncher.qml | 2 +- qmlcomponents/applicationlistmodel.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/containments/homescreen/contents/ui/HomeLauncher.qml b/containments/homescreen/contents/ui/HomeLauncher.qml index a12fecef..ffd23976 100644 --- a/containments/homescreen/contents/ui/HomeLauncher.qml +++ b/containments/homescreen/contents/ui/HomeLauncher.qml @@ -12,7 +12,7 @@ Item { property bool isDropTarget: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole opacity: isDropTarget ? 0.3 : 1 -onModelDataChanged:print("AAAA"); + PlasmaCore.IconItem { id: icon anchors.centerIn: parent diff --git a/qmlcomponents/applicationlistmodel.cpp b/qmlcomponents/applicationlistmodel.cpp index 77f54e0d..540161bc 100644 --- a/qmlcomponents/applicationlistmodel.cpp +++ b/qmlcomponents/applicationlistmodel.cpp @@ -205,7 +205,8 @@ void ApplicationListModel::runApplication(const QString &storageId) KService::Ptr service = KService::serviceByStorageId(storageId); - QProcess::startDetached(service->exec()); + //ignore parameters like %u + QProcess::startDetached(service->exec().replace(QRegExp("%\\w"), "")); } QStringList ApplicationListModel::appOrder() const From e1133e04b63ee3416305b4920b5d3cbff8197a40 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 16:01:06 +0200 Subject: [PATCH 32/86] an applet for quick settings intended for the top panel --- applets/CMakeLists.txt | 1 + applets/quicksettings/Messages.sh | 4 + .../quicksettings/contents/ui/Delegate.qml | 69 +++++++++++ applets/quicksettings/contents/ui/main.qml | 110 ++++++++++++++++++ applets/quicksettings/metadata.desktop | 21 ++++ shell/contents/layout.js | 3 +- 6 files changed, 206 insertions(+), 2 deletions(-) create mode 100755 applets/quicksettings/Messages.sh create mode 100644 applets/quicksettings/contents/ui/Delegate.qml create mode 100644 applets/quicksettings/contents/ui/main.qml create mode 100644 applets/quicksettings/metadata.desktop diff --git a/applets/CMakeLists.txt b/applets/CMakeLists.txt index a99f1209..9c80f571 100644 --- a/applets/CMakeLists.txt +++ b/applets/CMakeLists.txt @@ -1,3 +1,4 @@ plasma_install_package(clock org.kde.phone.clock) plasma_install_package(notifications org.kde.phone.notifications) +plasma_install_package(quicksettings org.kde.phone.quicksettings) diff --git a/applets/quicksettings/Messages.sh b/applets/quicksettings/Messages.sh new file mode 100755 index 00000000..83683e21 --- /dev/null +++ b/applets/quicksettings/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_org.kde.phone.quicksettings.pot +rm -f rc.cpp diff --git a/applets/quicksettings/contents/ui/Delegate.qml b/applets/quicksettings/contents/ui/Delegate.qml new file mode 100644 index 00000000..1464bc5e --- /dev/null +++ b/applets/quicksettings/contents/ui/Delegate.qml @@ -0,0 +1,69 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +RowLayout { + spacing: units.smallSpacing + width: parent.width / 2 - units.largeSpacing / 2 + Rectangle { + Layout.minimumWidth: units.iconSizes.large + Layout.minimumHeight: width + color: model.enabled ? + Qt.rgba(PlasmaCore.ColorScope.highlightColor.r, PlasmaCore.ColorScope.highlightColor.g, PlasmaCore.ColorScope.highlightColor.b, iconMouseArea.pressed ? 0.5 : 0.3) : + Qt.rgba(PlasmaCore.ColorScope.textColor.r, PlasmaCore.ColorScope.textColor.g, PlasmaCore.ColorScope.textColor.b, iconMouseArea.pressed ? 0.5 : 0.2) + + PlasmaCore.IconItem { + colorGroup: PlasmaCore.ColorScope.colorGroup + anchors { + fill: parent + margins: units.smallSpacing + } + source: model.icon + MouseArea { + id: iconMouseArea + anchors.fill: parent + onClicked: print("action of mouse area") + } + } + } + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: Qt.rgba(PlasmaCore.ColorScope.textColor.r, PlasmaCore.ColorScope.textColor.g, PlasmaCore.ColorScope.textColor.b, labelMouseArea.pressed ? 0.5 : 0.2) + PlasmaComponents.Label { + anchors { + fill: parent + margins: units.smallSpacing + } + text: model.text + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + MouseArea { + id: labelMouseArea + anchors.fill: parent + onClicked: print("execute active-settings -m " + model.settingsModule) + } + } + } +} + diff --git a/applets/quicksettings/contents/ui/main.qml b/applets/quicksettings/contents/ui/main.qml new file mode 100644 index 00000000..c1b2e089 --- /dev/null +++ b/applets/quicksettings/contents/ui/main.qml @@ -0,0 +1,110 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.plasmoid 2.0 + + +Item { + id: bigClock + + /*Layout.minimumWidth: implicitWidth + Layout.minimumHeight: implicitHeight*/ + Plasmoid.preferredRepresentation: plasmoid.fullRepresentation + + ListModel { + id: settingsModel + + ListElement { + text: "Settings" + icon: "configure" + enabled: false + settingsModule: "" + } + ListElement { + text: "Mobile network" + icon: "network-mobile-80" + enabled: true + settingsModule: "" + } + ListElement { + text: "Airplane mode" + icon: "flightmode-on" + enabled: false + settingsModule: "" + } + ListElement { + text: "Bluetooth" + icon: "preferences-system-bluetooth" + enabled: false + settingsModule: "" + } + ListElement { + text: "Wireless" + icon: "network-wireless-on" + enabled: true + settingsModule: "org.kde.satellite.settings.wifi" + } + ListElement { + text: "Alarms" + icon: "korgac" + enabled: false + settingsModule: "" + } + ListElement { + text: "Notifications" + icon: "preferences-desktop-notification" + enabled: true + settingsModule: "" + } + ListElement { + text: "Brightness" + icon: "video-display-brightness" + enabled: false + settingsModule: "org.kde.active.settings.powermanagement" + } + ListElement { + text: "Flashlight" + icon: "package_games_puzzle" + enabled: false + settingsModule: "" + } + ListElement { + text: "Location" + icon: "plasmaapplet-location" + enabled: false + settingsModule: "" + } + } + + Flow { + anchors { + fill: parent + margins: units.largeSpacing + } + spacing: units.largeSpacing + Repeater { + model: settingsModel + delegate: Delegate {} + } + } +} diff --git a/applets/quicksettings/metadata.desktop b/applets/quicksettings/metadata.desktop new file mode 100644 index 00000000..a205c922 --- /dev/null +++ b/applets/quicksettings/metadata.desktop @@ -0,0 +1,21 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Quick Settings + +Comment=A panel to quickly access most important settings + +Icon=systemsettings +Type=Service +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Marco Martin +X-KDE-PluginInfo-Email=mart@kde.org +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=org.kde.phone.quicksettings +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=plasma.kde.org +X-KDE-ServiceTypes=Plasma/Applet +X-Plasma-API=declarativeappletscript +X-KDE-PluginInfo-Category=Settings + +X-Plasma-MainScript=ui/main.qml +X-Plasma-RemoteLocation= diff --git a/shell/contents/layout.js b/shell/contents/layout.js index df22c90d..cb89007f 100644 --- a/shell/contents/layout.js +++ b/shell/contents/layout.js @@ -17,5 +17,4 @@ for (var j = 0; j < desktopsArray.length; j++) { desktopsForActivity(id)[0].addWidget("org.kde.phone.notifications"); var panel = new Panel("org.kde.phone.panel"); -panel.addWidget("org.kde.plasma.battery"); -panel.addWidget("org.kde.plasma.devicenotifier"); \ No newline at end of file +panel.addWidget("org.kde.phone.quicksettings"); From fe4ad093ecef565847eb7c2e17e984dd5ed2664c Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 16:50:26 +0200 Subject: [PATCH 33/86] add a native interface to execute command and do the needed hw related things --- applets/CMakeLists.txt | 3 +- applets/quicksettings/CMakeLists.txt | 19 ++++++++ .../{ => package}/contents/ui/Delegate.qml | 8 +++- .../{ => package}/contents/ui/main.qml | 0 .../{ => package}/metadata.desktop | 1 + applets/quicksettings/quicksettings.cpp | 43 +++++++++++++++++++ applets/quicksettings/quicksettings.h | 43 +++++++++++++++++++ containments/panel/contents/ui/main.qml | 3 +- 8 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 applets/quicksettings/CMakeLists.txt rename applets/quicksettings/{ => package}/contents/ui/Delegate.qml (89%) rename applets/quicksettings/{ => package}/contents/ui/main.qml (100%) rename applets/quicksettings/{ => package}/metadata.desktop (92%) create mode 100644 applets/quicksettings/quicksettings.cpp create mode 100644 applets/quicksettings/quicksettings.h diff --git a/applets/CMakeLists.txt b/applets/CMakeLists.txt index 9c80f571..ca6d9d10 100644 --- a/applets/CMakeLists.txt +++ b/applets/CMakeLists.txt @@ -1,4 +1,5 @@ plasma_install_package(clock org.kde.phone.clock) plasma_install_package(notifications org.kde.phone.notifications) -plasma_install_package(quicksettings org.kde.phone.quicksettings) + +add_subdirectory(quicksettings) diff --git a/applets/quicksettings/CMakeLists.txt b/applets/quicksettings/CMakeLists.txt new file mode 100644 index 00000000..595a1f47 --- /dev/null +++ b/applets/quicksettings/CMakeLists.txt @@ -0,0 +1,19 @@ +set(quicksettings_SRCS + quicksettings.cpp +) + +add_library(plasma_applet_quicksettings MODULE ${quicksettings_SRCS}) + +kcoreaddons_desktop_to_json(plasma_applet_quicksettings package/metadata.desktop) + +target_link_libraries(plasma_applet_quicksettings + Qt5::Gui + KF5::Plasma + KF5::I18n) + + +install(TARGETS plasma_applet_quicksettings DESTINATION ${PLUGIN_INSTALL_DIR}) +#install(FILES plasma-quicksettings-default.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + +plasma_install_package(package org.kde.phone.quicksettings) + diff --git a/applets/quicksettings/contents/ui/Delegate.qml b/applets/quicksettings/package/contents/ui/Delegate.qml similarity index 89% rename from applets/quicksettings/contents/ui/Delegate.qml rename to applets/quicksettings/package/contents/ui/Delegate.qml index 1464bc5e..3e504b1e 100644 --- a/applets/quicksettings/contents/ui/Delegate.qml +++ b/applets/quicksettings/package/contents/ui/Delegate.qml @@ -61,7 +61,13 @@ RowLayout { MouseArea { id: labelMouseArea anchors.fill: parent - onClicked: print("execute active-settings -m " + model.settingsModule) + onClicked: { + var command = "active-settings"; + if (model.settingsModule) { + command += " -m " + model.settingsModule; + } + plasmoid.nativeInterface.executeCommand(command) + } } } } diff --git a/applets/quicksettings/contents/ui/main.qml b/applets/quicksettings/package/contents/ui/main.qml similarity index 100% rename from applets/quicksettings/contents/ui/main.qml rename to applets/quicksettings/package/contents/ui/main.qml diff --git a/applets/quicksettings/metadata.desktop b/applets/quicksettings/package/metadata.desktop similarity index 92% rename from applets/quicksettings/metadata.desktop rename to applets/quicksettings/package/metadata.desktop index a205c922..fb11f66a 100644 --- a/applets/quicksettings/metadata.desktop +++ b/applets/quicksettings/package/metadata.desktop @@ -11,6 +11,7 @@ X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.phone.quicksettings +X-KDE-Library=plasma_applet_quicksettings X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=plasma.kde.org X-KDE-ServiceTypes=Plasma/Applet diff --git a/applets/quicksettings/quicksettings.cpp b/applets/quicksettings/quicksettings.cpp new file mode 100644 index 00000000..f82e0a61 --- /dev/null +++ b/applets/quicksettings/quicksettings.cpp @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 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 02110-1301 USA . * + ***************************************************************************/ + +#include "quicksettings.h" + +#include +#include + +QuickSettings::QuickSettings(QObject *parent, const QVariantList &args) + : Plasma::Applet(parent, args) +{ + setHasConfigurationInterface(true); +} + +QuickSettings::~QuickSettings() +{ +} + +void QuickSettings::executeCommand(const QString &command) +{ + qWarning()<<"Executing"< * + * + * * + * 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 02110-1301 USA . * + ***************************************************************************/ + +#ifndef QUICKSETTINGS_H +#define QUICKSETTINGS_H + + +#include + + +class QuickSettings : public Plasma::Applet +{ + Q_OBJECT + +public: + QuickSettings( QObject *parent, const QVariantList &args ); + ~QuickSettings(); + +public Q_SLOTS: + void executeCommand(const QString &command); + +private: + +}; + +#endif diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml index fc1000c6..7b0b75ea 100644 --- a/containments/panel/contents/ui/main.qml +++ b/containments/panel/contents/ui/main.qml @@ -239,6 +239,7 @@ PlasmaCore.ColorScope { PlasmaComponents.TabBar { id: tabBar + visible: plasmoid.applets.count > 1 anchors { left: parent.left top: parent.top @@ -251,7 +252,7 @@ PlasmaCore.ColorScope { id: tabGroup anchors { left: parent.left - top: tabBar.bottom + top: plasmoid.applets.count > 1 ? tabBar.bottom : parent.top right: parent.right bottom: parent.bottom margins: units.smallSpacing From 7263c9ac15ce68ad41ce96c6663535f157d8cf84 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 16:55:23 +0200 Subject: [PATCH 34/86] s/settingsModule/settingsCommand --- .../package/contents/ui/Delegate.qml | 6 ++---- .../package/contents/ui/main.qml | 20 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/applets/quicksettings/package/contents/ui/Delegate.qml b/applets/quicksettings/package/contents/ui/Delegate.qml index 3e504b1e..7ff1d406 100644 --- a/applets/quicksettings/package/contents/ui/Delegate.qml +++ b/applets/quicksettings/package/contents/ui/Delegate.qml @@ -62,11 +62,9 @@ RowLayout { id: labelMouseArea anchors.fill: parent onClicked: { - var command = "active-settings"; - if (model.settingsModule) { - command += " -m " + model.settingsModule; + if (model.settingsCommand) { + plasmoid.nativeInterface.executeCommand(model.settingsCommand); } - plasmoid.nativeInterface.executeCommand(command) } } } diff --git a/applets/quicksettings/package/contents/ui/main.qml b/applets/quicksettings/package/contents/ui/main.qml index c1b2e089..0c0ef6f8 100644 --- a/applets/quicksettings/package/contents/ui/main.qml +++ b/applets/quicksettings/package/contents/ui/main.qml @@ -38,61 +38,61 @@ Item { text: "Settings" icon: "configure" enabled: false - settingsModule: "" + settingsCommand: "active-settings" } ListElement { text: "Mobile network" icon: "network-mobile-80" enabled: true - settingsModule: "" + settingsCommand: "" } ListElement { text: "Airplane mode" icon: "flightmode-on" enabled: false - settingsModule: "" + settingsCommand: "" } ListElement { text: "Bluetooth" icon: "preferences-system-bluetooth" enabled: false - settingsModule: "" + settingsCommand: "" } ListElement { text: "Wireless" icon: "network-wireless-on" enabled: true - settingsModule: "org.kde.satellite.settings.wifi" + settingsCommand: "active-settings -m org.kde.satellite.settings.wifi" } ListElement { text: "Alarms" icon: "korgac" enabled: false - settingsModule: "" + settingsCommand: "" } ListElement { text: "Notifications" icon: "preferences-desktop-notification" enabled: true - settingsModule: "" + settingsCommand: "" } ListElement { text: "Brightness" icon: "video-display-brightness" enabled: false - settingsModule: "org.kde.active.settings.powermanagement" + settingsCommand: "active-settings -m org.kde.active.settings.powermanagement" } ListElement { text: "Flashlight" icon: "package_games_puzzle" enabled: false - settingsModule: "" + settingsCommand: "" } ListElement { text: "Location" icon: "plasmaapplet-location" enabled: false - settingsModule: "" + settingsCommand: "" } } From 129adccee67aabc77fe15be82021086f0dd2a864 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 17:03:37 +0200 Subject: [PATCH 35/86] a mechanism for the toggle action --- applets/quicksettings/package/contents/ui/Delegate.qml | 10 +++++++++- applets/quicksettings/package/contents/ui/main.qml | 10 +++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/applets/quicksettings/package/contents/ui/Delegate.qml b/applets/quicksettings/package/contents/ui/Delegate.qml index 7ff1d406..1e3c4dfa 100644 --- a/applets/quicksettings/package/contents/ui/Delegate.qml +++ b/applets/quicksettings/package/contents/ui/Delegate.qml @@ -42,7 +42,13 @@ RowLayout { MouseArea { id: iconMouseArea anchors.fill: parent - onClicked: print("action of mouse area") + onClicked: { + if (model.toggleFunction) { + root[model.toggleFunction](); + } else if (model.settingsCommand) { + plasmoid.nativeInterface.executeCommand(model.settingsCommand); + } + } } } } @@ -64,6 +70,8 @@ RowLayout { onClicked: { if (model.settingsCommand) { plasmoid.nativeInterface.executeCommand(model.settingsCommand); + } else if (model.toggleFunction) { + root[model.toggleFunction](); } } } diff --git a/applets/quicksettings/package/contents/ui/main.qml b/applets/quicksettings/package/contents/ui/main.qml index 0c0ef6f8..e38f9806 100644 --- a/applets/quicksettings/package/contents/ui/main.qml +++ b/applets/quicksettings/package/contents/ui/main.qml @@ -25,10 +25,12 @@ import org.kde.plasma.plasmoid 2.0 Item { - id: bigClock + id: root + + function toggleAirplane() { + print("toggle airplane mode") + } - /*Layout.minimumWidth: implicitWidth - Layout.minimumHeight: implicitHeight*/ Plasmoid.preferredRepresentation: plasmoid.fullRepresentation ListModel { @@ -39,6 +41,7 @@ Item { icon: "configure" enabled: false settingsCommand: "active-settings" + toggleFunction: "" } ListElement { text: "Mobile network" @@ -51,6 +54,7 @@ Item { icon: "flightmode-on" enabled: false settingsCommand: "" + toggleFunction: "toggleAirplane" } ListElement { text: "Bluetooth" From a4af908173b21a002f098306f228f7f1137740a3 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 11 Apr 2015 18:51:28 +0200 Subject: [PATCH 36/86] a delegate for screen brightness with slider no idea if the dataengine will work with the phone --- .../contents/ui/BrightnessDelegate.qml | 75 +++++++++++++++++++ .../package/contents/ui/Delegate.qml | 10 ++- .../package/contents/ui/main.qml | 16 +++- 3 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 applets/quicksettings/package/contents/ui/BrightnessDelegate.qml diff --git a/applets/quicksettings/package/contents/ui/BrightnessDelegate.qml b/applets/quicksettings/package/contents/ui/BrightnessDelegate.qml new file mode 100644 index 00000000..0e592700 --- /dev/null +++ b/applets/quicksettings/package/contents/ui/BrightnessDelegate.qml @@ -0,0 +1,75 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + id: delegateRoot + implicitWidth: delegate.implicitWidth + implicitHeight: delegate.implicitHeight + (slider.opacity > 0 ? slider.height : 0) + + property int screenBrightness + readonly property int maximumScreenBrightness: pmSource.data["PowerDevil"] ? pmSource.data["PowerDevil"]["Maximum Screen Brightness"] || 0 : 0 + + PlasmaCore.DataSource { + id: pmSource + engine: "powermanagement" + connectedSources: ["PowerDevil"] + + onDataChanged: { + delegateRoot.screenBrightness = source.data["PowerDevil"]["Screen Brightness"]; + } + } + + Delegate { + id: delegate + toggled: slider.opacity > 0 + function toggle() { + slider.opacity = slider.opacity > 0 ? 0 : 1; + } + } + PlasmaComponents.Slider { + id: slider + anchors.top: delegate.bottom + width: flow.width + opacity: 0 + x: -delegateRoot.parent.x + value: screenBrightness + minimumValue: maximumValue > 100 ? 1 : 0 + maximumValue: delegateRoot.maximumScreenBrightness + Behavior on opacity { + OpacityAnimator { + duration: units.shortDuration + easing.type: Easing.InOutQuad + } + } + } + + onScreenBrightnessChanged: { + var service = pmSource.serviceForSource("PowerDevil"); + var operation = service.operationDescription("setBrightness"); + operation.brightness = slider.value; + operation.silent = true + service.startOperationCall(operation); + } +} + diff --git a/applets/quicksettings/package/contents/ui/Delegate.qml b/applets/quicksettings/package/contents/ui/Delegate.qml index 1e3c4dfa..5752de39 100644 --- a/applets/quicksettings/package/contents/ui/Delegate.qml +++ b/applets/quicksettings/package/contents/ui/Delegate.qml @@ -23,12 +23,14 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents RowLayout { + id: delegateRoot + property bool toggled: model.enabled spacing: units.smallSpacing - width: parent.width / 2 - units.largeSpacing / 2 + implicitWidth: flow.width / 2 - units.largeSpacing / 2 Rectangle { Layout.minimumWidth: units.iconSizes.large Layout.minimumHeight: width - color: model.enabled ? + color: toggled ? Qt.rgba(PlasmaCore.ColorScope.highlightColor.r, PlasmaCore.ColorScope.highlightColor.g, PlasmaCore.ColorScope.highlightColor.b, iconMouseArea.pressed ? 0.5 : 0.3) : Qt.rgba(PlasmaCore.ColorScope.textColor.r, PlasmaCore.ColorScope.textColor.g, PlasmaCore.ColorScope.textColor.b, iconMouseArea.pressed ? 0.5 : 0.2) @@ -43,7 +45,9 @@ RowLayout { id: iconMouseArea anchors.fill: parent onClicked: { - if (model.toggleFunction) { + if (delegateRoot.toggle) { + delegateRoot.toggle(); + } else if (model.toggleFunction) { root[model.toggleFunction](); } else if (model.settingsCommand) { plasmoid.nativeInterface.executeCommand(model.settingsCommand); diff --git a/applets/quicksettings/package/contents/ui/main.qml b/applets/quicksettings/package/contents/ui/main.qml index e38f9806..85f20ec3 100644 --- a/applets/quicksettings/package/contents/ui/main.qml +++ b/applets/quicksettings/package/contents/ui/main.qml @@ -42,6 +42,7 @@ Item { enabled: false settingsCommand: "active-settings" toggleFunction: "" + delegate: "" } ListElement { text: "Mobile network" @@ -85,6 +86,7 @@ Item { icon: "video-display-brightness" enabled: false settingsCommand: "active-settings -m org.kde.active.settings.powermanagement" + delegate: "BrightnessDelegate" } ListElement { text: "Flashlight" @@ -101,6 +103,7 @@ Item { } Flow { + id: flow anchors { fill: parent margins: units.largeSpacing @@ -108,7 +111,18 @@ Item { spacing: units.largeSpacing Repeater { model: settingsModel - delegate: Delegate {} + delegate: Loader { + width: item ? item.implicitWidth : 0 + height: item ? item.implicitHeight : 0 + source: Qt.resolvedUrl((model.delegate ? model.delegate : "Delegate") + ".qml") + } + } + move: Transition { + NumberAnimation { + duration: units.shortDuration + easing.type: Easing.InOutQuad + properties: "x,y" + } } } } From 2503593ef46607c7ebc3983493d5c87ecd73a1f6 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 17 Apr 2015 17:22:22 +0200 Subject: [PATCH 37/86] default the phone app as the first --- CMakeLists.txt | 2 +- containments/homescreen/contents/config/main.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 83a5cc6f..4927a9cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ plasma_install_package(shell org.kde.satellite.phone shells) install(DIRECTORY wallpaper/ DESTINATION "${WALLPAPER_INSTALL_DIR}/org.kde.satellite.lockers") kpackage_install_package(dialer org.kde.phone.dialer genericqml) -install(FILES dialer/metadata.desktop DESTINATION "${XDG_APPS_INSTALL_DIR}/org.kde.phone.dialer.desktop") +install(FILES dialer/metadata.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME org.kde.phone.dialer.desktop) install(DIRECTORY compositor/ DESTINATION ${DATA_INSTALL_DIR}/greenisland/org.kde.satellite.compositor.phone diff --git a/containments/homescreen/contents/config/main.xml b/containments/homescreen/contents/config/main.xml index 1f27dcef..92a9517b 100644 --- a/containments/homescreen/contents/config/main.xml +++ b/containments/homescreen/contents/config/main.xml @@ -11,6 +11,7 @@ + org.kde.phone.dialer.desktop From 9b06ffd68edbf1752d7eb28018ace37f5833a6c0 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 16:31:02 +0200 Subject: [PATCH 38/86] fix phone signal indicator --- containments/panel/contents/ui/main.qml | 11 +++++++++++ dialer/contents/ui/main.qml | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml index 7b0b75ea..f45e34fd 100644 --- a/containments/panel/contents/ui/main.qml +++ b/containments/panel/contents/ui/main.qml @@ -104,6 +104,17 @@ PlasmaCore.ColorScope { interval: 60 * 1000 } + OfonoManager { + id: ofonoManager + onAvailableChanged: { + console.log("Ofono is " + available) + } + onModemAdded: { + console.log("modem added " + modem) + } + onModemRemoved: console.log("modem removed") + } + OfonoNetworkRegistration { id: netreg Component.onCompleted: { diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index c6007c0f..bc1fa170 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -29,8 +29,8 @@ ApplicationWindow { height: 800 visible: true color: Qt.rgba(0, 0, 0, 0.9) - - OfonoManager { + + OfonoManager { id: ofonoManager onAvailableChanged: { console.log("Ofono is " + available) From 974b2fcfdabf7bdd706a238f88487c595455898c Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 16:50:51 +0200 Subject: [PATCH 39/86] fix layout --- dialer/contents/ui/Dialer.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer.qml index 30287990..1789b799 100644 --- a/dialer/contents/ui/Dialer.qml +++ b/dialer/contents/ui/Dialer.qml @@ -21,6 +21,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents import org.nemomobile.voicecall 1.0 Item { @@ -88,13 +89,12 @@ Item { fill: parent margins: 20 } - Text { + PlasmaComponents.Label { id: status Layout.fillWidth: true horizontalAlignment: Qt.AlignRight verticalAlignment: Qt.AlignVCenter font.pixelSize: one.font.pixelSize - color: textColor } Grid { @@ -196,7 +196,7 @@ Item { text: manager.activeVoiceCall ? secondsToTimeString(manager.activeVoiceCall.duration) : '' } RowLayout { - height: parent.height / 3 + Layout.minimumHeight: parent.height / 3 Layout.fillWidth: true Layout.fillHeight: true From dd1301b52227ab4ec8f4c65e03bb2dbcd2d613da Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 18:08:56 +0200 Subject: [PATCH 40/86] manager->voiceCallManager --- dialer/contents/ui/Dialer.qml | 25 ++++++++++++------------- dialer/contents/ui/main.qml | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer.qml index 1789b799..fda8a458 100644 --- a/dialer/contents/ui/Dialer.qml +++ b/dialer/contents/ui/Dialer.qml @@ -27,14 +27,13 @@ import org.nemomobile.voicecall 1.0 Item { id: dialer - state: manager.activeVoiceCall ? manager.activeVoiceCall.statusText : "disconnected" + state: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.statusText : "disconnected" property color textColor: "white" property bool calling: false // needs to be connected to a system service property bool enableButtons: calling property alias numberEntryText: status.text - property VoiceCallManager manager: root.manager - property string providerId: manager.providers.id(0) + property string providerId: voiceCallmanager.providers.id(0) function addNumber(number) { status.text = status.text + number @@ -44,13 +43,13 @@ Item { if (!calling) { console.log("Calling: " + status.text); dialer.calling = true; - manager.dial(providerId, status.text); + voiceCallmanager.dial(providerId, status.text); } else { console.log("Hanging up: " + status.text); status.text = ''; dialer.calling = false; - var call = manager.activeVoiceCall; + var call = voiceCallmanager.activeVoiceCall; if (call) { call.hangup(); } @@ -184,7 +183,7 @@ Item { verticalAlignment: Qt.AlignVCenter font.pixelSize: one.font.pixelSize color: textColor - text: manager.activeVoiceCall ? manager.activeVoiceCall.lineId : "" + text: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.lineId : "" } Text { Layout.fillWidth: true @@ -193,7 +192,7 @@ Item { verticalAlignment: Qt.AlignVCenter font.pixelSize: theme.smallestFont.pixelSize color: textColor - text: manager.activeVoiceCall ? secondsToTimeString(manager.activeVoiceCall.duration) : '' + text: voiceCallmanager.activeVoiceCall ? secondsToTimeString(voiceCallmanager.activeVoiceCall.duration) : '' } RowLayout { Layout.minimumHeight: parent.height / 3 @@ -204,7 +203,7 @@ Item { DialerIconButton { Layout.fillWidth: true Layout.fillHeight: true - source: dialer.state == "incoming" ? "call-start" : (manager.isMicrophoneMuted ? "audio-volume-muted" : "audio-volume-high") + source: dialer.state == "incoming" ? "call-start" : (voiceCallmanager.isMicrophoneMuted ? "audio-volume-muted" : "audio-volume-high") Rectangle { z: -1 color: dialer.state == "incoming" ? "green" : "white" @@ -217,11 +216,11 @@ Item { callback: function () { if (dialer.state == "incoming") { - if (manager.activeVoiceCall) { - manager.activeVoiceCall.answer(); + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.answer(); } } else { - manager.isMicrophoneMuted = !manager.isMicrophoneMuted; + voiceCallmanager.isMicrophoneMuted = !voiceCallmanager.isMicrophoneMuted; } } } @@ -241,8 +240,8 @@ Item { } callback: function () { - if (manager.activeVoiceCall) { - manager.activeVoiceCall.hangup(); + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.hangup(); } } } diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index bc1fa170..f68a546e 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -88,8 +88,8 @@ ApplicationWindow { id: netop } - property VoiceCallManager manager: VoiceCallManager { - id: manager + VoiceCallManager { + id: voiceCallmanager onActiveVoiceCallChanged: { if (activeVoiceCall) { From 0d8ce02d1aba2c4201a8597dd1b4a8f208ff71af Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 19:08:31 +0200 Subject: [PATCH 41/86] show directly call ui if server was already calling --- dialer/contents/ui/main.qml | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index f68a546e..e8feb49d 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -30,6 +30,8 @@ ApplicationWindow { visible: true color: Qt.rgba(0, 0, 0, 0.9) + property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 + OfonoManager { id: ofonoManager onAvailableChanged: { @@ -66,12 +68,6 @@ ApplicationWindow { } } - property OfonoSimManager simManager: ofonoSimManager - OfonoSimManager { - id: ofonoSimManager - modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : "" - } - OfonoNetworkRegistration { id: netreg Component.onCompleted: { @@ -87,17 +83,25 @@ ApplicationWindow { OfonoNetworkOperator { id: netop } - +Button { + z: 99 + text: "bah" + onClicked: { + print(voiceCallmanager.activeVoiceCall) + print(voiceCallmanager.voiceCalls.count) + print(voiceCallmanager.activeVoiceCall.status) + } +} VoiceCallManager { id: voiceCallmanager onActiveVoiceCallChanged: { if (activeVoiceCall) { //main.activeVoiceCallPerson = people.personByPhoneNumber(activeVoiceCall.lineId); - dialerOverlay.item.numberEntryText = activeVoiceCall.lineId; + // dialerOverlay.item.numberEntryText = activeVoiceCall.lineId; } else { - dialerOverlay.item.numberEntryText = ''; + // dialerOverlay.item.numberEntryText = ''; //main.activeVoiceCallPerson = null; } @@ -107,8 +111,18 @@ ApplicationWindow { console.log('*** QML *** VCM ERROR: ' + message); } } - Dialer { - id: dialerOverlay + + StackView { + id: stackView anchors.fill: parent } + + Component.onCompleted: { + //HACK: make sure activeVoiceCall is the proper one + voiceCallmanager.voiceCalls.onVoiceCallsChanged(); + voiceCallmanager.onActiveVoiceCallChanged(); + + //start with the dialer view + stackView.push(Qt.resolvedUrl("Dialer.qml")); + } } From 67c41ecd4d9aa9d2e66ffae8a96ea0694c486ad8 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 19:44:22 +0200 Subject: [PATCH 42/86] split the active call and dialer pages --- dialer/contents/ui/Call/CallPage.qml | 136 +++++++++++++++++++++++++++ dialer/contents/ui/Dialer.qml | 132 +------------------------- dialer/contents/ui/main.qml | 8 ++ 3 files changed, 146 insertions(+), 130 deletions(-) create mode 100644 dialer/contents/ui/Call/CallPage.qml diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/contents/ui/Call/CallPage.qml new file mode 100644 index 00000000..f43e1c1b --- /dev/null +++ b/dialer/contents/ui/Call/CallPage.qml @@ -0,0 +1,136 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.nemomobile.voicecall 1.0 + +Item { + id: callPage + + state: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.statusText : "disconnected" + property color textColor: "white" + property bool enableButtons: calling + + property string providerId: voiceCallmanager.providers.id(0) + + function secondsToTimeString(seconds) { + seconds = Math.floor(seconds/1000) + var h = Math.floor(seconds / 3600); + var m = Math.floor((seconds - (h * 3600)) / 60); + var s = seconds - h * 3600 - m * 60; + if(h < 10) h = '0' + h; + if(m < 10) m = '0' + m; + if(s < 10) s = '0' + s; + return '' + h + ':' + m + ':' + s; + } + + + ColumnLayout { + id: activeCallUi + spacing: 10 + + anchors { + fill: parent + margins: 20 + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: parent.height/2 + Rectangle { + height: Math.min(parent.width, parent.height) + width: height + radius: 5 + anchors.centerIn: parent + PlasmaCore.IconItem { + anchors { + fill: parent + centerIn: parent + margins: 20 + } + source: "im-user" + } + } + } + Text { + Layout.fillWidth: true + Layout.minimumHeight: implicitHeight + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pixelSize: one.font.pixelSize + color: textColor + text: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.lineId : "" + } + Text { + Layout.fillWidth: true + Layout.minimumHeight: implicitHeight + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + font.pixelSize: theme.smallestFont.pixelSize + color: textColor + text: voiceCallmanager.activeVoiceCall ? secondsToTimeString(voiceCallmanager.activeVoiceCall.duration) : '' + } + PlasmaComponents.ButtonRow { + exclusive: false + spacing: 0 + Layout.alignment: Qt.AlignHCenter + PlasmaComponents.ToolButton { + id: muteButton + flat: false + iconSource: voiceCallmanager.isMicrophoneMuted ? "audio-volume-muted" : "audio-volume-high" + onClicked: { + voiceCallmanager.isMicrophoneMuted = !voiceCallmanager.isMicrophoneMuted; + } + } + PlasmaComponents.ToolButton { + id: dialerButton + flat: false + iconSource: "input-keyboard" + onClicked: { + print("show dialer") + } + } + } + + + + //TODO: swipe thing instead + PlasmaComponents.Button { + text: "Answer" + onClicked: { + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.answer(); + } + } + } + PlasmaComponents.Button { + text: "Hangup" + onClicked: { + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.hangup(); + } + } + } + } +} diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer.qml index fda8a458..d9278cf3 100644 --- a/dialer/contents/ui/Dialer.qml +++ b/dialer/contents/ui/Dialer.qml @@ -27,9 +27,7 @@ import org.nemomobile.voicecall 1.0 Item { id: dialer - state: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.statusText : "disconnected" property color textColor: "white" - property bool calling: false // needs to be connected to a system service property bool enableButtons: calling property alias numberEntryText: status.text @@ -40,15 +38,13 @@ Item { } function call() { - if (!calling) { + if (!voiceCallmanager.activeVoiceCall) { console.log("Calling: " + status.text); - dialer.calling = true; voiceCallmanager.dial(providerId, status.text); } else { console.log("Hanging up: " + status.text); status.text = ''; - dialer.calling = false; var call = voiceCallmanager.activeVoiceCall; if (call) { call.hangup(); @@ -61,28 +57,8 @@ Item { status.text = "+41 76 555 5555" } - function secondsToTimeString(seconds) { - seconds = Math.floor(seconds/1000) - var h = Math.floor(seconds / 3600); - var m = Math.floor((seconds - (h * 3600)) / 60); - var s = seconds - h * 3600 - m * 60; - if(h < 10) h = '0' + h; - if(m < 10) m = '0' + m; - if(s < 10) s = '0' + s; - return '' + h + ':' + m + ':' + s; - } - - Behavior on opacity { - NumberAnimation { properties: "opacity"; duration: 100 } - } - - MouseArea { - anchors.fill: parent - } - ColumnLayout { id: dialPadArea - visible: dialer.state == "disconnected" anchors { fill: parent @@ -130,7 +106,7 @@ Item { } DialerIconButton { id: callButton - source: dialer.calling ? "call-stop" : "call-start" + source: "call-start" callback: call } DialerIconButton { @@ -138,110 +114,6 @@ Item { callback: function() { if (status.text.length > 0) { status.text = status.text.substr(0, status.text.length - 1); - } else { - dialer.calling = true; - dialer.calling = false; - } - } - } - } - } - - ColumnLayout { - id: activeCallUi - spacing: 10 - visible: dialer.state != "disconnected" - - anchors { - fill: parent - margins: 20 - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.minimumHeight: parent.height/2 - Rectangle { - height: Math.min(parent.width, parent.height) - width: height - radius: 5 - anchors.centerIn: parent - PlasmaCore.IconItem { - anchors { - fill: parent - centerIn: parent - margins: 20 - } - source: "im-user" - } - } - } - Text { - Layout.fillWidth: true - Layout.minimumHeight: implicitHeight - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - font.pixelSize: one.font.pixelSize - color: textColor - text: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.lineId : "" - } - Text { - Layout.fillWidth: true - Layout.minimumHeight: implicitHeight - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - font.pixelSize: theme.smallestFont.pixelSize - color: textColor - text: voiceCallmanager.activeVoiceCall ? secondsToTimeString(voiceCallmanager.activeVoiceCall.duration) : '' - } - RowLayout { - Layout.minimumHeight: parent.height / 3 - - Layout.fillWidth: true - Layout.fillHeight: true - - DialerIconButton { - Layout.fillWidth: true - Layout.fillHeight: true - source: dialer.state == "incoming" ? "call-start" : (voiceCallmanager.isMicrophoneMuted ? "audio-volume-muted" : "audio-volume-high") - Rectangle { - z: -1 - color: dialer.state == "incoming" ? "green" : "white" - opacity: 0.5 - radius: 5 - anchors { - fill: parent - } - } - - callback: function () { - if (dialer.state == "incoming") { - if (voiceCallmanager.activeVoiceCall) { - voiceCallmanager.activeVoiceCall.answer(); - } - } else { - voiceCallmanager.isMicrophoneMuted = !voiceCallmanager.isMicrophoneMuted; - } - } - } - - DialerIconButton { - Layout.fillWidth: true - Layout.fillHeight: true - source: "call-stop" - Rectangle { - z: -1 - color: "red" - opacity: 0.5 - radius: 5 - anchors { - fill: parent - } - } - - callback: function () { - if (voiceCallmanager.activeVoiceCall) { - voiceCallmanager.activeVoiceCall.hangup(); } } } diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index e8feb49d..9bf85b0a 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -32,6 +32,14 @@ ApplicationWindow { property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 + onStatusChanged: { + if (status > 0) { + stackView.push(Qt.resolvedUrl("Call/CallPage.qml")); + } else { + stackView.pop(); + } + } + OfonoManager { id: ofonoManager onAvailableChanged: { From b669fa97a0d6c7bb7cfc89b9be08ba0e0c079772 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 21:05:28 +0200 Subject: [PATCH 43/86] better answer/hangup ui --- dialer/contents/ui/Call/AnswerSwipe.qml | 77 +++++++++++++++++++++++++ dialer/contents/ui/Call/CallPage.qml | 58 ++++++++++++++----- dialer/contents/ui/main.qml | 44 +++++++------- 3 files changed, 140 insertions(+), 39 deletions(-) create mode 100644 dialer/contents/ui/Call/AnswerSwipe.qml diff --git a/dialer/contents/ui/Call/AnswerSwipe.qml b/dialer/contents/ui/Call/AnswerSwipe.qml new file mode 100644 index 00000000..92fb1474 --- /dev/null +++ b/dialer/contents/ui/Call/AnswerSwipe.qml @@ -0,0 +1,77 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.nemomobile.voicecall 1.0 + +MouseArea { + id: root + + signal accepted + signal rejected + + Layout.minimumHeight: units.gridUnit * 5 + Layout.fillWidth: true + property int handlePosition: (answerHandle.x + answerHandle.width/2) + drag { + target: answerHandle + axis: Drag.XAxis + minimumX: 0 + maximumX: width - answerHandle.width + } + Rectangle { + anchors.fill: parent + radius: height + color: Qt.rgba((handlePosition > root.width/2 ? 0.6 : 0)+0.2, (handlePosition < root.width/2 ? 0.6 : 0)+0.2, 0.2, Math.abs(handlePosition - (root.width/2)) / answerHandle.width/2); + Rectangle { + id: answerHandle + x: parent.width/2 - width/2 + height: parent.height + width: height + radius: width + color: Qt.rgba(0.2, 0.8, 0.2, 1) + PlasmaCore.IconItem { + source: "call-start" + width: parent.width * 0.7 + height: width + anchors.centerIn: parent + } + Behavior on x { + enabled: root.pressed + XAnimator { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } + } + onReleased: { + if (answerHandle.x <= answerHandle.width) { + root.accepted(); + } else if (answerHandle.x + answerHandle.width >= root.width - answerHandle.width) { + root.rejected(); + } + + answerHandle.x = width/2 - answerHandle.width/2 + } +} diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/contents/ui/Call/CallPage.qml index f43e1c1b..3710905f 100644 --- a/dialer/contents/ui/Call/CallPage.qml +++ b/dialer/contents/ui/Call/CallPage.qml @@ -28,8 +28,9 @@ Item { id: callPage state: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.statusText : "disconnected" + property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 + property color textColor: "white" - property bool enableButtons: calling property string providerId: voiceCallmanager.providers.id(0) @@ -78,7 +79,7 @@ Item { Layout.minimumHeight: implicitHeight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter - font.pixelSize: one.font.pixelSize + font.pixelSize: theme.defaultFont.pixelSize * 2 color: textColor text: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.lineId : "" } @@ -87,11 +88,20 @@ Item { Layout.minimumHeight: implicitHeight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter - font.pixelSize: theme.smallestFont.pixelSize color: textColor - text: voiceCallmanager.activeVoiceCall ? secondsToTimeString(voiceCallmanager.activeVoiceCall.duration) : '' + text: { + if (!voiceCallmanager.activeVoiceCall) { + return ''; + //STATUS_DIALING + } else if (voiceCallmanager.activeVoiceCall.status == 3) { + return i18n("Calling..."); + } else { + return secondsToTimeString(voiceCallmanager.activeVoiceCall.duration); + } + } } PlasmaComponents.ButtonRow { + opacity: status == 1 ? 1 : 0 exclusive: false spacing: 0 Layout.alignment: Qt.AlignHCenter @@ -114,21 +124,37 @@ Item { } + Item { + Layout.minimumHeight: units.gridUnit * 5 + Layout.fillWidth: true - //TODO: swipe thing instead - PlasmaComponents.Button { - text: "Answer" - onClicked: { - if (voiceCallmanager.activeVoiceCall) { - voiceCallmanager.activeVoiceCall.answer(); + AnswerSwipe { + anchors.fill: parent + //STATUS_INCOMING + visible: status == 5 + onAccepted: { + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.answer(); + } + } + onRejected: { + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.hangup(); + } } } - } - PlasmaComponents.Button { - text: "Hangup" - onClicked: { - if (voiceCallmanager.activeVoiceCall) { - voiceCallmanager.activeVoiceCall.hangup(); + + PlasmaComponents.Button { + anchors.fill: parent + //STATUS_INCOMING + visible: status != 5 + iconSource: "call-stop" + Layout.fillWidth: true + text: i18n("End Call") + onClicked: { + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.hangup(); + } } } } diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index 9bf85b0a..8b7feec8 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -22,24 +22,21 @@ import QtQuick.Controls 1.3 import QtQuick.Layouts 1.1 import org.nemomobile.voicecall 1.0 import MeeGo.QOfono 0.2 +import org.kde.plasma.extras 2.0 as PlasmaExtras ApplicationWindow { id: root + +//BEGIN PROPERTIES width: 600 height: 800 visible: true color: Qt.rgba(0, 0, 0, 0.9) property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 +//END PROPERTIES - onStatusChanged: { - if (status > 0) { - stackView.push(Qt.resolvedUrl("Call/CallPage.qml")); - } else { - stackView.pop(); - } - } - +//BEGIN MODELS OfonoManager { id: ofonoManager onAvailableChanged: { @@ -91,15 +88,7 @@ ApplicationWindow { OfonoNetworkOperator { id: netop } -Button { - z: 99 - text: "bah" - onClicked: { - print(voiceCallmanager.activeVoiceCall) - print(voiceCallmanager.voiceCalls.count) - print(voiceCallmanager.activeVoiceCall.status) - } -} + VoiceCallManager { id: voiceCallmanager @@ -119,18 +108,27 @@ Button { console.log('*** QML *** VCM ERROR: ' + message); } } +//END MODELS - StackView { - id: stackView +//BEGIN UI + PlasmaExtras.ConditionalLoader { anchors.fill: parent + when: root.visible && root.status == 0 + source: Qt.resolvedUrl("Dialer.qml") + opacity: root.status == 0 ? 1 : 0 } + PlasmaExtras.ConditionalLoader { + anchors.fill: parent + when: root.status > 0 + source: Qt.resolvedUrl("Call/CallPage.qml") + opacity: root.status > 0 ? 1 : 0 + } + +//END UI Component.onCompleted: { - //HACK: make sure activeVoiceCall is the proper one + //HACK: make sure activeVoiceCall is loaded if already existing voiceCallmanager.voiceCalls.onVoiceCallsChanged(); voiceCallmanager.onActiveVoiceCallChanged(); - - //start with the dialer view - stackView.push(Qt.resolvedUrl("Dialer.qml")); } } From 8842d19dd06c2270b9315e0f5fe354d339d984e5 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 21:09:41 +0200 Subject: [PATCH 44/86] dtmf tones --- dialer/contents/ui/Dialer.qml | 1 - dialer/contents/ui/DialerButton.qml | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer.qml index d9278cf3..9093bae6 100644 --- a/dialer/contents/ui/Dialer.qml +++ b/dialer/contents/ui/Dialer.qml @@ -28,7 +28,6 @@ Item { id: dialer property color textColor: "white" - property bool enableButtons: calling property alias numberEntryText: status.text property string providerId: voiceCallmanager.providers.id(0) diff --git a/dialer/contents/ui/DialerButton.qml b/dialer/contents/ui/DialerButton.qml index 784cb55f..f6bda28d 100644 --- a/dialer/contents/ui/DialerButton.qml +++ b/dialer/contents/ui/DialerButton.qml @@ -13,6 +13,9 @@ Text { MouseArea { anchors.fill: parent + onPressed: voiceCallmanager.startDtmfTone(parent.text); + onReleased: voiceCallmanager.stopDtmfTone(); + onCanceled: voiceCallmanager.stopDtmfTone(); onClicked: { if (callback) { callback(); From 25dc5015e6abd4c5266708f0ec26d9e30a45969c Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 22:46:15 +0200 Subject: [PATCH 45/86] better layout in the dialpad --- dialer/contents/ui/Call/CallPage.qml | 14 ++++---- dialer/contents/ui/Dialer.qml | 45 +++++++++++++++++-------- dialer/contents/ui/DialerButton.qml | 31 ++++++++++------- dialer/contents/ui/DialerIconButton.qml | 29 ++++++++++++---- dialer/contents/ui/main.qml | 2 +- 5 files changed, 79 insertions(+), 42 deletions(-) diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/contents/ui/Call/CallPage.qml index 3710905f..13fc5ddc 100644 --- a/dialer/contents/ui/Call/CallPage.qml +++ b/dialer/contents/ui/Call/CallPage.qml @@ -30,8 +30,6 @@ Item { state: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.statusText : "disconnected" property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 - property color textColor: "white" - property string providerId: voiceCallmanager.providers.id(0) function secondsToTimeString(seconds) { @@ -74,29 +72,29 @@ Item { } } } - Text { + PlasmaComponents.Label { Layout.fillWidth: true Layout.minimumHeight: implicitHeight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter - font.pixelSize: theme.defaultFont.pixelSize * 2 - color: textColor + font.pointSize: theme.defaultFont.pointSize * 2 text: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.lineId : "" } - Text { + PlasmaComponents.Label { Layout.fillWidth: true Layout.minimumHeight: implicitHeight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter - color: textColor text: { if (!voiceCallmanager.activeVoiceCall) { return ''; //STATUS_DIALING } else if (voiceCallmanager.activeVoiceCall.status == 3) { return i18n("Calling..."); - } else { + } else if (voiceCallmanager.activeVoiceCall.duration > 0) { return secondsToTimeString(voiceCallmanager.activeVoiceCall.duration); + } else { + return ''; } } } diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer.qml index 9093bae6..7a6b286e 100644 --- a/dialer/contents/ui/Dialer.qml +++ b/dialer/contents/ui/Dialer.qml @@ -27,7 +27,6 @@ import org.nemomobile.voicecall 1.0 Item { id: dialer - property color textColor: "white" property alias numberEntryText: status.text property string providerId: voiceCallmanager.providers.id(0) @@ -61,28 +60,28 @@ Item { anchors { fill: parent - margins: 20 + margins: units.largeSpacing } PlasmaComponents.Label { id: status Layout.fillWidth: true + Layout.minimumHeight: parent.height / 6 + Layout.maximumHeight: Layout.minimumHeight horizontalAlignment: Qt.AlignRight verticalAlignment: Qt.AlignVCenter - font.pixelSize: one.font.pixelSize + font.pointSize: 1024 + fontSizeMode: Text.Fit } - Grid { + GridLayout { id: pad columns: 3 - spacing: 0 - property int buttonHeight: height / 5 + + property int buttonHeight: parent.height / 6 Layout.fillWidth: true Layout.fillHeight: true - height: parent.height - status.height - width: parent.width - DialerButton { id: one; text: "1" } DialerButton { text: "2" } DialerButton { text: "3" } @@ -98,17 +97,35 @@ Item { DialerButton { text: "*"; } DialerButton { text: "0"; sub: "+"; } DialerButton { text: "#" } + } - DialerIconButton { - source: "im-user" - callback: fromContacts - } + RowLayout { + Layout.fillWidth: true + Layout.minimumHeight: parent.height / 6 + Layout.maximumHeight: Layout.minimumHeight DialerIconButton { id: callButton + Layout.minimumWidth: dialPadArea.width/3 + Layout.fillWidth: true + Layout.fillHeight: true + enabled: status.text.length > 0 + opacity: enabled ? 1 : 0.5 source: "call-start" callback: call } - DialerIconButton { + DialerButton { + Layout.minimumWidth: dialPadArea.width/3 + Layout.fillWidth: true + Layout.fillHeight: true + enabled: status.text.length > 0 + opacity: enabled ? 1 : 0.5 + text: i18n("Call") + callback: call + } + DialerIconButton { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumWidth: dialPadArea.width/3 source: "edit-clear" callback: function() { if (status.text.length > 0) { diff --git a/dialer/contents/ui/DialerButton.qml b/dialer/contents/ui/DialerButton.qml index f6bda28d..86954b0d 100644 --- a/dialer/contents/ui/DialerButton.qml +++ b/dialer/contents/ui/DialerButton.qml @@ -1,13 +1,20 @@ -import QtQuick 2.0 -import org.kde.plasma.core 2.0 as PlasmaCore -Text { - width: parent.width / parent.columns - height: parent.buttonHeight +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +PlasmaComponents.Label { + Layout.fillWidth: true + Layout.fillHeight: true + + //This is 0 to override the Label default height that would cause a binding loop + height: 0 horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter - color: dialer.textColor - font.pixelSize: Math.floor((width - (units.largeSpacing)) / 2) + font.pointSize: 1024 + fontSizeMode: Text.VerticalFit + property alias sub: longHold.text property var callback @@ -33,19 +40,19 @@ Text { } } - Text { + PlasmaComponents.Label { id: longHold anchors { - top: parent.top + verticalCenter: parent.verticalCenter right: parent.right } - height: parent.height + height: parent.height * 0.6 width: parent.width / 3 verticalAlignment: Qt.AlignVCenter visible: text.length > 0 opacity: 0.7 - font.pixelSize: parent.pixelSize * .8 - color: parent.color + font.pointSize: 1024 + fontSizeMode: Text.Fit } } diff --git a/dialer/contents/ui/DialerIconButton.qml b/dialer/contents/ui/DialerIconButton.qml index 254fb0e0..eb1ed1a8 100644 --- a/dialer/contents/ui/DialerIconButton.qml +++ b/dialer/contents/ui/DialerIconButton.qml @@ -1,19 +1,34 @@ import QtQuick 2.0 +import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents Item { - width: parent.width / parent.columns - height: parent.buttonHeight + id: buttonRoot + + Layout.fillWidth: true + Layout.fillHeight: true + property var callback - property string text property string sub property alias source: icon.source + property alias text: label.text - PlasmaCore.IconItem { - id: icon - width: units.iconSizes.medium - height: width + Row { anchors.centerIn: parent + PlasmaCore.IconItem { + id: icon + anchors.verticalCenter: parent.verticalCenter + width: height + height: buttonRoot.height * 0.6 + } + PlasmaComponents.Label { + id: label + height: buttonRoot.height + anchors.verticalCenter: parent.verticalCenter + font.pointSize: 1024 + fontSizeMode: Text.VerticalFit + } } MouseArea { diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index 8b7feec8..2e4e433a 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -31,7 +31,7 @@ ApplicationWindow { width: 600 height: 800 visible: true - color: Qt.rgba(0, 0, 0, 0.9) + //color: Qt.rgba(0, 0, 0, 0.9) property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 //END PROPERTIES From 130b201b2b190be8f00b07f9581a30db1d014c16 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 22:48:51 +0200 Subject: [PATCH 46/86] fade anim --- dialer/contents/ui/main.qml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index 2e4e433a..a1d3c62a 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -17,7 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.0 +import QtQuick 2.3 import QtQuick.Controls 1.3 import QtQuick.Layouts 1.1 import org.nemomobile.voicecall 1.0 @@ -116,6 +116,12 @@ ApplicationWindow { when: root.visible && root.status == 0 source: Qt.resolvedUrl("Dialer.qml") opacity: root.status == 0 ? 1 : 0 + Behavior on opacity { + OpacityAnimator { + duration: units.shortDuration + easing.type: Easing.InOutQuad + } + } } PlasmaExtras.ConditionalLoader { @@ -123,6 +129,12 @@ ApplicationWindow { when: root.status > 0 source: Qt.resolvedUrl("Call/CallPage.qml") opacity: root.status > 0 ? 1 : 0 + Behavior on opacity { + OpacityAnimator { + duration: units.shortDuration + easing.type: Easing.InOutQuad + } + } } //END UI From f776a0f1d3a682587c91e378b121b5f0fb5f35eb Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Apr 2015 22:56:24 +0200 Subject: [PATCH 47/86] more icon in the answer swipe --- dialer/contents/ui/Call/AnswerSwipe.qml | 23 +++++++++++++++++++++++ dialer/contents/ui/Dialer.qml | 8 +++----- dialer/contents/ui/main.qml | 1 + 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/dialer/contents/ui/Call/AnswerSwipe.qml b/dialer/contents/ui/Call/AnswerSwipe.qml index 92fb1474..1bd235a5 100644 --- a/dialer/contents/ui/Call/AnswerSwipe.qml +++ b/dialer/contents/ui/Call/AnswerSwipe.qml @@ -43,6 +43,28 @@ MouseArea { anchors.fill: parent radius: height color: Qt.rgba((handlePosition > root.width/2 ? 0.6 : 0)+0.2, (handlePosition < root.width/2 ? 0.6 : 0)+0.2, 0.2, Math.abs(handlePosition - (root.width/2)) / answerHandle.width/2); + + PlasmaCore.IconItem { + source: "call-start" + width: icon.width + height: width + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: units.largeSpacing + } + } + PlasmaCore.IconItem { + source: "call-stop" + width: icon.width + height: width + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + leftMargin: units.largeSpacing + } + } + Rectangle { id: answerHandle x: parent.width/2 - width/2 @@ -51,6 +73,7 @@ MouseArea { radius: width color: Qt.rgba(0.2, 0.8, 0.2, 1) PlasmaCore.IconItem { + id: icon source: "call-start" width: parent.width * 0.7 height: width diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer.qml index 7a6b286e..02ec4e05 100644 --- a/dialer/contents/ui/Dialer.qml +++ b/dialer/contents/ui/Dialer.qml @@ -113,19 +113,17 @@ Item { source: "call-start" callback: call } - DialerButton { + Item { Layout.minimumWidth: dialPadArea.width/3 Layout.fillWidth: true Layout.fillHeight: true - enabled: status.text.length > 0 - opacity: enabled ? 1 : 0.5 - text: i18n("Call") - callback: call } DialerIconButton { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumWidth: dialPadArea.width/3 + enabled: status.text.length > 0 + opacity: enabled ? 1 : 0.5 source: "edit-clear" callback: function() { if (status.text.length > 0) { diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index a1d3c62a..17bc24cb 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -22,6 +22,7 @@ import QtQuick.Controls 1.3 import QtQuick.Layouts 1.1 import org.nemomobile.voicecall 1.0 import MeeGo.QOfono 0.2 +import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras ApplicationWindow { From 78156bd6263082a33549e60fdc530689c20440f0 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 09:53:35 +0200 Subject: [PATCH 48/86] separe dialer and dialpad --- dialer/contents/ui/{ => Dialer}/Dialer.qml | 27 ++-------- .../ui/{ => Dialpad}/DialerButton.qml | 19 +++++++ .../ui/{ => Dialpad}/DialerIconButton.qml | 20 ++++++++ dialer/contents/ui/Dialpad/Dialpad.qml | 51 +++++++++++++++++++ dialer/contents/ui/main.qml | 2 +- 5 files changed, 94 insertions(+), 25 deletions(-) rename dialer/contents/ui/{ => Dialer}/Dialer.qml (83%) rename dialer/contents/ui/{ => Dialpad}/DialerButton.qml (63%) rename dialer/contents/ui/{ => Dialpad}/DialerIconButton.qml (59%) create mode 100644 dialer/contents/ui/Dialpad/Dialpad.qml diff --git a/dialer/contents/ui/Dialer.qml b/dialer/contents/ui/Dialer/Dialer.qml similarity index 83% rename from dialer/contents/ui/Dialer.qml rename to dialer/contents/ui/Dialer/Dialer.qml index 02ec4e05..d76c4022 100644 --- a/dialer/contents/ui/Dialer.qml +++ b/dialer/contents/ui/Dialer/Dialer.qml @@ -23,6 +23,7 @@ import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.nemomobile.voicecall 1.0 +import "../Dialpad" Item { id: dialer @@ -73,30 +74,8 @@ Item { fontSizeMode: Text.Fit } - GridLayout { - id: pad - columns: 3 - - property int buttonHeight: parent.height / 6 - - Layout.fillWidth: true - Layout.fillHeight: true - - DialerButton { id: one; text: "1" } - DialerButton { text: "2" } - DialerButton { text: "3" } - - DialerButton { text: "4" } - DialerButton { text: "5" } - DialerButton { text: "6" } - - DialerButton { text: "7" } - DialerButton { text: "8" } - DialerButton { text: "9" } - - DialerButton { text: "*"; } - DialerButton { text: "0"; sub: "+"; } - DialerButton { text: "#" } + Dialpad { + } RowLayout { diff --git a/dialer/contents/ui/DialerButton.qml b/dialer/contents/ui/Dialpad/DialerButton.qml similarity index 63% rename from dialer/contents/ui/DialerButton.qml rename to dialer/contents/ui/Dialpad/DialerButton.qml index 86954b0d..f388449e 100644 --- a/dialer/contents/ui/DialerButton.qml +++ b/dialer/contents/ui/Dialpad/DialerButton.qml @@ -1,3 +1,22 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ import QtQuick 2.0 import QtQuick.Layouts 1.1 diff --git a/dialer/contents/ui/DialerIconButton.qml b/dialer/contents/ui/Dialpad/DialerIconButton.qml similarity index 59% rename from dialer/contents/ui/DialerIconButton.qml rename to dialer/contents/ui/Dialpad/DialerIconButton.qml index eb1ed1a8..19afb872 100644 --- a/dialer/contents/ui/DialerIconButton.qml +++ b/dialer/contents/ui/Dialpad/DialerIconButton.qml @@ -1,3 +1,23 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore diff --git a/dialer/contents/ui/Dialpad/Dialpad.qml b/dialer/contents/ui/Dialpad/Dialpad.qml new file mode 100644 index 00000000..b5baaecd --- /dev/null +++ b/dialer/contents/ui/Dialpad/Dialpad.qml @@ -0,0 +1,51 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +GridLayout { + id: pad + columns: 3 + + property int buttonHeight: parent.height / 6 + + Layout.fillWidth: true + Layout.fillHeight: true + + DialerButton { id: one; text: "1" } + DialerButton { text: "2" } + DialerButton { text: "3" } + + DialerButton { text: "4" } + DialerButton { text: "5" } + DialerButton { text: "6" } + + DialerButton { text: "7" } + DialerButton { text: "8" } + DialerButton { text: "9" } + + DialerButton { text: "*"; } + DialerButton { text: "0"; sub: "+"; } + DialerButton { text: "#" } +} + diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index 17bc24cb..40a33bd4 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -115,7 +115,7 @@ ApplicationWindow { PlasmaExtras.ConditionalLoader { anchors.fill: parent when: root.visible && root.status == 0 - source: Qt.resolvedUrl("Dialer.qml") + source: Qt.resolvedUrl("Dialer/Dialer.qml") opacity: root.status == 0 ? 1 : 0 Behavior on opacity { OpacityAnimator { From 0e78af4239ed05a31b08ba43179492e46a43ca22 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 09:56:28 +0200 Subject: [PATCH 49/86] separe the avatar component --- dialer/contents/ui/Call/Avatar.qml | 45 ++++++++++++++++++++++++++++ dialer/contents/ui/Call/CallPage.qml | 21 ++----------- 2 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 dialer/contents/ui/Call/Avatar.qml diff --git a/dialer/contents/ui/Call/Avatar.qml b/dialer/contents/ui/Call/Avatar.qml new file mode 100644 index 00000000..d27bd6dc --- /dev/null +++ b/dialer/contents/ui/Call/Avatar.qml @@ -0,0 +1,45 @@ +/* + * Copyright 2014 Aaron Seigo + * Copyright 2014 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + + +Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: parent.height/2 + Rectangle { + height: Math.min(parent.width, parent.height) + width: height + radius: 5 + anchors.centerIn: parent + PlasmaCore.IconItem { + anchors { + fill: parent + centerIn: parent + margins: 20 + } + source: "im-user" + } + } +} diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/contents/ui/Call/CallPage.qml index 13fc5ddc..42743d9e 100644 --- a/dialer/contents/ui/Call/CallPage.qml +++ b/dialer/contents/ui/Call/CallPage.qml @@ -53,25 +53,8 @@ Item { margins: 20 } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.minimumHeight: parent.height/2 - Rectangle { - height: Math.min(parent.width, parent.height) - width: height - radius: 5 - anchors.centerIn: parent - PlasmaCore.IconItem { - anchors { - fill: parent - centerIn: parent - margins: 20 - } - source: "im-user" - } - } - } + Avatar {} + PlasmaComponents.Label { Layout.fillWidth: true Layout.minimumHeight: implicitHeight From 63300072c4e30e5d805d8539fa20cafb38a79c5e Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 10:08:37 +0200 Subject: [PATCH 50/86] optional dialer in the call page --- dialer/contents/ui/Call/CallPage.qml | 52 ++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/contents/ui/Call/CallPage.qml index 42743d9e..e8ad0627 100644 --- a/dialer/contents/ui/Call/CallPage.qml +++ b/dialer/contents/ui/Call/CallPage.qml @@ -24,6 +24,8 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.nemomobile.voicecall 1.0 +import "../Dialpad" + Item { id: callPage @@ -53,7 +55,45 @@ Item { margins: 20 } - Avatar {} + Flickable { + id: topFlickable + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: parent.height/2 + + contentWidth: topContents.width; + contentHeight: topContents.height + Row { + id: topContents + Avatar { + width: topFlickable.width + height: topFlickable.height + } + Dialpad { + width: topFlickable.width + height: topFlickable.height + } + } + + onMovingChanged: { + var checked = contentX > topFlickable.width/2; + + if (checked) { + topSlideAnim.to = topFlickable.width; + } else { + topSlideAnim.to = 0; + } + dialerButton.checked = checked; + topSlideAnim.running = true; + } + PropertyAnimation { + id: topSlideAnim + target: topFlickable + properties: "contentX" + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } PlasmaComponents.Label { Layout.fillWidth: true @@ -98,8 +138,14 @@ Item { id: dialerButton flat: false iconSource: "input-keyboard" - onClicked: { - print("show dialer") + checkable: true + onCheckedChanged: { + if (checked) { + topSlideAnim.to = topFlickable.width; + } else { + topSlideAnim.to = 0; + } + topSlideAnim.running = true; } } } From ad195790e84f2e03408f16b10fe7b0e38eab77d8 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 10:54:30 +0200 Subject: [PATCH 51/86] change callback a bit make the keypad on call ui send dtmf on the call --- dialer/contents/ui/Call/CallPage.qml | 14 +++++- dialer/contents/ui/Dialer/Dialer.qml | 17 ++++--- dialer/contents/ui/Dialpad/DialerButton.qml | 44 +++++++++++++++---- .../contents/ui/Dialpad/DialerIconButton.qml | 17 +++++-- dialer/contents/ui/Dialpad/Dialpad.qml | 4 ++ dialer/contents/ui/main.qml | 2 + 6 files changed, 78 insertions(+), 20 deletions(-) diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/contents/ui/Call/CallPage.qml index e8ad0627..4aee2e9d 100644 --- a/dialer/contents/ui/Call/CallPage.qml +++ b/dialer/contents/ui/Call/CallPage.qml @@ -45,6 +45,11 @@ Item { return '' + h + ':' + m + ':' + s; } + onStatusChanged: { + if (status != 1) { + dialerButton.checked = false; + } + } ColumnLayout { id: activeCallUi @@ -61,8 +66,9 @@ Item { Layout.fillHeight: true Layout.minimumHeight: parent.height/2 - contentWidth: topContents.width; + contentWidth: topContents.width contentHeight: topContents.height + interactive: status == 1; Row { id: topContents Avatar { @@ -72,6 +78,12 @@ Item { Dialpad { width: topFlickable.width height: topFlickable.height + + callback: function (string) { + if (voiceCallmanager.activeVoiceCall) { + voiceCallmanager.activeVoiceCall.sendDtmf(string); + } + } } } diff --git a/dialer/contents/ui/Dialer/Dialer.qml b/dialer/contents/ui/Dialer/Dialer.qml index d76c4022..42214bd1 100644 --- a/dialer/contents/ui/Dialer/Dialer.qml +++ b/dialer/contents/ui/Dialer/Dialer.qml @@ -51,11 +51,6 @@ Item { } } - function fromContacts() { - console.log("Should get from contacts!"); - status.text = "+41 76 555 5555" - } - ColumnLayout { id: dialPadArea @@ -75,7 +70,15 @@ Item { } Dialpad { - + callback: function (string) { + addNumber(string); + } + pressedCallback: function (string) { + voiceCallmanager.startDtmfTone(string); + } + releasedCallback: function (string) { + voiceCallmanager.stopDtmfTone(); + } } RowLayout { @@ -104,7 +107,7 @@ Item { enabled: status.text.length > 0 opacity: enabled ? 1 : 0.5 source: "edit-clear" - callback: function() { + callback: function(text) { if (status.text.length > 0) { status.text = status.text.substr(0, status.text.length - 1); } diff --git a/dialer/contents/ui/Dialpad/DialerButton.qml b/dialer/contents/ui/Dialpad/DialerButton.qml index f388449e..b3a6c770 100644 --- a/dialer/contents/ui/Dialpad/DialerButton.qml +++ b/dialer/contents/ui/Dialpad/DialerButton.qml @@ -36,25 +36,53 @@ PlasmaComponents.Label { property alias sub: longHold.text property var callback + property var pressedCallback + property var releasedCallback MouseArea { anchors.fill: parent - onPressed: voiceCallmanager.startDtmfTone(parent.text); - onReleased: voiceCallmanager.stopDtmfTone(); - onCanceled: voiceCallmanager.stopDtmfTone(); + onPressed: { + if (pressedCallback) { + pressedCallback(parent.text); + } else if (pad.pressedCallback) { + pad.pressedCallback(parent.text); + } + } + onReleased: { + if (releasedCallback) { + releasedCallback(parent.text); + } else if (pad.releasedCallback) { + pad.releasedCallback(parent.text); + } + } + onCanceled: { + if (releasedCallback) { + releasedCallback(parent.text); + } else if (pad.releasedCallback) { + pad.releasedCallback(parent.text); + } + } + onClicked: { if (callback) { - callback(); - } else { - addNumber(parent.text); + callback(parent.text); + } else if (pad.callback) { + pad.callback(parent.text); } } onPressAndHold: { + var text; if (longHold.visible) { - addNumber(longHold.text); + text = longHold.text; } else { - addNumber(parent.text); + text = parent.text; + } + + if (callback) { + callback(text); + } else if (pad.callback) { + pad.callback(text); } } } diff --git a/dialer/contents/ui/Dialpad/DialerIconButton.qml b/dialer/contents/ui/Dialpad/DialerIconButton.qml index 19afb872..29be828b 100644 --- a/dialer/contents/ui/Dialpad/DialerIconButton.qml +++ b/dialer/contents/ui/Dialpad/DialerIconButton.qml @@ -30,6 +30,8 @@ Item { Layout.fillHeight: true property var callback + property var pressedCallback + property var releasedCallback property string sub property alias source: icon.source property alias text: label.text @@ -55,17 +57,24 @@ Item { anchors.fill: parent onClicked: { if (callback) { - callback(); + callback(parent.text); } else { addNumber(parent.text); } } onPressAndHold: { - if (parent.sub.length > 0) { - addNumber(parent.sub); + var text; + if (longHold.visible) { + text = longHold.text; } else { - addNumber(parent.text); + text = parent.text; + } + + if (callback) { + callback(text); + } else if (pad.callback) { + pad.callback(text); } } } diff --git a/dialer/contents/ui/Dialpad/Dialpad.qml b/dialer/contents/ui/Dialpad/Dialpad.qml index b5baaecd..1303be73 100644 --- a/dialer/contents/ui/Dialpad/Dialpad.qml +++ b/dialer/contents/ui/Dialpad/Dialpad.qml @@ -27,6 +27,10 @@ GridLayout { id: pad columns: 3 + property var callback + property var pressedCallback + property var releasedCallback + property int buttonHeight: parent.height / 6 Layout.fillWidth: true diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index 40a33bd4..37daea13 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -116,6 +116,7 @@ ApplicationWindow { anchors.fill: parent when: root.visible && root.status == 0 source: Qt.resolvedUrl("Dialer/Dialer.qml") + z: root.status == 0 ? 2 : 0 opacity: root.status == 0 ? 1 : 0 Behavior on opacity { OpacityAnimator { @@ -130,6 +131,7 @@ ApplicationWindow { when: root.status > 0 source: Qt.resolvedUrl("Call/CallPage.qml") opacity: root.status > 0 ? 1 : 0 + z: root.status > 0 ? 2 : 0 Behavior on opacity { OpacityAnimator { duration: units.shortDuration From 6024e054b72585eb55bf3468fc84cc654d20346a Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 10:57:37 +0200 Subject: [PATCH 52/86] fix copyright --- dialer/contents/ui/Call/AnswerSwipe.qml | 3 +-- dialer/contents/ui/Call/Avatar.qml | 3 +-- dialer/contents/ui/Call/CallPage.qml | 2 +- dialer/contents/ui/main.qml | 3 ++- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dialer/contents/ui/Call/AnswerSwipe.qml b/dialer/contents/ui/Call/AnswerSwipe.qml index 1bd235a5..278c3824 100644 --- a/dialer/contents/ui/Call/AnswerSwipe.qml +++ b/dialer/contents/ui/Call/AnswerSwipe.qml @@ -1,6 +1,5 @@ /* - * Copyright 2014 Aaron Seigo - * Copyright 2014 Marco Martin + * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as diff --git a/dialer/contents/ui/Call/Avatar.qml b/dialer/contents/ui/Call/Avatar.qml index d27bd6dc..1c138601 100644 --- a/dialer/contents/ui/Call/Avatar.qml +++ b/dialer/contents/ui/Call/Avatar.qml @@ -1,6 +1,5 @@ /* - * Copyright 2014 Aaron Seigo - * Copyright 2014 Marco Martin + * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/contents/ui/Call/CallPage.qml index 4aee2e9d..e6032306 100644 --- a/dialer/contents/ui/Call/CallPage.qml +++ b/dialer/contents/ui/Call/CallPage.qml @@ -1,6 +1,6 @@ /* * Copyright 2014 Aaron Seigo - * Copyright 2014 Marco Martin + * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index 37daea13..ff8327b5 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -1,4 +1,5 @@ -/* +/** + * Copyright 2014 Aaron Seigo * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or modify From 590db2f678e229f9f92bcbb3e4c615cb0f7ff13e Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 11:23:31 +0200 Subject: [PATCH 53/86] dummy pages for history and contacts --- dialer/contents/ui/Dialer/Contacts.qml | 30 ++++++++++++ dialer/contents/ui/Dialer/DialPage.qml | 66 ++++++++++++++++++++++++++ dialer/contents/ui/Dialer/History.qml | 30 ++++++++++++ dialer/contents/ui/main.qml | 2 +- 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 dialer/contents/ui/Dialer/Contacts.qml create mode 100644 dialer/contents/ui/Dialer/DialPage.qml create mode 100644 dialer/contents/ui/Dialer/History.qml diff --git a/dialer/contents/ui/Dialer/Contacts.qml b/dialer/contents/ui/Dialer/Contacts.qml new file mode 100644 index 00000000..541aec5a --- /dev/null +++ b/dialer/contents/ui/Dialer/Contacts.qml @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + PlasmaComponents.Label { + anchors.centerIn: parent + text: i18n("No contacts") + } +} \ No newline at end of file diff --git a/dialer/contents/ui/Dialer/DialPage.qml b/dialer/contents/ui/Dialer/DialPage.qml new file mode 100644 index 00000000..d832a765 --- /dev/null +++ b/dialer/contents/ui/Dialer/DialPage.qml @@ -0,0 +1,66 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Column { + spacing: 0 + PlasmaComponents.TabGroup { + anchors { + left: parent.left + right: parent.right + } + height: parent.height - tabbar.height + History { + id: history + } + Contacts { + id: contacts + } + Dialer { + id: dialer + } + } + PlasmaComponents.TabBar { + id: tabbar + height: units.gridUnit * 5 + anchors { + horizontalCenter: parent.horizontalCenter + } + tabPosition: Qt.BottomEdge + PlasmaComponents.TabButton { + iconSource: "view-history" + text: i18n("History") + tab: history + } + PlasmaComponents.TabButton { + iconSource: "view-pim-contacts" + text: i18n("Contacts") + tab: contacts + } + PlasmaComponents.TabButton { + iconSource: "input-keyboard" + text: i18n("Dialpad") + tab: dialer + } + } +} \ No newline at end of file diff --git a/dialer/contents/ui/Dialer/History.qml b/dialer/contents/ui/Dialer/History.qml new file mode 100644 index 00000000..4b7a9c1b --- /dev/null +++ b/dialer/contents/ui/Dialer/History.qml @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + PlasmaComponents.Label { + anchors.centerIn: parent + text: i18n("No recent calls") + } +} \ No newline at end of file diff --git a/dialer/contents/ui/main.qml b/dialer/contents/ui/main.qml index ff8327b5..fd17930b 100644 --- a/dialer/contents/ui/main.qml +++ b/dialer/contents/ui/main.qml @@ -116,7 +116,7 @@ ApplicationWindow { PlasmaExtras.ConditionalLoader { anchors.fill: parent when: root.visible && root.status == 0 - source: Qt.resolvedUrl("Dialer/Dialer.qml") + source: Qt.resolvedUrl("Dialer/DialPage.qml") z: root.status == 0 ? 2 : 0 opacity: root.status == 0 ? 1 : 0 Behavior on opacity { From c162599b95e16ce57c1903d08cf98c2a0001d763 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 11:28:29 +0200 Subject: [PATCH 54/86] move dialer in a subdir, prepare for the c++ part --- CMakeLists.txt | 4 +--- dialer/CMakeLists.txt | 3 +++ dialer/{ => package}/contents/ui/Call/AnswerSwipe.qml | 0 dialer/{ => package}/contents/ui/Call/Avatar.qml | 0 dialer/{ => package}/contents/ui/Call/CallPage.qml | 0 dialer/{ => package}/contents/ui/Dialer/Contacts.qml | 0 dialer/{ => package}/contents/ui/Dialer/DialPage.qml | 0 dialer/{ => package}/contents/ui/Dialer/Dialer.qml | 0 dialer/{ => package}/contents/ui/Dialer/History.qml | 0 dialer/{ => package}/contents/ui/Dialpad/DialerButton.qml | 0 dialer/{ => package}/contents/ui/Dialpad/DialerIconButton.qml | 0 dialer/{ => package}/contents/ui/Dialpad/Dialpad.qml | 0 dialer/{ => package}/contents/ui/main.qml | 0 dialer/{ => package}/metadata.desktop | 0 14 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 dialer/CMakeLists.txt rename dialer/{ => package}/contents/ui/Call/AnswerSwipe.qml (100%) rename dialer/{ => package}/contents/ui/Call/Avatar.qml (100%) rename dialer/{ => package}/contents/ui/Call/CallPage.qml (100%) rename dialer/{ => package}/contents/ui/Dialer/Contacts.qml (100%) rename dialer/{ => package}/contents/ui/Dialer/DialPage.qml (100%) rename dialer/{ => package}/contents/ui/Dialer/Dialer.qml (100%) rename dialer/{ => package}/contents/ui/Dialer/History.qml (100%) rename dialer/{ => package}/contents/ui/Dialpad/DialerButton.qml (100%) rename dialer/{ => package}/contents/ui/Dialpad/DialerIconButton.qml (100%) rename dialer/{ => package}/contents/ui/Dialpad/Dialpad.qml (100%) rename dialer/{ => package}/contents/ui/main.qml (100%) rename dialer/{ => package}/metadata.desktop (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4927a9cd..aa8d86b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,9 +37,6 @@ plasma_install_package(look-and-feel org.kde.satellite.phone look-and-feel) plasma_install_package(shell org.kde.satellite.phone shells) install(DIRECTORY wallpaper/ DESTINATION "${WALLPAPER_INSTALL_DIR}/org.kde.satellite.lockers") -kpackage_install_package(dialer org.kde.phone.dialer genericqml) -install(FILES dialer/metadata.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME org.kde.phone.dialer.desktop) - install(DIRECTORY compositor/ DESTINATION ${DATA_INSTALL_DIR}/greenisland/org.kde.satellite.compositor.phone PATTERN .svn EXCLUDE @@ -53,3 +50,4 @@ add_subdirectory(services) add_subdirectory(settingsmodules) add_subdirectory(applets) add_subdirectory(containments) +add_subdirectory(dialer) diff --git a/dialer/CMakeLists.txt b/dialer/CMakeLists.txt new file mode 100644 index 00000000..c2adad8f --- /dev/null +++ b/dialer/CMakeLists.txt @@ -0,0 +1,3 @@ + +kpackage_install_package(package org.kde.phone.dialer genericqml) +install(FILES package/metadata.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME org.kde.phone.dialer.desktop) diff --git a/dialer/contents/ui/Call/AnswerSwipe.qml b/dialer/package/contents/ui/Call/AnswerSwipe.qml similarity index 100% rename from dialer/contents/ui/Call/AnswerSwipe.qml rename to dialer/package/contents/ui/Call/AnswerSwipe.qml diff --git a/dialer/contents/ui/Call/Avatar.qml b/dialer/package/contents/ui/Call/Avatar.qml similarity index 100% rename from dialer/contents/ui/Call/Avatar.qml rename to dialer/package/contents/ui/Call/Avatar.qml diff --git a/dialer/contents/ui/Call/CallPage.qml b/dialer/package/contents/ui/Call/CallPage.qml similarity index 100% rename from dialer/contents/ui/Call/CallPage.qml rename to dialer/package/contents/ui/Call/CallPage.qml diff --git a/dialer/contents/ui/Dialer/Contacts.qml b/dialer/package/contents/ui/Dialer/Contacts.qml similarity index 100% rename from dialer/contents/ui/Dialer/Contacts.qml rename to dialer/package/contents/ui/Dialer/Contacts.qml diff --git a/dialer/contents/ui/Dialer/DialPage.qml b/dialer/package/contents/ui/Dialer/DialPage.qml similarity index 100% rename from dialer/contents/ui/Dialer/DialPage.qml rename to dialer/package/contents/ui/Dialer/DialPage.qml diff --git a/dialer/contents/ui/Dialer/Dialer.qml b/dialer/package/contents/ui/Dialer/Dialer.qml similarity index 100% rename from dialer/contents/ui/Dialer/Dialer.qml rename to dialer/package/contents/ui/Dialer/Dialer.qml diff --git a/dialer/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml similarity index 100% rename from dialer/contents/ui/Dialer/History.qml rename to dialer/package/contents/ui/Dialer/History.qml diff --git a/dialer/contents/ui/Dialpad/DialerButton.qml b/dialer/package/contents/ui/Dialpad/DialerButton.qml similarity index 100% rename from dialer/contents/ui/Dialpad/DialerButton.qml rename to dialer/package/contents/ui/Dialpad/DialerButton.qml diff --git a/dialer/contents/ui/Dialpad/DialerIconButton.qml b/dialer/package/contents/ui/Dialpad/DialerIconButton.qml similarity index 100% rename from dialer/contents/ui/Dialpad/DialerIconButton.qml rename to dialer/package/contents/ui/Dialpad/DialerIconButton.qml diff --git a/dialer/contents/ui/Dialpad/Dialpad.qml b/dialer/package/contents/ui/Dialpad/Dialpad.qml similarity index 100% rename from dialer/contents/ui/Dialpad/Dialpad.qml rename to dialer/package/contents/ui/Dialpad/Dialpad.qml diff --git a/dialer/contents/ui/main.qml b/dialer/package/contents/ui/main.qml similarity index 100% rename from dialer/contents/ui/main.qml rename to dialer/package/contents/ui/main.qml diff --git a/dialer/metadata.desktop b/dialer/package/metadata.desktop similarity index 100% rename from dialer/metadata.desktop rename to dialer/package/metadata.desktop From fc047fd462d32e45c88e633f2b55953cd2601f1e Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 12:12:10 +0200 Subject: [PATCH 55/86] c++ app with daemon option the app never closes, reinvoking it makes it show --- CMakeLists.txt | 2 +- dialer/CMakeLists.txt | 2 + dialer/package/contents/ui/main.qml | 3 +- dialer/package/metadata.desktop | 2 +- dialer/src/CMakeLists.txt | 23 ++++++ dialer/src/main.cpp | 106 ++++++++++++++++++++++++++++ 6 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 dialer/src/CMakeLists.txt create mode 100644 dialer/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index aa8d86b2..71f6b94e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Gui Widgets Qml Quick) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n) -find_package(KF5 REQUIRED COMPONENTS PlasmaQuick) +find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/dialer/CMakeLists.txt b/dialer/CMakeLists.txt index c2adad8f..9927f95e 100644 --- a/dialer/CMakeLists.txt +++ b/dialer/CMakeLists.txt @@ -1,3 +1,5 @@ kpackage_install_package(package org.kde.phone.dialer genericqml) install(FILES package/metadata.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME org.kde.phone.dialer.desktop) + +add_subdirectory(src) diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index fd17930b..d7f611e0 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -32,8 +32,7 @@ ApplicationWindow { //BEGIN PROPERTIES width: 600 height: 800 - visible: true - //color: Qt.rgba(0, 0, 0, 0.9) + property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 //END PROPERTIES diff --git a/dialer/package/metadata.desktop b/dialer/package/metadata.desktop index cf835d33..54766985 100644 --- a/dialer/package/metadata.desktop +++ b/dialer/package/metadata.desktop @@ -14,7 +14,7 @@ X-KDE-PluginInfo-Name=org.kde.phone.dialer X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-KDE-ServiceTypes=KPackage/Generic -Exec=kpackagelauncherqml -a org.kde.phone.dialer +Exec=plasmaphonedialer X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= diff --git a/dialer/src/CMakeLists.txt b/dialer/src/CMakeLists.txt new file mode 100644 index 00000000..f430c3f6 --- /dev/null +++ b/dialer/src/CMakeLists.txt @@ -0,0 +1,23 @@ + + +set(plasmaphonedialer_SRCS + main.cpp +) + +add_executable(plasmaphonedialer ${plasmaphonedialer_SRCS}) +target_compile_definitions(plasmaphonedialer PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") + +#find_package(ActiveApp REQUIRED) + +target_link_libraries(plasmaphonedialer + Qt5::Gui + Qt5::Quick + Qt5::Widgets + KF5::Declarative + KF5::I18n + KF5::Package + KF5::QuickAddons + KF5::DBusAddons +) + +install(TARGETS plasmaphonedialer ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/dialer/src/main.cpp b/dialer/src/main.cpp new file mode 100644 index 00000000..8b7862ce --- /dev/null +++ b/dialer/src/main.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + QCommandLineParser parser; + QApplication app(argc, argv); + + app.setQuitOnLastWindowClosed(false); + KDBusService service(KDBusService::Unique); + + const QString description = i18n("Plasma Phone Dialer"); + const char version[] = PROJECT_VERSION; + + app.setApplicationVersion(version); + parser.addVersionOption(); + parser.addHelpOption(); + parser.setApplicationDescription(description); + + QCommandLineOption daemonOption("daemon", "Daemon mode. run without displaying anything."); + + parser.addOption(daemonOption); + + parser.process(app); + + const QString packagePath("org.kde.phone.dialer"); + + //usually we have an ApplicationWindow here, so we do not need to create a window by ourselves + KDeclarative::QmlObject *obj = new KDeclarative::QmlObject(); + obj->setTranslationDomain(packagePath); + obj->setInitializationDelayed(true); + obj->loadPackage(packagePath); + obj->engine()->rootContext()->setContextProperty("commandlineArguments", parser.positionalArguments()); + + obj->completeInitialization(); + + if (!obj->package().metadata().isValid()) { + return -1; + } + + KPluginMetaData data = obj->package().metadata(); + // About data + KAboutData aboutData(data.pluginId(), data.name(), data.version(), data.description(), KAboutLicense::byKeyword(data.license()).key()); + + for (auto author : data.authors()) { + aboutData.addAuthor(author.name(), author.task(), author.emailAddress(), author.webAddress(), author.ocsUsername()); + } + + //The root is not a window? + //have to use a normal QQuickWindow since the root item is already created + QWindow *window = qobject_cast(obj->rootObject()); + if (window) { + QObject::connect(&service, &KDBusService::activateRequested, [=](const QStringList &arguments, const QString &workingDirectory) { + Q_UNUSED(arguments) + Q_UNUSED(workingDirectory); + window->show(); + window->requestActivate(); + }); + if (!parser.isSet(daemonOption)) { + window->show(); + window->requestActivate(); + } + window->setTitle(obj->package().metadata().name()); + window->setIcon(QIcon::fromTheme(obj->package().metadata().iconName())); + } else { + qWarning() << "Error loading the ApplicationWindow"; + } + + return app.exec(); +} + From 2081b4d6281c23719811b5bd197ff2e954c0c62a Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 12:28:14 +0200 Subject: [PATCH 56/86] how the window when the phone is ringing --- dialer/package/contents/ui/main.qml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index d7f611e0..10726900 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -37,6 +37,15 @@ ApplicationWindow { property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 //END PROPERTIES +//BEGIN SIGNAL HANDLERS + onStatusChanged: { + //STATUS_INCOMING + if (status == 5) { + root.visible = true; + } + } +//END SIGNAL HANDLERS + //BEGIN MODELS OfonoManager { id: ofonoManager From c91da664d3933929a363b0cf74bdacd362c7831c Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 21 Apr 2015 14:14:33 +0200 Subject: [PATCH 57/86] small test app for the app model --- CMakeLists.txt | 2 +- qmlcomponents/CMakeLists.txt | 2 +- qmlcomponents/applicationlistmodel.cpp | 12 + qmlcomponents/applicationlistmodel.h | 4 + qmlcomponents/modeltest/CMakeLists.txt | 15 + qmlcomponents/modeltest/main.cpp | 54 +++ qmlcomponents/modeltest/modeltest.cpp | 592 +++++++++++++++++++++++++ qmlcomponents/modeltest/modeltest.h | 88 ++++ 8 files changed, 767 insertions(+), 2 deletions(-) create mode 100644 qmlcomponents/modeltest/CMakeLists.txt create mode 100644 qmlcomponents/modeltest/main.cpp create mode 100644 qmlcomponents/modeltest/modeltest.cpp create mode 100644 qmlcomponents/modeltest/modeltest.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 71f6b94e..e496cb93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ include(ECMGenerateHeaders) include(GenerateExportHeader) include(FeatureSummary) -find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Gui Widgets Qml Quick) +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Gui Widgets Qml Quick Test) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n) find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons) diff --git a/qmlcomponents/CMakeLists.txt b/qmlcomponents/CMakeLists.txt index b25c4777..0532a9b6 100644 --- a/qmlcomponents/CMakeLists.txt +++ b/qmlcomponents/CMakeLists.txt @@ -20,4 +20,4 @@ install(TARGETS satellitecomponentsplugin DESTINATION ${QML_INSTALL_DIR}/org/kde install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/satellite/components) - +add_subdirectory(modeltest) diff --git a/qmlcomponents/applicationlistmodel.cpp b/qmlcomponents/applicationlistmodel.cpp index 540161bc..09c104e9 100644 --- a/qmlcomponents/applicationlistmodel.cpp +++ b/qmlcomponents/applicationlistmodel.cpp @@ -153,6 +153,13 @@ QVariant ApplicationListModel::data(const QModelIndex &index, int role) const } } +Qt::ItemFlags ApplicationListModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + return Qt::ItemIsDragEnabled|QAbstractItemModel::flags(index); +} + int ApplicationListModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { @@ -162,6 +169,11 @@ int ApplicationListModel::rowCount(const QModelIndex &parent) const return m_applicationList.count(); } +void ApplicationListModel::moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild) +{ + moveItem(sourceRow, destinationChild); +} + Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination) { if (row < 0 || destination < 0 || row >= m_applicationList.length() || diff --git a/qmlcomponents/applicationlistmodel.h b/qmlcomponents/applicationlistmodel.h index 15cc5272..a5094566 100644 --- a/qmlcomponents/applicationlistmodel.h +++ b/qmlcomponents/applicationlistmodel.h @@ -46,10 +46,14 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + void moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild); + int count() { return m_applicationList.count(); } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex &index) const; + QHash roleNames() const Q_DECL_OVERRIDE; enum Roles { diff --git a/qmlcomponents/modeltest/CMakeLists.txt b/qmlcomponents/modeltest/CMakeLists.txt new file mode 100644 index 00000000..246eeef7 --- /dev/null +++ b/qmlcomponents/modeltest/CMakeLists.txt @@ -0,0 +1,15 @@ + +set(applicationlistmodeltest_SRCS + main.cpp + modeltest.cpp + ../applicationlistmodel.cpp + ) + +add_executable(applicationlistmodeltest ${applicationlistmodeltest_SRCS}) +target_link_libraries(applicationlistmodeltest + Qt5::Core + Qt5::Test + Qt5::Widgets + KF5::Service + ) + diff --git a/qmlcomponents/modeltest/main.cpp b/qmlcomponents/modeltest/main.cpp new file mode 100644 index 00000000..cfa3ef52 --- /dev/null +++ b/qmlcomponents/modeltest/main.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include + + +#include "../applicationlistmodel.h" +#include "modeltest.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QWidget *widget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout(widget); + + ApplicationListModel *applicationListModel = new ApplicationListModel(widget); + ModelTest *test = new ModelTest(applicationListModel, widget); + + QTreeView *view = new QTreeView(widget); + QPushButton *upButton = new QPushButton(widget); + upButton->setText("Move Up"); + QObject::connect(upButton, &QPushButton::clicked, [=](){ + QModelIndex idx = view->currentIndex(); + if (idx.row() > 0) { + applicationListModel->moveItem(idx.row(), idx.row()-1); + } + }); + QPushButton *downButton = new QPushButton(widget); + downButton->setText("Move Down"); + QObject::connect(downButton, &QPushButton::clicked, [=](){ + QModelIndex idx = view->currentIndex(); + if (idx.row() > 0) { + applicationListModel->moveItem(idx.row(), idx.row()+1); + } + }); + layout->addWidget(upButton); + layout->addWidget(downButton); + + view->setDragDropMode(QAbstractItemView::InternalMove); + view->setModel(applicationListModel); + applicationListModel->loadApplications(); + layout->addWidget(view); + + QAction *quit = new QAction(widget); + quit->setShortcut(Qt::CTRL + Qt::Key_Q); + QObject::connect(quit, SIGNAL(triggered()), &app, SLOT(quit())); + + widget->addAction(quit); + widget->show(); + return app.exec(); +} diff --git a/qmlcomponents/modeltest/modeltest.cpp b/qmlcomponents/modeltest/modeltest.cpp new file mode 100644 index 00000000..ec66e401 --- /dev/null +++ b/qmlcomponents/modeltest/modeltest.cpp @@ -0,0 +1,592 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "modeltest.h" + +#include +#include + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) +{ + if (!model) + qFatal("%s: model must not be null", Q_FUNC_INFO); + + connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) ); + connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) ); + connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) ); + connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(runAllTests()) ); + connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(runAllTests()) ); + + // Special checks for changes + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged()) ); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged()) ); + + connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) ); + connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(rowsInserted(QModelIndex,int,int)) ); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int)) ); + connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), + this, SLOT(headerDataChanged(Qt::Orientation,int,int)) ); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if ( fetchingMore ) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() ); + model->canFetchMore ( QModelIndex() ); + QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); + QVERIFY( model->data ( QModelIndex() ) == QVariant() ); + fetchingMore = true; + model->fetchMore ( QModelIndex() ); + fetchingMore = false; + Qt::ItemFlags flags = model->flags ( QModelIndex() ); + QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); + model->hasChildren ( QModelIndex() ); + model->hasIndex ( 0, 0 ); + model->headerData ( 0, Qt::Horizontal ); + model->index ( 0, 0 ); + model->itemData ( QModelIndex() ); + QVariant cache; + model->match ( QModelIndex(), -1, cache ); + model->mimeTypes(); + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + QVERIFY( model->rowCount() >= 0 ); + QVariant variant; + model->setData ( QModelIndex(), variant, -1 ); + model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); + model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); + QMap roles; + model->sibling ( 0, 0, QModelIndex() ); + model->span ( QModelIndex() ); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ +// qDebug() << "rc"; + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + int rows = model->rowCount ( topIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( topIndex ) ); + + QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); + if ( secondLevelIndex.isValid() ) { // not the top level + // check a row count where parent is valid + rows = model->rowCount ( secondLevelIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( secondLevelIndex ) ); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->columnCount ( topIndex ) >= 0 ); + + // check a column count where parent is valid + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + if ( childIndex.isValid() ) + QVERIFY( model->columnCount ( childIndex ) >= 0 ); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ +// qDebug() << "hi"; + // Make sure that invalid values returns an invalid index + QVERIFY( !model->hasIndex ( -2, -2 ) ); + QVERIFY( !model->hasIndex ( -2, 0 ) ); + QVERIFY( !model->hasIndex ( 0, -2 ) ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + QVERIFY( !model->hasIndex ( rows, columns ) ); + QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); + + if ( rows > 0 ) + QVERIFY( model->hasIndex ( 0, 0 ) ); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ +// qDebug() << "i"; + // Make sure that invalid values returns an invalid index + QVERIFY( model->index ( -2, -2 ) == QModelIndex() ); + QVERIFY( model->index ( -2, 0 ) == QModelIndex() ); + QVERIFY( model->index ( 0, -2 ) == QModelIndex() ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if ( rows == 0 ) + return; + + // Catch off by one errors + QVERIFY( model->index ( rows, columns ) == QModelIndex() ); + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index ( 0, 0 ); + QModelIndex b = model->index ( 0, 0 ); + QVERIFY( a == b ); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ +// qDebug() << "p"; + // Make sure the model won't crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + + if ( model->rowCount() == 0 ) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->parent ( topIndex ) == QModelIndex() ); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if ( model->rowCount ( topIndex ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QVERIFY( model->parent ( childIndex ) == topIndex ); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); + if ( model->rowCount ( topIndex1 ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); + QVERIFY( childIndex != childIndex1 ); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren ( QModelIndex() ); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while ( p.isValid() ) + p = p.parent(); + + // For models that are dynamically populated + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + + int rows = model->rowCount ( parent ); + int columns = model->columnCount ( parent ); + + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + // Some further testing against rows(), columns(), and hasChildren() + QVERIFY( rows >= 0 ); + QVERIFY( columns >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + const QModelIndex topLeftChild = model->index( 0, 0, parent ); + + QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); + for ( int r = 0; r < rows; ++r ) { + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); + for ( int c = 0; c < columns; ++c ) { + QVERIFY( model->hasIndex ( r, c, parent ) ); + QModelIndex index = model->index ( r, c, parent ); + // rowCount() and columnCount() said that it existed... + QVERIFY( index.isValid() ); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index ( r, c, parent ); + QVERIFY( index == modifiedIndex ); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index ( r, c, parent ); + QModelIndex b = model->index ( r, c, parent ); + QVERIFY( a == b ); + + { + const QModelIndex sibling = model->sibling( r, c, topLeftChild ); + QVERIFY( index == sibling ); + } + { + const QModelIndex sibling = topLeftChild.sibling( r, c ); + QVERIFY( index == sibling ); + } + + // Some basic checking on the index that is returned + QVERIFY( index.model() == model ); + QCOMPARE( index.row(), r ); + QCOMPARE( index.column(), c ); + // While you can technically return a QVariant usually this is a sign + // of a bug in data(). Disable if this really is ok in your model. +// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); + + // If the next test fails here is some somewhat useful debug you play with. + + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); +// And a view that you can even use to show the model. +// QTreeView view; +// view.setModel(model); +// view.show(); + } + + // Check that we can get back our real parent. + QCOMPARE( model->parent ( index ), parent ); + + // recursively go down the children + if ( model->hasChildren ( index ) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren ( index, ++currentDepth ); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index ( r, c, parent ); + QVERIFY( index == newerIndex ); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + QVERIFY( !model->data ( QModelIndex() ).isValid() ); + + if ( model->rowCount() == 0 ) + return; + + // A valid index should have a valid QVariant data + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // shouldn't be able to set data on an invalid index + QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); + + // General Purpose roles that should return a QString + QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert() ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert() ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert() ); + } + + // General Purpose roles that should return a QSize + variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); + if ( variant.isValid() ) { + QVERIFY( variant.canConvert() ); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); + if ( fontVariant.isValid() ) { + QVERIFY( fontVariant.canConvert() ); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); + if ( textAlignmentVariant.isValid() ) { + int alignment = textAlignmentVariant.toInt(); + QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( colorVariant.canConvert() ); + } + + colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( colorVariant.canConvert() ); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); + if ( checkStateVariant.isValid() ) { + int state = checkStateVariant.toInt(); + QVERIFY( state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked ); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */) +{ +// Q_UNUSED(end); +// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() +// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); +// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( start, 0, parent ) ); + insert.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) +{ + Changing c = insert.pop(); + QVERIFY( c.parent == parent ); +// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize +// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); + +// for (int ii=start; ii <= end; ii++) +// { +// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); +// } +// qDebug(); + + QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + + QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) ); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) + changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); +} + +void ModelTest::layoutChanged() +{ + for ( int i = 0; i < changing.count(); ++i ) { + QPersistentModelIndex p = changing[i]; + QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) ); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) +{ +qDebug() << "ratbr" << parent << start << end; + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( end + 1, 0, parent ) ); + remove.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) +{ + qDebug() << "rr" << parent << start << end; + Changing c = remove.pop(); + QVERIFY( c.parent == parent ); + QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) ); +} + +void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + QVERIFY(topLeft.isValid()); + QVERIFY(bottomRight.isValid()); + QModelIndex commonParent = bottomRight.parent(); + QVERIFY(topLeft.parent() == commonParent); + QVERIFY(topLeft.row() <= bottomRight.row()); + QVERIFY(topLeft.column() <= bottomRight.column()); + int rowCount = model->rowCount(commonParent); + int columnCount = model->columnCount(commonParent); + QVERIFY(bottomRight.row() < rowCount); + QVERIFY(bottomRight.column() < columnCount); +} + +void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end) +{ + QVERIFY(start >= 0); + QVERIFY(end >= 0); + QVERIFY(start <= end); + int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount(); + QVERIFY(start < itemCount); + QVERIFY(end < itemCount); +} + diff --git a/qmlcomponents/modeltest/modeltest.h b/qmlcomponents/modeltest/modeltest.h new file mode 100644 index 00000000..b3d23cfb --- /dev/null +++ b/qmlcomponents/modeltest/modeltest.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); + void rowsInserted( const QModelIndex & parent, int start, int end ); + void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); + void rowsRemoved( const QModelIndex & parent, int start, int end ); + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void headerDataChanged(Qt::Orientation orientation, int start, int end); + +private: + void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); + + QAbstractItemModel *model; + + struct Changing { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif From b9503b8a2f301c088ea6a2b0ea4e2d55d7d1ca68 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 24 Apr 2015 19:14:31 +0200 Subject: [PATCH 58/86] prototype for top panel following the mockup prototype following the mocups here http://forums.plasma-mobile.org/showthread.php?tid=11863 if it's decided against this, revert just this commit --- containments/panel/contents/ui/main.qml | 81 ++++++++++++++++--------- shell/contents/applet/CompactApplet.qml | 61 +++++++------------ 2 files changed, 74 insertions(+), 68 deletions(-) diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml index f45e34fd..f761eb32 100644 --- a/containments/panel/contents/ui/main.qml +++ b/containments/panel/contents/ui/main.qml @@ -45,7 +45,7 @@ PlasmaCore.ColorScope { } function addApplet(applet, x, y) { - var container = appletContainerComponent.createObject(tabGroup) + var container = appletContainerComponent.createObject(layout) container.visible = true print("Applet added: " + applet) @@ -53,22 +53,48 @@ PlasmaCore.ColorScope { var appletHeight = applet.height; applet.parent = container; container.applet = applet; - applet.anchors.fill = container; + //applet.anchors.fill = container; + applet.width = units.iconSizes.medium + applet.height = units.iconSizes.medium applet.visible = true; container.visible = true; - //container.parent = tabs; + // If the provided position is valid, use it. + if (x >= 0 && y >= 0) { + var index = LayoutManager.insertAtCoordinates(container, x , y); - //The tab - var tab = tabComponent.createObject(tabBar.layout); - tab.iconSource = applet.icon; - tab.tab = container; + // Fall through to determining an appropriate insert position. + } else { + var before = null; + container.animationsEnabled = false; + + if (lastSpacer.parent === layout) { + before = lastSpacer; + } + + if (before) { + LayoutManager.insertBefore(before, container); + + // Fall through to adding at the end. + } else { + container.parent = layout; + } + + //event compress the enable of animations + //startupTimer.restart(); + } + + if (applet.Layout.fillWidth) { + lastSpacer.parent = root; + } else { + lastSpacer.parent = layout; + } } Component.onCompleted: { LayoutManager.plasmoid = plasmoid; LayoutManager.root = root; - LayoutManager.layout = appletsLayout; + LayoutManager.layout = layout; LayoutManager.restore(); } @@ -79,12 +105,21 @@ PlasmaCore.ColorScope { Component { id: appletContainerComponent - Item { + Rectangle { + color: "grey" //not used yet property bool animationsEnabled: false property Item applet Layout.fillWidth: true - Layout.fillHeight: true + // Layout.fillHeight: true + Layout.minimumHeight: applet && applet.expanded ? units.gridUnit * 20 : units.iconSizes.medium + Layout.maximumHeight: Layout.minimumHeight + Behavior on height { + NumberAnimation { + duration: units.shortDuration + easing.type: Easing.InOutQuad + } + } } } @@ -248,26 +283,14 @@ PlasmaCore.ColorScope { id: panelContents anchors.fill: parent - PlasmaComponents.TabBar { - id: tabBar - visible: plasmoid.applets.count > 1 - anchors { - left: parent.left - top: parent.top - right: parent.right - margins: units.smallSpacing - } - height: units.iconSizes.huge + Item { + id: lastSpacer + Layout.fillWidth: true + Layout.fillHeight: true } - PlasmaComponents.TabGroup { - id: tabGroup - anchors { - left: parent.left - top: plasmoid.applets.count > 1 ? tabBar.bottom : parent.top - right: parent.right - bottom: parent.bottom - margins: units.smallSpacing - } + ColumnLayout { + id: layout + anchors.fill: parent } } } diff --git a/shell/contents/applet/CompactApplet.qml b/shell/contents/applet/CompactApplet.qml index 3405338c..d49d5893 100644 --- a/shell/contents/applet/CompactApplet.qml +++ b/shell/contents/applet/CompactApplet.qml @@ -15,7 +15,7 @@ * 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.0 +import QtQuick 2.4 import QtQuick.Layouts 1.1 import QtQuick.Window 2.0 @@ -53,23 +53,23 @@ PlasmaCore.ToolTipArea { } //if the fullRepresentation size was restored to a stored size, or if is dragged from the desktop, restore popup size if (fullRepresentation.width > 0) { - popupWindow.mainItem.width = fullRepresentation.width; + appletParent.width = fullRepresentation.width; } else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredWidth > 0) { - popupWindow.mainItem.width = fullRepresentation.Layout.preferredWidth + appletParent.width = fullRepresentation.Layout.preferredWidth } else if (fullRepresentation.implicitWidth > 0) { - popupWindow.mainItem.width = fullRepresentation.implicitWidth + appletParent.width = fullRepresentation.implicitWidth } else { - popupWindow.mainItem.width = theme.mSize(theme.defaultFont).width * 35 + appletParent.width = theme.mSize(theme.defaultFont).width * 35 } if (fullRepresentation.height > 0) { - popupWindow.mainItem.height = fullRepresentation.height; + appletParent.height = fullRepresentation.height; } else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredHeight > 0) { - popupWindow.mainItem.height = fullRepresentation.Layout.preferredHeight + appletParent.height = fullRepresentation.Layout.preferredHeight } else if (fullRepresentation.implicitHeight > 0) { - popupWindow.mainItem.height = fullRepresentation.implicitHeight + appletParent.height = fullRepresentation.implicitHeight } else { - popupWindow.mainItem.height = theme.mSize(theme.defaultFont).height * 25 + appletParent.height = theme.mSize(theme.defaultFont).height * 25 } fullRepresentation.parent = appletParent; @@ -116,38 +116,21 @@ PlasmaCore.ToolTipArea { onTriggered: plasmoid.expanded = popupWindow.visible; } - PlasmaCore.Dialog { - id: popupWindow - objectName: "popupWindow" - flags: Qt.WindowStaysOnTopHint - visible: plasmoid.expanded && fullRepresentation - visualParent: compactRepresentation ? compactRepresentation : null - location: plasmoid.location - hideOnWindowDeactivate: plasmoid.hideOnWindowDeactivate + MouseEventListener { + id: appletParent + opacity: plasmoid.expanded ? 1 : 0 + anchors.top: parent.bottom + Layout.minimumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumWidth : 0 + Layout.minimumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumHeight: 0 + Layout.maximumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumWidth : Infinity + Layout.maximumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumHeight: Infinity - property var oldStatus: PlasmaCore.Types.UnknownStatus - - //It's a MouseEventListener to get all the events, so the eventfilter will be able to catch them - mainItem: MouseEventListener { - id: appletParent - Layout.minimumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumWidth : 0 - Layout.minimumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumHeight: 0 - Layout.maximumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumWidth : Infinity - Layout.maximumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumHeight: Infinity - } - - onVisibleChanged: { - if (!visible) { - expandedSync.restart(); - plasmoid.status = oldStatus; - } else { - oldStatus = plasmoid.status; - plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus; - // This call currently fails and complains at runtime: - // QWindow::setWindowState: QWindow::setWindowState does not accept Qt::WindowActive - popupWindow.requestActivate(); + Behavior on opacity { + OpacityAnimator { + duration: units.shortDuration + easing.type: Easing.InOutQuad } } - } + } From 3275b8b698191dd03639e16b4116a4baa789c86a Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sat, 25 Apr 2015 17:30:08 +0200 Subject: [PATCH 59/86] finish the new layouting prototype --- containments/panel/contents/ui/main.qml | 44 ++++++++++++------- shell/contents/applet/CompactApplet.qml | 37 ++++------------ .../applet/DefaultCompactRepresentation.qml | 30 +++++++++++-- 3 files changed, 63 insertions(+), 48 deletions(-) diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml index f761eb32..4c33cc46 100644 --- a/containments/panel/contents/ui/main.qml +++ b/containments/panel/contents/ui/main.qml @@ -38,6 +38,7 @@ PlasmaCore.ColorScope { property Item toolBox property int buttonHeight: width/4 property bool reorderingApps: false + property QtObject expandedApplet Containment.onAppletAdded: { addApplet(applet, x, y); @@ -54,7 +55,8 @@ PlasmaCore.ColorScope { applet.parent = container; container.applet = applet; //applet.anchors.fill = container; - applet.width = units.iconSizes.medium + applet.anchors.left = container.left; + applet.anchors.right = container.right; applet.height = units.iconSizes.medium applet.visible = true; container.visible = true; @@ -66,7 +68,6 @@ PlasmaCore.ColorScope { // Fall through to determining an appropriate insert position. } else { var before = null; - container.animationsEnabled = false; if (lastSpacer.parent === layout) { before = lastSpacer; @@ -105,21 +106,30 @@ PlasmaCore.ColorScope { Component { id: appletContainerComponent - Rectangle { - color: "grey" - //not used yet - property bool animationsEnabled: false + Item { property Item applet Layout.fillWidth: true - // Layout.fillHeight: true - Layout.minimumHeight: applet && applet.expanded ? units.gridUnit * 20 : units.iconSizes.medium - Layout.maximumHeight: Layout.minimumHeight - Behavior on height { - NumberAnimation { - duration: units.shortDuration - easing.type: Easing.InOutQuad - } - } + clip: true + anchors { + left: parent.left + right: parent.right + } + height: applet && applet.expanded ? Math.max(applet.fullRepresentationItem.Layout.minimumHeight, units.iconSizes.medium) : units.iconSizes.medium + Behavior on height { + NumberAnimation { + duration: units.shortDuration + easing.type: Easing.InOutQuad + } + } + Connections { + target: applet + onExpandedChanged: { + if (root.expandedApplet) { + root.expandedApplet.expanded = false; + } + root.expandedApplet = applet; + } + } } } @@ -282,15 +292,17 @@ PlasmaCore.ColorScope { contents: Item { id: panelContents anchors.fill: parent + clip: true Item { id: lastSpacer Layout.fillWidth: true Layout.fillHeight: true } - ColumnLayout { + Column { id: layout anchors.fill: parent + spacing: units.smallSpacing } } } diff --git a/shell/contents/applet/CompactApplet.qml b/shell/contents/applet/CompactApplet.qml index d49d5893..f7d61a27 100644 --- a/shell/contents/applet/CompactApplet.qml +++ b/shell/contents/applet/CompactApplet.qml @@ -51,7 +51,7 @@ PlasmaCore.ToolTipArea { if (!fullRepresentation) { return; } - //if the fullRepresentation size was restored to a stored size, or if is dragged from the desktop, restore popup size + /* //if the fullRepresentation size was restored to a stored size, or if is dragged from the desktop, restore popup size if (fullRepresentation.width > 0) { appletParent.width = fullRepresentation.width; } else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredWidth > 0) { @@ -70,40 +70,19 @@ PlasmaCore.ToolTipArea { appletParent.height = fullRepresentation.implicitHeight } else { appletParent.height = theme.mSize(theme.defaultFont).height * 25 - } + }*/ fullRepresentation.parent = appletParent; fullRepresentation.anchors.fill = fullRepresentation.parent; } - PlasmaCore.FrameSvgItem { + Rectangle { id: expandedItem anchors.fill: parent - imagePath: "widgets/tabbar" - visible: fromCurrentTheme - prefix: { - var prefix; - switch (plasmoid.location) { - case PlasmaCore.Types.LeftEdge: - prefix = "west-active-tab"; - break; - case PlasmaCore.Types.TopEdge: - prefix = "north-active-tab"; - break; - case PlasmaCore.Types.RightEdge: - prefix = "east-active-tab"; - break; - default: - prefix = "south-active-tab"; - } - if (!hasElementPrefix(prefix)) { - prefix = "active-tab"; - } - return prefix; - } - opacity: plasmoid.expanded ? 1 : 0 + color: PlasmaCore.ColorScope.highlightColor + opacity: plasmoid.expanded ? 0.3 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: units.shortDuration easing.type: Easing.InOutQuad } @@ -116,7 +95,7 @@ PlasmaCore.ToolTipArea { onTriggered: plasmoid.expanded = popupWindow.visible; } - MouseEventListener { + Item { id: appletParent opacity: plasmoid.expanded ? 1 : 0 anchors.top: parent.bottom @@ -124,6 +103,8 @@ PlasmaCore.ToolTipArea { Layout.minimumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.minimumHeight: 0 Layout.maximumWidth: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumWidth : Infinity Layout.maximumHeight: (fullRepresentation && fullRepresentation.Layout) ? fullRepresentation.Layout.maximumHeight: Infinity + width: Math.max(parent.width, Layout.minimumWidth) + height: Layout.minimumHeight Behavior on opacity { OpacityAnimator { diff --git a/shell/contents/applet/DefaultCompactRepresentation.qml b/shell/contents/applet/DefaultCompactRepresentation.qml index f1611053..80b864fa 100644 --- a/shell/contents/applet/DefaultCompactRepresentation.qml +++ b/shell/contents/applet/DefaultCompactRepresentation.qml @@ -19,9 +19,11 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents -PlasmaCore.IconItem { - id: icon +Row { + id: main + spacing: units.largeSpacing Layout.minimumWidth: { switch (plasmoid.formFactor) { @@ -45,8 +47,28 @@ PlasmaCore.IconItem { } } - source: plasmoid.icon ? plasmoid.icon : "plasma" - active: mouseArea.containsMouse + PlasmaCore.IconItem { + id: icon + source: plasmoid.icon ? plasmoid.icon : "plasma" + active: mouseArea.containsMouse + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + anchors.verticalCenter: parent.verticalCenter + } + PlasmaCore.SvgItem { + svg: PlasmaCore.Svg { + id: arrowSvg + imagePath: "widgets/arrows" + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + } + width: units.iconSizes.smallMedium + height: width + elementId: plasmoid.expanded ? "up-arrow" : "down-arrow" + anchors.verticalCenter: parent.verticalCenter + } + PlasmaComponents.Label { + text: plasmoid.title + anchors.verticalCenter: parent.verticalCenter + } MouseArea { id: mouseArea From 3fab7b980cf163d3ca9f642dc0939ad55be072eb Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sun, 26 Apr 2015 15:50:59 +0200 Subject: [PATCH 60/86] add a c++ qobject will be used to do notifications --- dialer/src/CMakeLists.txt | 1 + dialer/src/dialerutils.cpp | 33 +++++++++++++++++++++++++++++++++ dialer/src/dialerutils.h | 37 +++++++++++++++++++++++++++++++++++++ dialer/src/main.cpp | 5 +++++ 4 files changed, 76 insertions(+) create mode 100644 dialer/src/dialerutils.cpp create mode 100644 dialer/src/dialerutils.h diff --git a/dialer/src/CMakeLists.txt b/dialer/src/CMakeLists.txt index f430c3f6..e3c452ed 100644 --- a/dialer/src/CMakeLists.txt +++ b/dialer/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(plasmaphonedialer_SRCS main.cpp + dialerutils.cpp ) add_executable(plasmaphonedialer ${plasmaphonedialer_SRCS}) diff --git a/dialer/src/dialerutils.cpp b/dialer/src/dialerutils.cpp new file mode 100644 index 00000000..745a4da3 --- /dev/null +++ b/dialer/src/dialerutils.cpp @@ -0,0 +1,33 @@ +/* + * 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 version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "dialerutils.h" + +#include + +DialerUtils::DialerUtils(QObject *parent) +: QObject(parent) +{ + +} + +DialerUtils::~DialerUtils() +{ +} + +#include "moc_dialerutils.cpp" diff --git a/dialer/src/dialerutils.h b/dialer/src/dialerutils.h new file mode 100644 index 00000000..ae2d5d23 --- /dev/null +++ b/dialer/src/dialerutils.h @@ -0,0 +1,37 @@ +/* + * 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 version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DIALERUTILS_H +#define DIALERUTILS_H + +#include + +class DialerUtils : public QObject +{ + Q_OBJECT +public: + + DialerUtils(QObject *parent = 0); + virtual ~DialerUtils(); + +private: + +}; + + +#endif diff --git a/dialer/src/main.cpp b/dialer/src/main.cpp index 8b7862ce..7356386f 100644 --- a/dialer/src/main.cpp +++ b/dialer/src/main.cpp @@ -19,6 +19,8 @@ #include +#include "dialerutils.h" + #include #include #include @@ -67,6 +69,9 @@ int main(int argc, char **argv) obj->loadPackage(packagePath); obj->engine()->rootContext()->setContextProperty("commandlineArguments", parser.positionalArguments()); + DialerUtils *dialerUtils = new DialerUtils; + obj->engine()->rootContext()->setContextProperty("dialerUtils", QVariant::fromValue(dialerUtils)); + obj->completeInitialization(); if (!obj->package().metadata().isValid()) { From c3f65ad5e8c736e04a86e7758903191b22743436 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sun, 26 Apr 2015 16:42:14 +0200 Subject: [PATCH 61/86] add a knotification for missed calls --- CMakeLists.txt | 2 +- dialer/package/contents/ui/main.qml | 26 ++++++++++++++++- dialer/src/CMakeLists.txt | 2 ++ dialer/src/dialerutils.cpp | 44 +++++++++++++++++++++++++++-- dialer/src/dialerutils.h | 11 +++++++- dialer/src/plasma_dialer.notifyrc | 9 ++++++ 6 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 dialer/src/plasma_dialer.notifyrc diff --git a/CMakeLists.txt b/CMakeLists.txt index e496cb93..d78c4f7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Gui Widgets Qml Quick Test) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n) -find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons) +find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons Notifications) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 10726900..8e9dbce9 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -33,15 +33,39 @@ ApplicationWindow { width: 600 height: 800 - property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0 + //keep track of the status we were in + property int previousStatus + //keep track if we were visible when ringing + property bool wasVisible //END PROPERTIES //BEGIN SIGNAL HANDLERS onStatusChanged: { //STATUS_INCOMING if (status == 5) { + wasVisible = root.visible; root.visible = true; + //Was STATUS_INCOMING now is STATUS_DISCONNECTED: Missed call! + } else if (status == 7 && previousStatus == 5) { + dialerUtils.notifyMissedCall(); + root.visible = wasVisible; + } + + previousStatus = status; + } + + Connections { + target: dialerUtils + onMissedCallsActionTriggered: { + root.visible = true; + } + } + + onVisibleChanged: { + //reset missed calls if the status is not STATUS_INCOMING when got visible + if (visible && status != 5) { + dialerUtils.resetMissedCalls(); } } //END SIGNAL HANDLERS diff --git a/dialer/src/CMakeLists.txt b/dialer/src/CMakeLists.txt index e3c452ed..c2980082 100644 --- a/dialer/src/CMakeLists.txt +++ b/dialer/src/CMakeLists.txt @@ -19,6 +19,8 @@ target_link_libraries(plasmaphonedialer KF5::Package KF5::QuickAddons KF5::DBusAddons + KF5::Notifications ) install(TARGETS plasmaphonedialer ${INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES plasma_dialer.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR}) diff --git a/dialer/src/dialerutils.cpp b/dialer/src/dialerutils.cpp index 745a4da3..0b14e04a 100644 --- a/dialer/src/dialerutils.cpp +++ b/dialer/src/dialerutils.cpp @@ -20,14 +20,54 @@ #include +#include + DialerUtils::DialerUtils(QObject *parent) -: QObject(parent) +: QObject(parent), + m_missedCalls(0) { - } DialerUtils::~DialerUtils() { } +void DialerUtils::notifyMissedCall() +{ + qWarning() << "Missed Call."; + + ++m_missedCalls; + if (!m_callsNotification) { + m_callsNotification = new KNotification("callMissed", KNotification::Persistent, 0); + } + m_callsNotification->setComponentName("plasma_dialer"); + m_callsNotification->setIconName("call-start"); + m_callsNotification->setTitle(i18np("One call missed", "%1 calls missed", m_missedCalls)); + + QStringList actions; + actions.append(i18n("View")); + m_callsNotification->setActions(actions); + QObject::connect(m_callsNotification.data(), &KNotification::action1Activated, + [=]() { + qWarning()<<"View action activated"; + emit missedCallsActionTriggered(); + resetMissedCalls(); + }); + + if (m_missedCalls == 1) { + m_callsNotification->sendEvent(); + } else { + m_callsNotification->update(); + } +} + +void DialerUtils::resetMissedCalls() +{ + m_missedCalls = 0; + if (m_callsNotification) { + m_callsNotification->close(); + } + m_callsNotification.clear(); +} + #include "moc_dialerutils.cpp" diff --git a/dialer/src/dialerutils.h b/dialer/src/dialerutils.h index ae2d5d23..dde482b6 100644 --- a/dialer/src/dialerutils.h +++ b/dialer/src/dialerutils.h @@ -20,6 +20,8 @@ #define DIALERUTILS_H #include +#include +#include class DialerUtils : public QObject { @@ -29,8 +31,15 @@ public: DialerUtils(QObject *parent = 0); virtual ~DialerUtils(); + Q_INVOKABLE void notifyMissedCall(); + Q_INVOKABLE void resetMissedCalls(); + +Q_SIGNALS: + void missedCallsActionTriggered(); + private: - + QPointer m_callsNotification; + int m_missedCalls; }; diff --git a/dialer/src/plasma_dialer.notifyrc b/dialer/src/plasma_dialer.notifyrc new file mode 100644 index 00000000..c1c51e4b --- /dev/null +++ b/dialer/src/plasma_dialer.notifyrc @@ -0,0 +1,9 @@ +[Global] +IconName=call-start +Comment=Plasma Phone Dialer + +[Event/callMissed] +Name=Missed Call +Comment=A call has been missed +Action=Popup + From 16cbc31fd17483d802ce4bf662e32542022f86b6 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sun, 26 Apr 2015 19:50:19 +0200 Subject: [PATCH 62/86] simple sqlite-based call history still doesn't get updated real-time --- dialer/package/contents/ui/Dialer/Dialer.qml | 1 + dialer/package/contents/ui/Dialer/History.qml | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/dialer/package/contents/ui/Dialer/Dialer.qml b/dialer/package/contents/ui/Dialer/Dialer.qml index 42214bd1..061ae94e 100644 --- a/dialer/package/contents/ui/Dialer/Dialer.qml +++ b/dialer/package/contents/ui/Dialer/Dialer.qml @@ -36,6 +36,7 @@ Item { status.text = status.text + number } + //TODO: move in root item function call() { if (!voiceCallmanager.activeVoiceCall) { console.log("Calling: " + status.text); diff --git a/dialer/package/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml index 4b7a9c1b..c117fa59 100644 --- a/dialer/package/contents/ui/Dialer/History.qml +++ b/dialer/package/contents/ui/Dialer/History.qml @@ -21,10 +21,80 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import QtQuick.LocalStorage 2.0 Item { + + //TODO: move in root item + property string providerId: voiceCallmanager.providers.id(0) + function call(number) { + if (!voiceCallmanager.activeVoiceCall) { + console.log("Calling: " + status.text); + voiceCallmanager.dial(providerId, number); + + } else { + console.log("Hanging up: " + status.text); + status.text = ''; + var call = voiceCallmanager.activeVoiceCall; + if (call) { + call.hangup(); + } + } + } + + Component.onCompleted: { + var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); + + db.transaction( + function(tx) { + // Create the database if it doesn't already exist + //callType: wether is incoming, outgoing, unanswered + tx.executeSql('CREATE TABLE IF NOT EXISTS History(number TEXT, time DATETIME, callType TEXT)'); + + // Add (another) greeting row + //tx.executeSql("INSERT INTO History VALUES(?, datetime('now') )", ['+39000']); + + // Show all added greetings + var rs = tx.executeSql('SELECT * FROM History'); + + var r = "" + for(var i = 0; i < rs.rows.length; i++) { + r += rs.rows.item(i).number + ", " + rs.rows.item(i).time + "\n" + historyModel.append({number: rs.rows.item(i).number, time: rs.rows.item(i).time}) + } + } + ) + } + PlasmaComponents.Label { anchors.centerIn: parent text: i18n("No recent calls") + visible: false + } + PlasmaExtras.ScrollArea { + anchors.fill: parent + ListView { + id: view + model: ListModel { + id: historyModel + } + delegate: MouseArea { + width: view.width + height: childrenRect.height + onClicked: call(model.number); + + RowLayout { + width: view.width + PlasmaComponents.Label { + text: model.number + Layout.fillWidth: true + } + PlasmaComponents.Label { + text: Qt.formatDateTime(model.time, Qt.locale().dateTimeFormat(Locale.LongFormat)); + } + } + } + } } } \ No newline at end of file From c58e932db2a3254a513a670b9e3aef06df68d0e9 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Sun, 26 Apr 2015 21:36:17 +0200 Subject: [PATCH 63/86] correctly insert values in the history --- dialer/package/contents/ui/Dialer/Dialer.qml | 20 +---- dialer/package/contents/ui/Dialer/History.qml | 83 ++++++++--------- dialer/package/contents/ui/main.qml | 89 +++++++++++++++++-- 3 files changed, 125 insertions(+), 67 deletions(-) diff --git a/dialer/package/contents/ui/Dialer/Dialer.qml b/dialer/package/contents/ui/Dialer/Dialer.qml index 061ae94e..da2806b7 100644 --- a/dialer/package/contents/ui/Dialer/Dialer.qml +++ b/dialer/package/contents/ui/Dialer/Dialer.qml @@ -36,22 +36,6 @@ Item { status.text = status.text + number } - //TODO: move in root item - function call() { - if (!voiceCallmanager.activeVoiceCall) { - console.log("Calling: " + status.text); - voiceCallmanager.dial(providerId, status.text); - - } else { - console.log("Hanging up: " + status.text); - status.text = ''; - var call = voiceCallmanager.activeVoiceCall; - if (call) { - call.hangup(); - } - } - } - ColumnLayout { id: dialPadArea @@ -94,7 +78,9 @@ Item { enabled: status.text.length > 0 opacity: enabled ? 1 : 0.5 source: "call-start" - callback: call + callback: function() { + call(status.text); + } } Item { Layout.minimumWidth: dialPadArea.width/3 diff --git a/dialer/package/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml index c117fa59..a21e503c 100644 --- a/dialer/package/contents/ui/Dialer/History.qml +++ b/dialer/package/contents/ui/Dialer/History.qml @@ -22,49 +22,19 @@ import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras -import QtQuick.LocalStorage 2.0 -Item { +Rectangle { + color: syspal.base - //TODO: move in root item - property string providerId: voiceCallmanager.providers.id(0) - function call(number) { - if (!voiceCallmanager.activeVoiceCall) { - console.log("Calling: " + status.text); - voiceCallmanager.dial(providerId, number); - - } else { - console.log("Hanging up: " + status.text); - status.text = ''; - var call = voiceCallmanager.activeVoiceCall; - if (call) { - call.hangup(); - } - } - } - - Component.onCompleted: { - var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); - - db.transaction( - function(tx) { - // Create the database if it doesn't already exist - //callType: wether is incoming, outgoing, unanswered - tx.executeSql('CREATE TABLE IF NOT EXISTS History(number TEXT, time DATETIME, callType TEXT)'); - - // Add (another) greeting row - //tx.executeSql("INSERT INTO History VALUES(?, datetime('now') )", ['+39000']); - - // Show all added greetings - var rs = tx.executeSql('SELECT * FROM History'); - - var r = "" - for(var i = 0; i < rs.rows.length; i++) { - r += rs.rows.item(i).number + ", " + rs.rows.item(i).time + "\n" - historyModel.append({number: rs.rows.item(i).number, time: rs.rows.item(i).time}) - } - } - ) + function secondsToTimeString(seconds) { + seconds = Math.floor(seconds/1000) + var h = Math.floor(seconds / 3600); + var m = Math.floor((seconds - (h * 3600)) / 60); + var s = seconds - h * 3600 - m * 60; + if(h < 10) h = '0' + h; + if(m < 10) m = '0' + m; + if(s < 10) s = '0' + s; + return '' + h + ':' + m + ':' + s; } PlasmaComponents.Label { @@ -76,8 +46,18 @@ Item { anchors.fill: parent ListView { id: view - model: ListModel { - id: historyModel + model: historyModel + section { + property: "date" + labelPositioning: ViewSection.CurrentLabelAtStart + delegate: Rectangle { + width: view.width + height: childrenRect.height + color: syspal.base + PlasmaComponents.Label { + text: Qt.formatDate(section, Qt.locale().dateFormat(Locale.LongFormat)); + } + } } delegate: MouseArea { width: view.width @@ -86,12 +66,27 @@ Item { RowLayout { width: view.width + PlasmaComponents.Label { + text: { + switch (model.callType) { + case 0: + return "miss"; + case 1: + return "incoming"; + case 2: + return "outgoing"; + } + } + } PlasmaComponents.Label { text: model.number Layout.fillWidth: true } PlasmaComponents.Label { - text: Qt.formatDateTime(model.time, Qt.locale().dateTimeFormat(Locale.LongFormat)); + text: i18n("Duration: %1", secondsToTimeString(model.duration)); + } + PlasmaComponents.Label { + text: Qt.formatTime(model.date+" "+model.time, Qt.locale().timeFormat(Locale.LongFormat)); } } } diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 8e9dbce9..7cd2b453 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -21,6 +21,7 @@ import QtQuick 2.3 import QtQuick.Controls 1.3 import QtQuick.Layouts 1.1 +import QtQuick.LocalStorage 2.0 import org.nemomobile.voicecall 1.0 import MeeGo.QOfono 0.2 import org.kde.plasma.core 2.0 as PlasmaCore @@ -38,18 +39,28 @@ ApplicationWindow { property int previousStatus //keep track if we were visible when ringing property bool wasVisible + //support a single provider for now + property string providerId: voiceCallmanager.providers.id(0) + //was the last call an incoming one? + property bool isIncoming //END PROPERTIES //BEGIN SIGNAL HANDLERS onStatusChanged: { + //STATUS_ACTIVE + if (status == 1) { + root.isIncoming = voiceCallmanager.activeVoiceCall.isIncoming; //STATUS_INCOMING - if (status == 5) { + } else if (status == 5) { wasVisible = root.visible; root.visible = true; //Was STATUS_INCOMING now is STATUS_DISCONNECTED: Missed call! } else if (status == 7 && previousStatus == 5) { dialerUtils.notifyMissedCall(); root.visible = wasVisible; + insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, 0, 0); + } else if (status == 7) { + insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, voiceCallmanager.activeVoiceCall.duration, isIncoming ? 1 : 2); } previousStatus = status; @@ -70,7 +81,76 @@ ApplicationWindow { } //END SIGNAL HANDLERS +//BEGIN FUNCTIONS + function call(number) { + if (!voiceCallmanager.activeVoiceCall) { + console.log("Calling: " + status.text); + voiceCallmanager.dial(providerId, number); + + } else { + console.log("Hanging up: " + status.text); + status.text = ''; + var call = voiceCallmanager.activeVoiceCall; + if (call) { + call.hangup(); + } + } + } + + function insertCallInHistory(number, duration, callType) { + //DATABSE + var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); + + db.transaction( + function(tx) { + var rs = tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), time('now'), ?, ? )", [number, duration, callType]); + + // Show all added greetings + var rs = tx.executeSql('SELECT * FROM History where id=?', [rs.insertId]); + + for(var i = 0; i < rs.rows.length; i++) { + historyModel.append(rs.rows.item(i)); + } + } + ) + } + +//END FUNCTIONS + +//BEGIN DATABASE + Component.onCompleted: { + //HACK: make sure activeVoiceCall is loaded if already existing + voiceCallmanager.voiceCalls.onVoiceCallsChanged(); + voiceCallmanager.onActiveVoiceCallChanged(); + + //DATABSE + var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); + + db.transaction( + function(tx) { + // Create the database if it doesn't already exist + //callType: wether is incoming, outgoing, unanswered + tx.executeSql('CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, date DATE, time TIME, duration INTEGER, callType INTEGER)'); + + // Add (another) greeting row + // tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), time('now'), ? )", ['+39000', 0]); + + // Show all added greetings + var rs = tx.executeSql('SELECT * FROM History'); + + for(var i = 0; i < rs.rows.length; i++) { + historyModel.append(rs.rows.item(i)); + } + } + ) + } +//END DATABASE + //BEGIN MODELS + ListModel { + id: historyModel + } + OfonoManager { id: ofonoManager onAvailableChanged: { @@ -142,6 +222,8 @@ ApplicationWindow { console.log('*** QML *** VCM ERROR: ' + message); } } + + SystemPalette {id: syspal} //END MODELS //BEGIN UI @@ -174,9 +256,4 @@ ApplicationWindow { } //END UI - Component.onCompleted: { - //HACK: make sure activeVoiceCall is loaded if already existing - voiceCallmanager.voiceCalls.onVoiceCallsChanged(); - voiceCallmanager.onActiveVoiceCallChanged(); - } } From fb4765d50aa7a7744ae950566e5978abdc6207b3 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 27 Apr 2015 11:01:18 +0200 Subject: [PATCH 64/86] capability to remove items better history delegate --- dialer/package/contents/ui/Dialer/History.qml | 46 ++----- .../contents/ui/Dialer/HistoryDelegate.qml | 122 ++++++++++++++++++ dialer/package/contents/ui/main.qml | 18 ++- 3 files changed, 147 insertions(+), 39 deletions(-) create mode 100644 dialer/package/contents/ui/Dialer/HistoryDelegate.qml diff --git a/dialer/package/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml index a21e503c..9479bb1f 100644 --- a/dialer/package/contents/ui/Dialer/History.qml +++ b/dialer/package/contents/ui/Dialer/History.qml @@ -17,14 +17,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.0 +import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras -Rectangle { - color: syspal.base +Item { function secondsToTimeString(seconds) { seconds = Math.floor(seconds/1000) @@ -50,46 +49,17 @@ Rectangle { section { property: "date" labelPositioning: ViewSection.CurrentLabelAtStart - delegate: Rectangle { - width: view.width - height: childrenRect.height - color: syspal.base + delegate: PlasmaComponents.ListItem { + //width: view.width + //height: childrenRect.height + //color: syspal.base + sectionDelegate: true PlasmaComponents.Label { text: Qt.formatDate(section, Qt.locale().dateFormat(Locale.LongFormat)); } } } - delegate: MouseArea { - width: view.width - height: childrenRect.height - onClicked: call(model.number); - - RowLayout { - width: view.width - PlasmaComponents.Label { - text: { - switch (model.callType) { - case 0: - return "miss"; - case 1: - return "incoming"; - case 2: - return "outgoing"; - } - } - } - PlasmaComponents.Label { - text: model.number - Layout.fillWidth: true - } - PlasmaComponents.Label { - text: i18n("Duration: %1", secondsToTimeString(model.duration)); - } - PlasmaComponents.Label { - text: Qt.formatTime(model.date+" "+model.time, Qt.locale().timeFormat(Locale.LongFormat)); - } - } - } + delegate: HistoryDelegate {} } } } \ No newline at end of file diff --git a/dialer/package/contents/ui/Dialer/HistoryDelegate.qml b/dialer/package/contents/ui/Dialer/HistoryDelegate.qml new file mode 100644 index 00000000..57bc9f9e --- /dev/null +++ b/dialer/package/contents/ui/Dialer/HistoryDelegate.qml @@ -0,0 +1,122 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +Item { + id: delegateParent + width: view.width + height: childrenRect.height + + Behavior on height { + SpringAnimation { spring: 5; damping: 0.3 } + } + SequentialAnimation { + id: removeAnim + XAnimator { + target: delegate + from: delegate.x + to: delegate.x > 0 ? width : -width + duration: units.longDuration + easing.type: Easing.InOutQuad + } + PropertyAnimation { + target: delegateParent + properties: "height" + to: 0 + duration: units.longDuration + easing.type: Easing.InOutQuad + } + ScriptAction { + script: removeCallFromHistory(index); + } + } + + XAnimator { + id: resetAnim + target: delegate + from: delegate.x + to: 0 + duration: units.longDuration + easing.type: Easing.InOutQuad + } + + PlasmaComponents.ListItem { + id: delegate + + MouseArea { + width: parent.width + height: childrenRect.height + onClicked: call(model.number); + drag.axis: Drag.XAxis + drag.target: delegate + onReleased: { + if (drag.active) { + if (delegate.x > delegate.width / 3 || delegate.x < width / -3) { + removeAnim.running = true; + } else { + resetAnim.running = true; + } + } + } + + RowLayout { + width: parent.width + //FIXME: ad hoc icons + PlasmaCore.IconItem { + width: units.iconSizes.medium + height: width + source: { + switch (model.callType) { + case 0: + return "list-remove"; + case 1: + return "go-down"; + case 2: + return "go-up"; + } + } + } + ColumnLayout { + PlasmaComponents.Label { + text: "Name (todo)" + } + PlasmaComponents.Label { + text: i18n("Number: %1", model.number) + Layout.fillWidth: true + } + } + ColumnLayout { + PlasmaComponents.Label { + Layout.alignment: Qt.AlignRight + text: Qt.formatTime(model.date+" "+model.time, Qt.locale().timeFormat(Locale.ShortFormat)); + } + PlasmaComponents.Label { + Layout.alignment: Qt.AlignRight + text: i18n("Duration: %1", secondsToTimeString(model.duration)); + } + } + } + } + } +} diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 7cd2b453..94f5288b 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -115,6 +115,23 @@ ApplicationWindow { ) } + function removeCallFromHistory(id) { + var item = historyModel.get(id); + + if (!item) { + return; + } + + var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); + + db.transaction( + function(tx) { + tx.executeSql("DELETE from History WHERE id=?", [id]); + } + ) + + historyModel.remove(id); + } //END FUNCTIONS //BEGIN DATABASE @@ -223,7 +240,6 @@ ApplicationWindow { } } - SystemPalette {id: syspal} //END MODELS //BEGIN UI From 839208addc3694ce077a7e75d40c41a1277fbb81 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 27 Apr 2015 11:14:53 +0200 Subject: [PATCH 65/86] sort calls by time --- dialer/package/contents/ui/Dialer/History.qml | 7 ++++++- dialer/package/contents/ui/Dialer/HistoryDelegate.qml | 2 +- dialer/package/contents/ui/main.qml | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/dialer/package/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml index 9479bb1f..48aa91ba 100644 --- a/dialer/package/contents/ui/Dialer/History.qml +++ b/dialer/package/contents/ui/Dialer/History.qml @@ -41,11 +41,16 @@ Item { text: i18n("No recent calls") visible: false } + PlasmaExtras.ScrollArea { anchors.fill: parent ListView { id: view - model: historyModel + model: PlasmaCore.SortFilterModel { + sourceModel: historyModel + sortRole: "time" + sortOrder: Qt.DescendingOrder + } section { property: "date" labelPositioning: ViewSection.CurrentLabelAtStart diff --git a/dialer/package/contents/ui/Dialer/HistoryDelegate.qml b/dialer/package/contents/ui/Dialer/HistoryDelegate.qml index 57bc9f9e..7dcaa30c 100644 --- a/dialer/package/contents/ui/Dialer/HistoryDelegate.qml +++ b/dialer/package/contents/ui/Dialer/HistoryDelegate.qml @@ -109,7 +109,7 @@ Item { ColumnLayout { PlasmaComponents.Label { Layout.alignment: Qt.AlignRight - text: Qt.formatTime(model.date+" "+model.time, Qt.locale().timeFormat(Locale.ShortFormat)); + text: Qt.formatTime(model.time, Qt.locale().timeFormat(Locale.ShortFormat)); } PlasmaComponents.Label { Layout.alignment: Qt.AlignRight diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 94f5288b..9ec510b0 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -103,7 +103,7 @@ ApplicationWindow { db.transaction( function(tx) { - var rs = tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), time('now'), ?, ? )", [number, duration, callType]); + var rs = tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), datetime('now'), ?, ? )", [number, duration, callType]); // Show all added greetings var rs = tx.executeSql('SELECT * FROM History where id=?', [rs.insertId]); @@ -126,7 +126,7 @@ ApplicationWindow { db.transaction( function(tx) { - tx.executeSql("DELETE from History WHERE id=?", [id]); + tx.executeSql("DELETE from History WHERE id=?", [item.id]); } ) @@ -147,7 +147,7 @@ ApplicationWindow { function(tx) { // Create the database if it doesn't already exist //callType: wether is incoming, outgoing, unanswered - tx.executeSql('CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, date DATE, time TIME, duration INTEGER, callType INTEGER)'); + tx.executeSql('CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, date DATE, time DATETIME, duration INTEGER, callType INTEGER)'); // Add (another) greeting row // tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), time('now'), ? )", ['+39000', 0]); From 212f60077ff33c0e41fbc57274fcb429f673d663 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 27 Apr 2015 11:51:37 +0200 Subject: [PATCH 66/86] execute action when swiping left --- .../notifications/contents/ui/NotificationStripe.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/applets/notifications/contents/ui/NotificationStripe.qml b/applets/notifications/contents/ui/NotificationStripe.qml index 2023446d..f555b622 100644 --- a/applets/notifications/contents/ui/NotificationStripe.qml +++ b/applets/notifications/contents/ui/NotificationStripe.qml @@ -48,11 +48,16 @@ MouseArea { onReleased: { if (drag.active) { if (x > width / 4 || x < width / -4) { + //if there is an action, execute the first when swiping left + if (x < 0 && actions) { + var action = actions.get(0) + root.executeAction(source, action.id) + } notificationsModel.remove(index); } else { x = 0; } - } else if (body) { + } else if (body || actions) { expanded = !expanded; } } @@ -134,7 +139,10 @@ MouseArea { model: notificationItem.actions delegate: PlasmaComponents.Button { text: model.text - onClicked: root.executeAction(notificationItem.source, model.id) + onClicked: { + root.executeAction(notificationItem.source, model.id) + notificationsModel.remove(index); + } } } } From 7050b65686ed157385a12e77771ec9cc32e34a31 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 27 Apr 2015 12:06:01 +0200 Subject: [PATCH 67/86] prettier notifications --- dialer/package/contents/ui/main.qml | 3 ++- dialer/src/dialerutils.cpp | 10 ++++++++-- dialer/src/dialerutils.h | 2 +- dialer/src/plasma_dialer.notifyrc | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 9ec510b0..98059514 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -56,7 +56,8 @@ ApplicationWindow { root.visible = true; //Was STATUS_INCOMING now is STATUS_DISCONNECTED: Missed call! } else if (status == 7 && previousStatus == 5) { - dialerUtils.notifyMissedCall(); + var prettyDate = Qt.formatTime(voiceCallmanager.activeVoiceCall.startedAt, Qt.locale().timeFormat(Locale.ShortFormat)); + dialerUtils.notifyMissedCall(voiceCallmanager.activeVoiceCall.lineId, i18n("%1 called at %2", voiceCallmanager.activeVoiceCall.lineId, prettyDate)); root.visible = wasVisible; insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, 0, 0); } else if (status == 7) { diff --git a/dialer/src/dialerutils.cpp b/dialer/src/dialerutils.cpp index 0b14e04a..1d4b25c9 100644 --- a/dialer/src/dialerutils.cpp +++ b/dialer/src/dialerutils.cpp @@ -32,7 +32,7 @@ DialerUtils::~DialerUtils() { } -void DialerUtils::notifyMissedCall() +void DialerUtils::notifyMissedCall(const QString &caller, const QString &description) { qWarning() << "Missed Call."; @@ -42,7 +42,13 @@ void DialerUtils::notifyMissedCall() } m_callsNotification->setComponentName("plasma_dialer"); m_callsNotification->setIconName("call-start"); - m_callsNotification->setTitle(i18np("One call missed", "%1 calls missed", m_missedCalls)); + if (m_missedCalls == 1) { + m_callsNotification->setTitle(i18n("Missed call from %1", caller)); + m_callsNotification->setText(description); + } else { + m_callsNotification->setTitle(i18n("%1 calls missed", m_missedCalls)); + m_callsNotification->setText(i18n("Last call: %1", description)); + } QStringList actions; actions.append(i18n("View")); diff --git a/dialer/src/dialerutils.h b/dialer/src/dialerutils.h index dde482b6..53ae5855 100644 --- a/dialer/src/dialerutils.h +++ b/dialer/src/dialerutils.h @@ -31,7 +31,7 @@ public: DialerUtils(QObject *parent = 0); virtual ~DialerUtils(); - Q_INVOKABLE void notifyMissedCall(); + Q_INVOKABLE void notifyMissedCall(const QString &caller, const QString &description); Q_INVOKABLE void resetMissedCalls(); Q_SIGNALS: diff --git a/dialer/src/plasma_dialer.notifyrc b/dialer/src/plasma_dialer.notifyrc index c1c51e4b..14865a2e 100644 --- a/dialer/src/plasma_dialer.notifyrc +++ b/dialer/src/plasma_dialer.notifyrc @@ -1,6 +1,6 @@ [Global] IconName=call-start -Comment=Plasma Phone Dialer +Comment=Phone [Event/callMissed] Name=Missed Call From 5efb8207695f3459a3679f60b395ab8d8efc4505 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 27 Apr 2015 13:25:47 +0200 Subject: [PATCH 68/86] remove the correct call --- dialer/package/contents/ui/Dialer/History.qml | 2 +- .../contents/ui/Dialer/HistoryDelegate.qml | 2 +- dialer/package/contents/ui/main.qml | 21 ++++++++++--------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/dialer/package/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml index 48aa91ba..96e48d92 100644 --- a/dialer/package/contents/ui/Dialer/History.qml +++ b/dialer/package/contents/ui/Dialer/History.qml @@ -39,7 +39,7 @@ Item { PlasmaComponents.Label { anchors.centerIn: parent text: i18n("No recent calls") - visible: false + visible: historyModel.count == 0 } PlasmaExtras.ScrollArea { diff --git a/dialer/package/contents/ui/Dialer/HistoryDelegate.qml b/dialer/package/contents/ui/Dialer/HistoryDelegate.qml index 7dcaa30c..c14cd3e2 100644 --- a/dialer/package/contents/ui/Dialer/HistoryDelegate.qml +++ b/dialer/package/contents/ui/Dialer/HistoryDelegate.qml @@ -48,7 +48,7 @@ Item { easing.type: Easing.InOutQuad } ScriptAction { - script: removeCallFromHistory(index); + script: removeCallFromHistory(model.originalIndex); } } diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 98059514..b82403cd 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -106,18 +106,20 @@ ApplicationWindow { function(tx) { var rs = tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), datetime('now'), ?, ? )", [number, duration, callType]); - // Show all added greetings var rs = tx.executeSql('SELECT * FROM History where id=?', [rs.insertId]); for(var i = 0; i < rs.rows.length; i++) { - historyModel.append(rs.rows.item(i)); + var row = rs.rows.item(i); + row.originalIndex = historyModel.count; + historyModel.append(row); } } ) } - function removeCallFromHistory(id) { - var item = historyModel.get(id); + //index is historyModel row number, not db id and not sortmodel row number + function removeCallFromHistory(index) { + var item = historyModel.get(index); if (!item) { return; @@ -131,7 +133,8 @@ ApplicationWindow { } ) - historyModel.remove(id); + historyModel.remove(index); + } //END FUNCTIONS @@ -150,14 +153,12 @@ ApplicationWindow { //callType: wether is incoming, outgoing, unanswered tx.executeSql('CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, date DATE, time DATETIME, duration INTEGER, callType INTEGER)'); - // Add (another) greeting row - // tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), time('now'), ? )", ['+39000', 0]); - - // Show all added greetings var rs = tx.executeSql('SELECT * FROM History'); for(var i = 0; i < rs.rows.length; i++) { - historyModel.append(rs.rows.item(i)); + var row = rs.rows.item(i); + row.originalIndex = historyModel.count; + historyModel.append(row); } } ) From fc430058bfd831ae6fe2ad15b55a49f10e1da081 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 27 Apr 2015 13:33:48 +0200 Subject: [PATCH 69/86] pressed effect --- .../contents/ui/Dialpad/DialerButton.qml | 17 ++++++++++++++++- .../contents/ui/Dialpad/DialerIconButton.qml | 17 ++++++++++++++++- dialer/package/contents/ui/main.qml | 1 - 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/dialer/package/contents/ui/Dialpad/DialerButton.qml b/dialer/package/contents/ui/Dialpad/DialerButton.qml index b3a6c770..890a96af 100644 --- a/dialer/package/contents/ui/Dialpad/DialerButton.qml +++ b/dialer/package/contents/ui/Dialpad/DialerButton.qml @@ -18,7 +18,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.0 +import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents @@ -39,7 +39,22 @@ PlasmaComponents.Label { property var pressedCallback property var releasedCallback + Rectangle { + anchors.fill: parent + z: -1 + color: PlasmaCore.ColorScope.highlightColor + radius: units.smallSpacing + opacity: mouse.pressed ? 0.4 : 0 + Behavior on opacity { + OpacityAnimator { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } + MouseArea { + id: mouse anchors.fill: parent onPressed: { if (pressedCallback) { diff --git a/dialer/package/contents/ui/Dialpad/DialerIconButton.qml b/dialer/package/contents/ui/Dialpad/DialerIconButton.qml index 29be828b..a88a5280 100644 --- a/dialer/package/contents/ui/Dialpad/DialerIconButton.qml +++ b/dialer/package/contents/ui/Dialpad/DialerIconButton.qml @@ -18,7 +18,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.0 +import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents @@ -36,6 +36,20 @@ Item { property alias source: icon.source property alias text: label.text + Rectangle { + anchors.fill: parent + z: -1 + color: PlasmaCore.ColorScope.highlightColor + radius: units.smallSpacing + opacity: mouse.pressed ? 0.4 : 0 + Behavior on opacity { + OpacityAnimator { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } + Row { anchors.centerIn: parent PlasmaCore.IconItem { @@ -54,6 +68,7 @@ Item { } MouseArea { + id: mouse anchors.fill: parent onClicked: { if (callback) { diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index b82403cd..39ec3dca 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -134,7 +134,6 @@ ApplicationWindow { ) historyModel.remove(index); - } //END FUNCTIONS From 111223b727590cb0fab8c19134851988858fb444 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 27 Apr 2015 13:50:37 +0200 Subject: [PATCH 70/86] clear calls button tabbar to filter only missed calls --- dialer/package/contents/ui/Dialer/History.qml | 79 ++++++++++++++----- dialer/package/contents/ui/main.qml | 13 +++ 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/dialer/package/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml index 96e48d92..07ec51a8 100644 --- a/dialer/package/contents/ui/Dialer/History.qml +++ b/dialer/package/contents/ui/Dialer/History.qml @@ -42,29 +42,70 @@ Item { visible: historyModel.count == 0 } - PlasmaExtras.ScrollArea { + ColumnLayout { anchors.fill: parent - ListView { - id: view - model: PlasmaCore.SortFilterModel { - sourceModel: historyModel - sortRole: "time" - sortOrder: Qt.DescendingOrder - } - section { - property: "date" - labelPositioning: ViewSection.CurrentLabelAtStart - delegate: PlasmaComponents.ListItem { - //width: view.width - //height: childrenRect.height - //color: syspal.base - sectionDelegate: true - PlasmaComponents.Label { - text: Qt.formatDate(section, Qt.locale().dateFormat(Locale.LongFormat)); + visible: historyModel.count > 0 + PlasmaComponents.ToolBar { + Layout.fillWidth: true + tools: RowLayout { + id: toolBarLayout + PlasmaComponents.TabBar { + tabPosition: Qt.TopEdge + PlasmaComponents.TabButton { + iconSource: "call-start" + text: i18n("All") + onCheckedChanged: { + if (checked) { + filterModel.filterString = ""; + } + } + } + PlasmaComponents.TabButton { + iconSource: "list-remove" + text: i18n("Missed") + onCheckedChanged: { + if (checked) { + filterModel.filterString = "0"; + } + } } } + Item { + Layout.fillWidth: true + } + PlasmaComponents.Button { + text: i18n("Clear") + onClicked: clearHistory(); + } + } + } + PlasmaExtras.ScrollArea { + Layout.fillWidth: true + Layout.fillHeight: true + ListView { + id: view + model: PlasmaCore.SortFilterModel { + id: filterModel + sourceModel: historyModel + filterRole: "callType" + sortRole: "time" + sortOrder: Qt.DescendingOrder + } + section { + property: "date" + labelPositioning: ViewSection.CurrentLabelAtStart + delegate: PlasmaComponents.ListItem { + //width: view.width + //height: childrenRect.height + //color: syspal.base + sectionDelegate: true + PlasmaComponents.Label { + text: Qt.formatDate(section, Qt.locale().dateFormat(Locale.LongFormat)); + } + } + } + delegate: HistoryDelegate {} } - delegate: HistoryDelegate {} } } } \ No newline at end of file diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 39ec3dca..8b381dc0 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -135,6 +135,19 @@ ApplicationWindow { historyModel.remove(index); } + + function clearHistory() { + var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); + + db.transaction( + function(tx) { + tx.executeSql("DELETE from History"); + } + ) + + historyModel.clear(); + } + //END FUNCTIONS //BEGIN DATABASE From 8b8647ab43cdd4b9ec144dca1cdc643434a289fe Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 28 Apr 2015 11:18:56 +0200 Subject: [PATCH 71/86] ring with a loop notification --- dialer/package/contents/ui/Dialpad/Dialpad.qml | 16 ++++++++-------- dialer/package/contents/ui/main.qml | 3 +++ dialer/src/dialerutils.cpp | 16 ++++++++++++++++ dialer/src/dialerutils.h | 3 +++ dialer/src/plasma_dialer.notifyrc | 6 ++++++ 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/dialer/package/contents/ui/Dialpad/Dialpad.qml b/dialer/package/contents/ui/Dialpad/Dialpad.qml index 1303be73..9fa5c49d 100644 --- a/dialer/package/contents/ui/Dialpad/Dialpad.qml +++ b/dialer/package/contents/ui/Dialpad/Dialpad.qml @@ -37,16 +37,16 @@ GridLayout { Layout.fillHeight: true DialerButton { id: one; text: "1" } - DialerButton { text: "2" } - DialerButton { text: "3" } + DialerButton { text: "2"; sub: "ABC" } + DialerButton { text: "3"; sub: "DEF" } - DialerButton { text: "4" } - DialerButton { text: "5" } - DialerButton { text: "6" } + DialerButton { text: "4"; sub: "GHI" } + DialerButton { text: "5"; sub: "JKL" } + DialerButton { text: "6"; sub: "MNO" } - DialerButton { text: "7" } - DialerButton { text: "8" } - DialerButton { text: "9" } + DialerButton { text: "7"; sub: "PQRS" } + DialerButton { text: "8"; sub: "TUV" } + DialerButton { text: "9"; sub: "WXYZ" } DialerButton { text: "*"; } DialerButton { text: "0"; sub: "+"; } diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 8b381dc0..b3383ff9 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -54,6 +54,7 @@ ApplicationWindow { } else if (status == 5) { wasVisible = root.visible; root.visible = true; + dialerUtils.notifyRinging(); //Was STATUS_INCOMING now is STATUS_DISCONNECTED: Missed call! } else if (status == 7 && previousStatus == 5) { var prettyDate = Qt.formatTime(voiceCallmanager.activeVoiceCall.startedAt, Qt.locale().timeFormat(Locale.ShortFormat)); @@ -62,6 +63,8 @@ ApplicationWindow { insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, 0, 0); } else if (status == 7) { insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, voiceCallmanager.activeVoiceCall.duration, isIncoming ? 1 : 2); + } else { + dialerUtils.stopRinging(); } previousStatus = status; diff --git a/dialer/src/dialerutils.cpp b/dialer/src/dialerutils.cpp index 1d4b25c9..7c3c7eeb 100644 --- a/dialer/src/dialerutils.cpp +++ b/dialer/src/dialerutils.cpp @@ -76,4 +76,20 @@ void DialerUtils::resetMissedCalls() m_callsNotification.clear(); } +void DialerUtils::notifyRinging() +{ + if (!m_ringingNotification) { + m_ringingNotification = new KNotification("ringing", KNotification::Persistent|KNotification::LoopSound, 0); + m_ringingNotification->setComponentName("plasma_dialer"); + } + m_ringingNotification->sendEvent(); +} + +void DialerUtils::stopRinging() +{ + if (m_ringingNotification) { + m_ringingNotification->close(); + } +} + #include "moc_dialerutils.cpp" diff --git a/dialer/src/dialerutils.h b/dialer/src/dialerutils.h index 53ae5855..41cc218f 100644 --- a/dialer/src/dialerutils.h +++ b/dialer/src/dialerutils.h @@ -33,12 +33,15 @@ public: Q_INVOKABLE void notifyMissedCall(const QString &caller, const QString &description); Q_INVOKABLE void resetMissedCalls(); + Q_INVOKABLE void notifyRinging(); + Q_INVOKABLE void stopRinging(); Q_SIGNALS: void missedCallsActionTriggered(); private: QPointer m_callsNotification; + QPointer m_ringingNotification; int m_missedCalls; }; diff --git a/dialer/src/plasma_dialer.notifyrc b/dialer/src/plasma_dialer.notifyrc index 14865a2e..34ec63b5 100644 --- a/dialer/src/plasma_dialer.notifyrc +++ b/dialer/src/plasma_dialer.notifyrc @@ -7,3 +7,9 @@ Name=Missed Call Comment=A call has been missed Action=Popup +[Event/ringing] +Name=Ringing +Comment=The phone is ringing +Action=Sound +Sound=KDE-Sys-Question.ogg + From 75c672dc5ff048eaef33c3180244b9f965ec9145 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 28 Apr 2015 11:31:39 +0200 Subject: [PATCH 72/86] make sure to stop ringing --- dialer/package/contents/ui/main.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index b3383ff9..93a7095c 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -63,7 +63,9 @@ ApplicationWindow { insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, 0, 0); } else if (status == 7) { insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, voiceCallmanager.activeVoiceCall.duration, isIncoming ? 1 : 2); - } else { + } + + if (status != 5) { dialerUtils.stopRinging(); } From cff41cc6c0b1a61dac9b1521e560879c8292d30f Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 28 Apr 2015 12:23:36 +0200 Subject: [PATCH 73/86] remove date db column --- dialer/package/contents/ui/Dialer/History.qml | 5 +---- dialer/package/contents/ui/main.qml | 6 ++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dialer/package/contents/ui/Dialer/History.qml b/dialer/package/contents/ui/Dialer/History.qml index 07ec51a8..ea29fd4b 100644 --- a/dialer/package/contents/ui/Dialer/History.qml +++ b/dialer/package/contents/ui/Dialer/History.qml @@ -93,11 +93,8 @@ Item { } section { property: "date" - labelPositioning: ViewSection.CurrentLabelAtStart delegate: PlasmaComponents.ListItem { - //width: view.width - //height: childrenRect.height - //color: syspal.base + id: sectionItem sectionDelegate: true PlasmaComponents.Label { text: Qt.formatDate(section, Qt.locale().dateFormat(Locale.LongFormat)); diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 93a7095c..3b919da6 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -109,12 +109,13 @@ ApplicationWindow { db.transaction( function(tx) { - var rs = tx.executeSql("INSERT INTO History VALUES(NULL, ?, date('now'), datetime('now'), ?, ? )", [number, duration, callType]); + var rs = tx.executeSql("INSERT INTO History VALUES(NULL, ?, datetime('now'), ?, ? )", [number, duration, callType]); var rs = tx.executeSql('SELECT * FROM History where id=?', [rs.insertId]); for(var i = 0; i < rs.rows.length; i++) { var row = rs.rows.item(i); + row.date = Qt.formatDate(row.time, "yyyy-MM-dd"); row.originalIndex = historyModel.count; historyModel.append(row); } @@ -168,12 +169,13 @@ ApplicationWindow { function(tx) { // Create the database if it doesn't already exist //callType: wether is incoming, outgoing, unanswered - tx.executeSql('CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, date DATE, time DATETIME, duration INTEGER, callType INTEGER)'); + tx.executeSql('CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, time DATETIME, duration INTEGER, callType INTEGER)'); var rs = tx.executeSql('SELECT * FROM History'); for(var i = 0; i < rs.rows.length; i++) { var row = rs.rows.item(i); + row.date = Qt.formatDate(row.time, "yyyy-MM-dd"); row.originalIndex = historyModel.count; historyModel.append(row); } From c9ee41f3ba6f5a814f061e82976137705a680f0f Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 28 Apr 2015 12:29:14 +0200 Subject: [PATCH 74/86] fix calling --- dialer/package/contents/ui/main.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dialer/package/contents/ui/main.qml b/dialer/package/contents/ui/main.qml index 3b919da6..01a35fa4 100644 --- a/dialer/package/contents/ui/main.qml +++ b/dialer/package/contents/ui/main.qml @@ -90,11 +90,11 @@ ApplicationWindow { //BEGIN FUNCTIONS function call(number) { if (!voiceCallmanager.activeVoiceCall) { - console.log("Calling: " + status.text); + console.log("Calling: " + providerId + " " + number); voiceCallmanager.dial(providerId, number); } else { - console.log("Hanging up: " + status.text); + console.log("Hanging up: " + voiceCallmanager.activeVoiceCall.lineId); status.text = ''; var call = voiceCallmanager.activeVoiceCall; if (call) { From bdee5e84abf8aa8cb5dfa3fbf77b623979618aeb Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 28 Apr 2015 12:34:00 +0200 Subject: [PATCH 75/86] better layout for the letters in dialpad --- dialer/package/contents/ui/Dialpad/DialerButton.qml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dialer/package/contents/ui/Dialpad/DialerButton.qml b/dialer/package/contents/ui/Dialpad/DialerButton.qml index 890a96af..75931d93 100644 --- a/dialer/package/contents/ui/Dialpad/DialerButton.qml +++ b/dialer/package/contents/ui/Dialpad/DialerButton.qml @@ -94,6 +94,10 @@ PlasmaComponents.Label { text = parent.text; } + if (text.length > 1) { + return; + } + if (callback) { callback(text); } else if (pad.callback) { @@ -107,12 +111,13 @@ PlasmaComponents.Label { anchors { verticalCenter: parent.verticalCenter right: parent.right + rightMargin: units.largeSpacing } - height: parent.height * 0.6 - width: parent.width / 3 + height: parent.height * 0.4 + width: parent.width / 4 verticalAlignment: Qt.AlignVCenter visible: text.length > 0 - opacity: 0.7 + opacity: 0.6 font.pointSize: 1024 fontSizeMode: Text.Fit From 2c31673fc566afb3aad10ebdda4e981f790f6d4f Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 28 Apr 2015 12:45:26 +0200 Subject: [PATCH 76/86] wider bottom tabbar --- dialer/package/contents/ui/Dialer/DialPage.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dialer/package/contents/ui/Dialer/DialPage.qml b/dialer/package/contents/ui/Dialer/DialPage.qml index d832a765..3e0c252d 100644 --- a/dialer/package/contents/ui/Dialer/DialPage.qml +++ b/dialer/package/contents/ui/Dialer/DialPage.qml @@ -44,7 +44,8 @@ Column { id: tabbar height: units.gridUnit * 5 anchors { - horizontalCenter: parent.horizontalCenter + left: parent.left + right: parent.right } tabPosition: Qt.BottomEdge PlasmaComponents.TabButton { From 1456a2e7187b27c8a309b85a29d37475704b93a9 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 29 Apr 2015 10:39:40 +0200 Subject: [PATCH 77/86] call number commandline argument --- dialer/src/main.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dialer/src/main.cpp b/dialer/src/main.cpp index 7356386f..ff206fde 100644 --- a/dialer/src/main.cpp +++ b/dialer/src/main.cpp @@ -54,8 +54,14 @@ int main(int argc, char **argv) parser.addHelpOption(); parser.setApplicationDescription(description); - QCommandLineOption daemonOption("daemon", "Daemon mode. run without displaying anything."); + QCommandLineOption daemonOption(QStringList() << QStringLiteral("d") << + QStringLiteral("daemon"), + i18n("Daemon mode. run without displaying anything.")); + QCommandLineOption dialOption(QStringList() << QStringLiteral("c") << QStringLiteral("call"), + i18n("Call the given number"), + QStringLiteral("number")); + parser.addOption(dialOption); parser.addOption(daemonOption); parser.process(app); @@ -102,6 +108,11 @@ int main(int argc, char **argv) } window->setTitle(obj->package().metadata().name()); window->setIcon(QIcon::fromTheme(obj->package().metadata().iconName())); + + if (parser.isSet(dialOption)) { + qWarning() << "Calling" << parser.value(dialOption); + obj->rootObject()->metaObject()->invokeMethod(obj->rootObject(), "call", Q_ARG(QVariant, parser.value(dialOption))); + } } else { qWarning() << "Error loading the ApplicationWindow"; } From 6972b67c1860daa8692b94b31747f46ecce9605a Mon Sep 17 00:00:00 2001 From: Martin Klapetek Date: Tue, 5 May 2015 17:23:31 +0200 Subject: [PATCH 78/86] Initial phonebook application commit --- phonebook/contents/ui/ContactsList.qml | 175 +++++++++++++++++++++++++ phonebook/contents/ui/main.qml | 61 +++++++++ phonebook/metadata.desktop | 20 +++ 3 files changed, 256 insertions(+) create mode 100644 phonebook/contents/ui/ContactsList.qml create mode 100644 phonebook/contents/ui/main.qml create mode 100644 phonebook/metadata.desktop diff --git a/phonebook/contents/ui/ContactsList.qml b/phonebook/contents/ui/ContactsList.qml new file mode 100644 index 00000000..bf235df7 --- /dev/null +++ b/phonebook/contents/ui/ContactsList.qml @@ -0,0 +1,175 @@ +/* + * Copyright 2015 Martin Klapetek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.1 +import org.kde.people 1.0 as KPeople +import org.kde.kquickcontrolsaddons 2.0 as ExtraComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + + +PlasmaExtras.ScrollArea { + anchors.fill: parent + verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + + contentItem: ListView { + id: contactsList + + property bool delegateSelected: false + + clip: true + model: PlasmaCore.SortFilterModel { + sortRole: "display" + sourceModel: KPeople.PersonsModel { + id: contactsModel + } + } + + boundsBehavior: Flickable.StopAtBounds + + delegate: PlasmaComponents.ListItem { + height: units.gridUnit * 6 + enabled: true + clip: true + opacity: contactsList.delegateSelected && contactsList.currentIndex != index ? 0.4 : 1 + + onClicked: { + if (contactsList.delegateSelected) { + contactsList.currentIndex = -1; + contactsList.delegateSelected = false; + } else { + contactsList.currentIndex = index; + contactsList.delegateSelected = true; + } + + contactsList.toggleOverlayButtons(contactsList.delegateSelected); + } + + + RowLayout { + id: mainLayout + anchors.fill: parent + + ExtraComponents.QPixmapItem { + id: avatarLabel + + Layout.maximumWidth: parent.height + Layout.minimumWidth: parent.height + Layout.fillHeight: true + + pixmap: model.decoration + fillMode: ExtraComponents.QPixmapItem.PreserveAspectFit + smooth: true + } + + ColumnLayout { + Layout.fillHeight: true + Layout.fillWidth: true + + Label { + id: nickLabel + + Layout.fillWidth: true + + text: model.display + elide: Text.ElideRight + } + + Label { + id: dataLabel + + Layout.fillWidth: true + + text: "605-909-123" + elide: Text.ElideRight + } + + } + } + } + + function toggleOverlayButtons(show) { + if (show) { + settingsRect.parent = contactsList.currentItem + settingsRect.visible = true; + + callRect.parent = contactsList.currentItem + callRect.visible = true; + } else { + settingsRect.visible = false; + callRect.visible = false; + } + } + + Rectangle { + id: settingsRect + height: units.gridUnit * 6 + width: height + units.gridUnit * 2 + radius: 45 + z: 100 + visible: false + color: "lightblue" + + anchors { + left: parent.left + leftMargin: -width/2 + verticalCenter: parent.verticalCenter + } + + + PlasmaCore.IconItem { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: parent.height / 4 + source: "configure-shortcuts" + } + } + + Rectangle { + id: callRect + height: settingsRect.height + width: settingsRect.width + radius: height + z: 100 + visible: false + color: "lightgreen" + + anchors { + right: parent.right + rightMargin: -width/2 + verticalCenter: parent.verticalCenter + } + + PlasmaCore.IconItem { + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: parent.height / 4 + source: "call-start" + } + } + + highlight: PlasmaComponents.Highlight { + hover: contactsList.focus + } + highlightMoveDuration: 0 + } +} diff --git a/phonebook/contents/ui/main.qml b/phonebook/contents/ui/main.qml new file mode 100644 index 00000000..b0d3cd4b --- /dev/null +++ b/phonebook/contents/ui/main.qml @@ -0,0 +1,61 @@ +/* + * Copyright 2015 Martin Klapetek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.1 + +ApplicationWindow { + width: 720 + height: 1280 + visible: true + + toolBar: ToolBar { + RowLayout { + anchors.fill: parent + Layout.fillWidth: true + + ToolButton { + text: i18n("Settings") + iconName: "call-start" + } + + ToolButton { + text: i18n("Recent") + iconName: "appointment-new" + } + + ToolButton { + text: i18n("Alphabetical") + iconName: "im-user" + } + } + } + + ColumnLayout { + anchors.fill: parent + + ContactsList { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + + +} diff --git a/phonebook/metadata.desktop b/phonebook/metadata.desktop new file mode 100644 index 00000000..48d1aa1e --- /dev/null +++ b/phonebook/metadata.desktop @@ -0,0 +1,20 @@ +[Desktop Entry] +Comment= +Encoding=UTF-8 +Keywords= +Name=Phonebook +Type=Application +Icon=package_toys +Exec=kpackagelauncherqml -a org.kde.phone.phonebook +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Martin Klapetek +X-KDE-PluginInfo-Category=Miscellaneous +X-KDE-PluginInfo-Email=mklapetek@kde.org +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=org.kde.phone.phonebook +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website= +X-KDE-ServiceTypes=KPackage/GenericQML + +X-Plasma-MainScript=ui/main.qml +X-Plasma-RemoteLocation= From af40bd64b1296304d0eda2d99c4414cbedbc8440 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 5 May 2015 19:15:38 +0200 Subject: [PATCH 79/86] take contacts from kpeople --- CMakeLists.txt | 3 + .../package/contents/ui/Dialer/Contacts.qml | 100 +++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d78c4f7d..48075fa9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,9 @@ install(DIRECTORY compositor/ PATTERN Messages.sh EXCLUDE PATTERN dummydata EXCLUDE) +kpackage_install_package(phonebook org.kde.phone.phonebook genericqml) +install(FILES phonebook/metadata.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME org.kde.phone.dialer.desktop) + add_subdirectory(bin) add_subdirectory(qmlcomponents) add_subdirectory(services) diff --git a/dialer/package/contents/ui/Dialer/Contacts.qml b/dialer/package/contents/ui/Dialer/Contacts.qml index 541aec5a..20d9ba70 100644 --- a/dialer/package/contents/ui/Dialer/Contacts.qml +++ b/dialer/package/contents/ui/Dialer/Contacts.qml @@ -19,12 +19,110 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 +import org.kde.people 1.0 as KPeople +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras Item { PlasmaComponents.Label { anchors.centerIn: parent text: i18n("No contacts") + visible: contactsModel.count == 0 } -} \ No newline at end of file + + ColumnLayout { + anchors.fill: parent + //visible: contactsModel.count > 0 + + PlasmaComponents.ToolBar { + Layout.fillWidth: true + tools: RowLayout { + id: toolBarLayout + PlasmaComponents.TextField { + id: searchField + clearButtonShown: true + Layout.fillWidth: true + Layout.fillHeight: true + placeholderText: i18n("Search...") + } + } + } + + PlasmaExtras.ScrollArea { + Layout.fillWidth: true + Layout.fillHeight: true + ListView { + id: view + model: PlasmaCore.SortFilterModel { + id: filterModel + sourceModel: KPeople.PersonsModel { + id: contactsModel + } + sortRole: "display" + filterRole: "display" + filterRegExp: ".*"+searchField.text+".*" + sortOrder: Qt.AscendingOrder + } + section { + property: "display" + criteria: ViewSection.FirstCharacter + delegate: PlasmaComponents.ListItem { + id: sectionItem + sectionDelegate: true + PlasmaComponents.Label { + text: section + } + } + } + delegate: PlasmaComponents.ListItem { + RowLayout { + id: delegateLayout + + KQuickControlsAddons.QPixmapItem { + id: avatarLabel + + Layout.minimumWidth: units.iconSizes.medium + Layout.maximumWidth: Layout.minimumWidth + Layout.minimumHeight: Layout.minimumWidth + Layout.maximumHeight: Layout.minimumWidth + + pixmap: model.decoration + fillMode: ExtraComponents.QPixmapItem.PreserveAspectFit + smooth: true + } + + ColumnLayout { + Layout.fillHeight: true + Layout.fillWidth: true + + PlasmaComponents.Label { + id: nickLabel + + Layout.fillWidth: true + + text: model.display + elide: Text.ElideRight + } + + PlasmaComponents.Label { + id: dataLabel + + Layout.fillWidth: true + + text: "605-909-123" + elide: Text.ElideRight + } + + } + } + MouseArea { + anchors.fill: parent + onClicked: call(12345) + } + } + } + } + } +} From c267e6e689fe901daec2eb03ee4ee010afb463ab Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 5 May 2015 19:29:48 +0200 Subject: [PATCH 80/86] support call://number as paramenter --- dialer/package/metadata.desktop | 1 + dialer/src/main.cpp | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/dialer/package/metadata.desktop b/dialer/package/metadata.desktop index 54766985..2a3ab8c2 100644 --- a/dialer/package/metadata.desktop +++ b/dialer/package/metadata.desktop @@ -18,3 +18,4 @@ Exec=plasmaphonedialer X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= +MimeType=x-scheme-handler/call diff --git a/dialer/src/main.cpp b/dialer/src/main.cpp index ff206fde..9930ecd9 100644 --- a/dialer/src/main.cpp +++ b/dialer/src/main.cpp @@ -57,11 +57,9 @@ int main(int argc, char **argv) QCommandLineOption daemonOption(QStringList() << QStringLiteral("d") << QStringLiteral("daemon"), i18n("Daemon mode. run without displaying anything.")); - QCommandLineOption dialOption(QStringList() << QStringLiteral("c") << QStringLiteral("call"), - i18n("Call the given number"), - QStringLiteral("number")); - parser.addOption(dialOption); + parser.addPositionalArgument("number", i18n("Call the given number")); + parser.addOption(daemonOption); parser.process(app); @@ -109,9 +107,13 @@ int main(int argc, char **argv) window->setTitle(obj->package().metadata().name()); window->setIcon(QIcon::fromTheme(obj->package().metadata().iconName())); - if (parser.isSet(dialOption)) { - qWarning() << "Calling" << parser.value(dialOption); - obj->rootObject()->metaObject()->invokeMethod(obj->rootObject(), "call", Q_ARG(QVariant, parser.value(dialOption))); + if (!parser.positionalArguments().isEmpty()) { + QString numberArg = parser.positionalArguments().first(); + if (numberArg.startsWith("call://")) { + numberArg = numberArg.mid(7); + } + qWarning() << "Calling" << numberArg; + obj->rootObject()->metaObject()->invokeMethod(obj->rootObject(), "call", Q_ARG(QVariant, numberArg)); } } else { qWarning() << "Error loading the ApplicationWindow"; From 356c504183a2216cb759843caa16899a0c3ee91b Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 5 May 2015 19:38:31 +0200 Subject: [PATCH 81/86] manage arguments in activateRequested --- dialer/src/main.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dialer/src/main.cpp b/dialer/src/main.cpp index 9930ecd9..623c4c95 100644 --- a/dialer/src/main.cpp +++ b/dialer/src/main.cpp @@ -95,10 +95,16 @@ int main(int argc, char **argv) QWindow *window = qobject_cast(obj->rootObject()); if (window) { QObject::connect(&service, &KDBusService::activateRequested, [=](const QStringList &arguments, const QString &workingDirectory) { - Q_UNUSED(arguments) Q_UNUSED(workingDirectory); window->show(); window->requestActivate(); + if (arguments.length() > 0) { + QString numberArg = arguments[1]; + if (numberArg.startsWith("call://")) { + numberArg = numberArg.mid(7); + } + obj->rootObject()->metaObject()->invokeMethod(obj->rootObject(), "call", Q_ARG(QVariant, numberArg)); + } }); if (!parser.isSet(daemonOption)) { window->show(); From 5ad851df7c3662b588a157f44b7f9b9564e21e50 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 5 May 2015 19:41:10 +0200 Subject: [PATCH 82/86] correct namespace --- dialer/package/contents/ui/Dialer/Contacts.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dialer/package/contents/ui/Dialer/Contacts.qml b/dialer/package/contents/ui/Dialer/Contacts.qml index 20d9ba70..6351527b 100644 --- a/dialer/package/contents/ui/Dialer/Contacts.qml +++ b/dialer/package/contents/ui/Dialer/Contacts.qml @@ -89,7 +89,7 @@ Item { Layout.maximumHeight: Layout.minimumWidth pixmap: model.decoration - fillMode: ExtraComponents.QPixmapItem.PreserveAspectFit + fillMode: KQuickControlsAddons.QPixmapItem.PreserveAspectFit smooth: true } From 4dbdce0fc467f9928facc366fba331449ddea6f2 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 5 May 2015 19:45:13 +0200 Subject: [PATCH 83/86] use parameter to support the call:// uris --- dialer/package/metadata.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dialer/package/metadata.desktop b/dialer/package/metadata.desktop index 2a3ab8c2..e263428d 100644 --- a/dialer/package/metadata.desktop +++ b/dialer/package/metadata.desktop @@ -14,7 +14,7 @@ X-KDE-PluginInfo-Name=org.kde.phone.dialer X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-KDE-ServiceTypes=KPackage/Generic -Exec=plasmaphonedialer +Exec=plasmaphonedialer %u X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= From 015c03b75d4a479bf0bf4a31f15acd23d6935a68 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 5 May 2015 20:13:10 +0200 Subject: [PATCH 84/86] invoke the dialer --- phonebook/contents/ui/ContactsList.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phonebook/contents/ui/ContactsList.qml b/phonebook/contents/ui/ContactsList.qml index bf235df7..91b001d4 100644 --- a/phonebook/contents/ui/ContactsList.qml +++ b/phonebook/contents/ui/ContactsList.qml @@ -165,6 +165,11 @@ PlasmaExtras.ScrollArea { anchors.leftMargin: parent.height / 4 source: "call-start" } + MouseArea { + anchors.fill: parent + //TODO: needs the proper number + onClicked: Qt.openUrlExternally("call://" + "605909123") + } } highlight: PlasmaComponents.Highlight { From f45474eba3b0e431f695845736b859021ac35033 Mon Sep 17 00:00:00 2001 From: Martin Klapetek Date: Tue, 12 May 2015 13:27:29 +0200 Subject: [PATCH 85/86] Add the custom SectionScroller This now copies to look from the mockup --- .../contents/ui/CustomSectionScroller.qml | 205 ++++++++++++++++++ .../contents/ui/private/SectionScroller.js | 71 ++++++ 2 files changed, 276 insertions(+) create mode 100644 phonebook/contents/ui/CustomSectionScroller.qml create mode 100644 phonebook/contents/ui/private/SectionScroller.js diff --git a/phonebook/contents/ui/CustomSectionScroller.qml b/phonebook/contents/ui/CustomSectionScroller.qml new file mode 100644 index 00000000..287b3111 --- /dev/null +++ b/phonebook/contents/ui/CustomSectionScroller.qml @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 + +import "private/SectionScroller.js" as Sections +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.components 2.0 as PlasmaComponents + +/** + * Similar to a ScrollBar or a ScrollDecorator. + * + * It's interactive and works on ListViews that have section.property set, so + * its contents are categorized. + * + * An indicator will say to what category the user scrolled to. Useful for + * things like address books or things sorted by date. Don't use with models + * too big (thousands of items) because it implies loading all the items to + * memory, as well loses precision. + * + * @inherit QtQuick.Item + */ +Item { + id: root + + /** + * The listview the sectionScroller will operate on. This component doesn't + * work with Flickable or GridView. + */ + property ListView listView + + onListViewChanged: { + if (listView && listView.model) + internal.initDirtyObserver(); + } + + Connections { + target: listView + onModelChanged: { + if (listView && listView.model) { + internal.initDirtyObserver() + } + } + } + + implicitWidth: scrollBar.implicitWidth + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + } + } + + anchors { + right: listView.right + top: listView.top + bottom: listView.bottom + } + + + PlasmaComponents.RangeModel { + id: range + + minimumValue: 0 + maximumValue: Math.max(0, listView.contentHeight - listView.height) + stepSize: 0 + //inverted: true + positionAtMinimum: root.width*2 + positionAtMaximum: root.height - root.width*2 + value: listView.contentY + onPositionChanged: { + var section = Sections.closestSection(position/listView.height); + if (section) { + if (listView.section.criteria == ViewSection.FirstCharacter) { + sectionLabel.text = section[0]; + } else { + sectionLabel.text = section; + } + } + } + + } + + PlasmaComponents.ScrollBar { + id: scrollBar + flickableItem: listView + anchors.fill: parent + interactive: true + } + PlasmaCore.FrameSvgItem { + id: tooltip + imagePath: "widgets/background" + width: units.gridUnit * 5 + margins.left + margins.right + height: sectionLabel.height + subtitle.height + margins.top + margins.bottom + + ColumnLayout { + anchors.centerIn: parent + + PlasmaExtras.Title { + id: sectionLabel + Layout.fillWidth: true + Layout.fillHeight: true + horizontalAlignment: Text.AlignHCenter + } + + PlasmaComponents.Label { + id: subtitle + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + visible: text.length > 0 + text: "Thursday, 7th" + } + + } + y: 0 + x: -listView.width/2 - width/2 + + opacity: sectionLabel.text && scrollBar.pressed ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + } + } + } + + + Timer { + id: dirtyTimer + interval: 250 + onTriggered: { + Sections.initSectionData(listView); + internal.modelDirty = false; + tooltip.visible = Sections._sections.length > 1 + } + } + QtObject { + id: internal + + property bool modelDirty: false + function initDirtyObserver() { + Sections.initSectionData(listView); + tooltip.visible = Sections._sections.length > 1 + function dirtyObserver() { + if (!internal.modelDirty) { + internal.modelDirty = true; + dirtyTimer.running = true; + } + } + + if (listView.model.countChanged) + listView.model.countChanged.connect(dirtyObserver); + + if (listView.model.itemsChanged) + listView.model.itemsChanged.connect(dirtyObserver); + + if (listView.model.itemsInserted) + listView.model.itemsInserted.connect(dirtyObserver); + + if (listView.model.itemsMoved) + listView.model.itemsMoved.connect(dirtyObserver); + + if (listView.model.itemsRemoved) + listView.model.itemsRemoved.connect(dirtyObserver); + } + } + Accessible.role: Accessible.ScrollBar +} diff --git a/phonebook/contents/ui/private/SectionScroller.js b/phonebook/contents/ui/private/SectionScroller.js new file mode 100644 index 00000000..7b987fed --- /dev/null +++ b/phonebook/contents/ui/private/SectionScroller.js @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var _sectionData = []; +var _sections = []; + +function initSectionData(list) { + if (!list || !list.model) return; + _sectionData = []; + _sections = []; + var current = ""; + var prop = list.section.property; + + for (var i = 0, count = list.model.count; i < count; i++) { + var item = list.model.get(i); + if (item[prop] !== current) { + current = item[prop]; + _sections.push(current); + _sectionData.push({ index: i, header: current }); + } + } +} + +function closestSection(pos) { + var tmp = (_sections.length) * pos; + var val = Math.ceil(tmp) // TODO: better algorithm + val = val < 2 ? 1 : val; + return _sections[val-1]; +} + +function indexOf(sectionName) { + var val = _sectionData[_sections.indexOf(sectionName)].index; + return val === 0 || val > 0 ? val : -1; +} From 4267242ac0bdf0c6f05491b841c07b86389351f4 Mon Sep 17 00:00:00 2001 From: Martin Klapetek Date: Tue, 12 May 2015 13:28:37 +0200 Subject: [PATCH 86/86] [phonebook] Make use of the SectionScroller --- phonebook/contents/ui/ContactsList.qml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/phonebook/contents/ui/ContactsList.qml b/phonebook/contents/ui/ContactsList.qml index 91b001d4..2844be5e 100644 --- a/phonebook/contents/ui/ContactsList.qml +++ b/phonebook/contents/ui/ContactsList.qml @@ -29,13 +29,16 @@ import org.kde.plasma.extras 2.0 as PlasmaExtras PlasmaExtras.ScrollArea { anchors.fill: parent - verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff contentItem: ListView { id: contactsList property bool delegateSelected: false + + section.property: "display" + section.criteria: ViewSection.FirstCharacter clip: true model: PlasmaCore.SortFilterModel { sortRole: "display" @@ -45,6 +48,10 @@ PlasmaExtras.ScrollArea { } boundsBehavior: Flickable.StopAtBounds + highlight: PlasmaComponents.Highlight { + hover: contactsList.focus + } + highlightMoveDuration: 0 delegate: PlasmaComponents.ListItem { height: units.gridUnit * 6 @@ -172,9 +179,9 @@ PlasmaExtras.ScrollArea { } } - highlight: PlasmaComponents.Highlight { - hover: contactsList.focus + CustomSectionScroller { + listView: contactsList } - highlightMoveDuration: 0 + } }