mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-08-03 10:14:45 +00:00
Merge branch 'mart/newContainmentShell'
Conflicts: CMakeLists.txt shell/contents/views/Desktop.qml
This commit is contained in:
commit
264bff2854
61 changed files with 5230 additions and 699 deletions
|
|
@ -23,10 +23,10 @@ include(ECMGenerateHeaders)
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
include(FeatureSummary)
|
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)
|
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n)
|
||||||
find_package(KF5 REQUIRED COMPONENTS PlasmaQuick I18n)
|
find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons Notifications)
|
||||||
|
|
||||||
|
|
||||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
@ -44,9 +44,13 @@ install(DIRECTORY compositor/
|
||||||
PATTERN Messages.sh EXCLUDE
|
PATTERN Messages.sh EXCLUDE
|
||||||
PATTERN dummydata 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(bin)
|
||||||
add_subdirectory(qmlcomponents)
|
add_subdirectory(qmlcomponents)
|
||||||
add_subdirectory(services)
|
add_subdirectory(services)
|
||||||
add_subdirectory(settingsmodules)
|
add_subdirectory(settingsmodules)
|
||||||
add_subdirectory(applets)
|
add_subdirectory(applets)
|
||||||
add_subdirectory(containments)
|
add_subdirectory(containments)
|
||||||
|
add_subdirectory(dialer)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
|
||||||
plasma_install_package(clock org.kde.phone.clock)
|
plasma_install_package(clock org.kde.phone.clock)
|
||||||
plasma_install_package(notifications org.kde.phone.notifications)
|
plasma_install_package(notifications org.kde.phone.notifications)
|
||||||
|
|
||||||
|
add_subdirectory(quicksettings)
|
||||||
|
|
|
||||||
|
|
@ -18,25 +18,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 2.0 as PlasmaComponents
|
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: root
|
id: notificationItem
|
||||||
|
|
||||||
|
|
||||||
height: units.gridUnit * 2
|
height: units.gridUnit * (expanded ? (actionsLayout.visible ? 6 : 4) : 2) + background.margins.top + background.margins.bottom
|
||||||
width: parent.width
|
width: parent.width
|
||||||
anchors.bottomMargin: 10
|
anchors.bottomMargin: 10
|
||||||
drag.axis: Drag.XAxis
|
drag.axis: Drag.XAxis
|
||||||
drag.target: root
|
drag.target: notificationItem
|
||||||
|
|
||||||
property bool expanded: false
|
property bool expanded: false
|
||||||
property var textGradient: Gradient {
|
property string source: model.source
|
||||||
GradientStop { position: 1.0; color: "#FF00000C" }
|
property var actions: model.actions
|
||||||
GradientStop { position: 0.0; color: "#00000C00" }
|
|
||||||
}
|
|
||||||
property color textGradientOverlay: "#9900000C"
|
|
||||||
|
|
||||||
Behavior on x {
|
Behavior on x {
|
||||||
SpringAnimation { spring: 2; damping: 0.2 }
|
SpringAnimation { spring: 2; damping: 0.2 }
|
||||||
|
|
@ -46,112 +45,105 @@ MouseArea {
|
||||||
SpringAnimation { spring: 5; damping: 0.3 }
|
SpringAnimation { spring: 5; damping: 0.3 }
|
||||||
}
|
}
|
||||||
|
|
||||||
onExpandedChanged: {
|
|
||||||
if (expanded && body) {
|
|
||||||
height = units.gridUnit * 4;
|
|
||||||
} else {
|
|
||||||
height = units.gridUnit * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
if (drag.active) {
|
if (drag.active) {
|
||||||
if (x > width / 4 || x < width / -4) {
|
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);
|
notificationsModel.remove(index);
|
||||||
} else {
|
} else {
|
||||||
x = 0;
|
x = 0;
|
||||||
}
|
}
|
||||||
} else if (body) {
|
} else if (body || actions) {
|
||||||
expanded = !expanded;
|
expanded = !expanded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.FrameSvgItem {
|
||||||
id: icon
|
id: background
|
||||||
width: units.iconSizes.medium
|
imagePath: "widgets/background"
|
||||||
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 {
|
anchors {
|
||||||
left: icon.right
|
fill: parent
|
||||||
leftMargin: units.largeSpacing
|
rightMargin: -notificationItem.width
|
||||||
|
leftMargin: units.gridUnit
|
||||||
|
}
|
||||||
|
colorGroup: PlasmaCore.ColorScope.colorGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
PlasmaComponents.ToolButton {
|
||||||
id: roundedRect
|
|
||||||
height: parent.height
|
|
||||||
width: parent.width * 2
|
|
||||||
radius: height //Math.max(height, units.gridUnit)
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
top: parent.top
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
leftMargin: units.gridUnit / 2
|
||||||
|
|
||||||
gradient: root.textGradient
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: height / 2
|
|
||||||
color: textGradientOverlay
|
|
||||||
}
|
}
|
||||||
|
iconSource: "window-close"
|
||||||
|
flat: false
|
||||||
|
onClicked: {
|
||||||
|
notificationsModel.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
PlasmaComponents.Label {
|
||||||
id: summaryArea
|
|
||||||
width: parent.width - icon.width - rounded.width - (units.largeSpacing * 2)
|
|
||||||
height: parent.height
|
|
||||||
anchors {
|
anchors {
|
||||||
left: root.expanded ? root.left : rounded.right
|
left: parent.left
|
||||||
right: root.right
|
verticalCenter: parent.verticalCenter
|
||||||
top: parent.top
|
leftMargin: units.gridUnit * 3
|
||||||
}
|
}
|
||||||
|
color: PlasmaCore.ColorScope.textColor
|
||||||
gradient: root.textGradient
|
text: model.appName
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: textGradientOverlay
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlasmaComponents.Label {
|
PlasmaComponents.Label {
|
||||||
id: summaryText
|
id: summaryText
|
||||||
anchors.fill: parent
|
anchors {
|
||||||
clip: true
|
right: icon.left
|
||||||
horizontalAlignment: root.expanded ? Qt.AlignHCenter : Qt.AlignLeft
|
verticalCenter: parent.verticalCenter
|
||||||
|
rightMargin: units.smallSpacing
|
||||||
|
}
|
||||||
|
horizontalAlignment: Qt.AlignRight
|
||||||
verticalAlignment: Qt.AlignVCenter
|
verticalAlignment: Qt.AlignVCenter
|
||||||
color: PlasmaCore.ColorScope.textColor
|
color: PlasmaCore.ColorScope.textColor
|
||||||
text: summary + (root.expanded ? (body ? "\n" + body : '') :
|
text: summary + (notificationItem.expanded ? (body ? "\n" + body : '') :
|
||||||
(body ? '...' : ''))
|
(body ? '...' : ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
PlasmaCore.IconItem {
|
||||||
|
id: icon
|
||||||
Rectangle {
|
|
||||||
id: extraArea
|
|
||||||
width: parent.width
|
|
||||||
height: parent.width
|
|
||||||
anchors {
|
anchors {
|
||||||
left: summaryArea.right
|
right: notificationItem.right
|
||||||
top: parent.top
|
verticalCenter: parent.verticalCenter
|
||||||
bottom: parent.bottom
|
}
|
||||||
|
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)
|
||||||
|
notificationsModel.remove(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gradient: root.textGradient
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: textGradientOverlay
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,9 +25,59 @@ import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
property int notificationId: 0
|
||||||
|
|
||||||
Layout.minimumHeight: notificationView.contentsHeight
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
PlasmaCore.DataSource {
|
||||||
id: notificationsSource
|
id: notificationsSource
|
||||||
|
|
||||||
|
|
@ -58,7 +108,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
homescreen.addNotification(
|
root.addNotification(
|
||||||
sourceName,
|
sourceName,
|
||||||
data,
|
data,
|
||||||
actions);
|
actions);
|
||||||
|
|
@ -70,17 +120,26 @@ Item {
|
||||||
id: notificationsModel
|
id: notificationsModel
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
|
source: "call1Source"
|
||||||
appIcon: "call-start"
|
appIcon: "call-start"
|
||||||
summary: "Missed call from Joe"
|
summary: "Missed call from Joe"
|
||||||
|
appName: "Phone"
|
||||||
body: "Called at 8:42 from +41 56 373 37 31"
|
body: "Called at 8:42 from +41 56 373 37 31"
|
||||||
|
actions: []
|
||||||
}
|
}
|
||||||
ListElement {
|
ListElement {
|
||||||
|
source: "im1Source"
|
||||||
appIcon: "im-google"
|
appIcon: "im-google"
|
||||||
|
appName: "Message"
|
||||||
summary: "July: Hey! Are you around?"
|
summary: "July: Hey! Are you around?"
|
||||||
|
actions: []
|
||||||
}
|
}
|
||||||
ListElement {
|
ListElement {
|
||||||
|
source: "im2Source"
|
||||||
appIcon: "im-google"
|
appIcon: "im-google"
|
||||||
|
appName: "Message"
|
||||||
summary: "July: Hello?"
|
summary: "July: Hello?"
|
||||||
|
actions: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
19
applets/quicksettings/CMakeLists.txt
Normal file
19
applets/quicksettings/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||||
|
|
||||||
4
applets/quicksettings/Messages.sh
Executable file
4
applets/quicksettings/Messages.sh
Executable file
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <notmart@gmail.com>
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
85
applets/quicksettings/package/contents/ui/Delegate.qml
Normal file
85
applets/quicksettings/package/contents/ui/Delegate.qml
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <notmart@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 {
|
||||||
|
id: delegateRoot
|
||||||
|
property bool toggled: model.enabled
|
||||||
|
spacing: units.smallSpacing
|
||||||
|
implicitWidth: flow.width / 2 - units.largeSpacing / 2
|
||||||
|
Rectangle {
|
||||||
|
Layout.minimumWidth: units.iconSizes.large
|
||||||
|
Layout.minimumHeight: width
|
||||||
|
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)
|
||||||
|
|
||||||
|
PlasmaCore.IconItem {
|
||||||
|
colorGroup: PlasmaCore.ColorScope.colorGroup
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: units.smallSpacing
|
||||||
|
}
|
||||||
|
source: model.icon
|
||||||
|
MouseArea {
|
||||||
|
id: iconMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (delegateRoot.toggle) {
|
||||||
|
delegateRoot.toggle();
|
||||||
|
} else if (model.toggleFunction) {
|
||||||
|
root[model.toggleFunction]();
|
||||||
|
} else if (model.settingsCommand) {
|
||||||
|
plasmoid.nativeInterface.executeCommand(model.settingsCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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: {
|
||||||
|
if (model.settingsCommand) {
|
||||||
|
plasmoid.nativeInterface.executeCommand(model.settingsCommand);
|
||||||
|
} else if (model.toggleFunction) {
|
||||||
|
root[model.toggleFunction]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
128
applets/quicksettings/package/contents/ui/main.qml
Normal file
128
applets/quicksettings/package/contents/ui/main.qml
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <notmart@gmail.com>
|
||||||
|
*
|
||||||
|
* 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: root
|
||||||
|
|
||||||
|
function toggleAirplane() {
|
||||||
|
print("toggle airplane mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
Plasmoid.preferredRepresentation: plasmoid.fullRepresentation
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: settingsModel
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
text: "Settings"
|
||||||
|
icon: "configure"
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: "active-settings"
|
||||||
|
toggleFunction: ""
|
||||||
|
delegate: ""
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Mobile network"
|
||||||
|
icon: "network-mobile-80"
|
||||||
|
enabled: true
|
||||||
|
settingsCommand: ""
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Airplane mode"
|
||||||
|
icon: "flightmode-on"
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: ""
|
||||||
|
toggleFunction: "toggleAirplane"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Bluetooth"
|
||||||
|
icon: "preferences-system-bluetooth"
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: ""
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Wireless"
|
||||||
|
icon: "network-wireless-on"
|
||||||
|
enabled: true
|
||||||
|
settingsCommand: "active-settings -m org.kde.satellite.settings.wifi"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Alarms"
|
||||||
|
icon: "korgac"
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: ""
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Notifications"
|
||||||
|
icon: "preferences-desktop-notification"
|
||||||
|
enabled: true
|
||||||
|
settingsCommand: ""
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Brightness"
|
||||||
|
icon: "video-display-brightness"
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: "active-settings -m org.kde.active.settings.powermanagement"
|
||||||
|
delegate: "BrightnessDelegate"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Flashlight"
|
||||||
|
icon: "package_games_puzzle"
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: ""
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
text: "Location"
|
||||||
|
icon: "plasmaapplet-location"
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
id: flow
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: units.largeSpacing
|
||||||
|
}
|
||||||
|
spacing: units.largeSpacing
|
||||||
|
Repeater {
|
||||||
|
model: settingsModel
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
applets/quicksettings/package/metadata.desktop
Normal file
22
applets/quicksettings/package/metadata.desktop
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
[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-Library=plasma_applet_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=
|
||||||
43
applets/quicksettings/quicksettings.cpp
Normal file
43
applets/quicksettings/quicksettings.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2015 Marco Martin <mart@kde.org> *
|
||||||
|
* *
|
||||||
|
* 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 <QDebug>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
QuickSettings::QuickSettings(QObject *parent, const QVariantList &args)
|
||||||
|
: Plasma::Applet(parent, args)
|
||||||
|
{
|
||||||
|
setHasConfigurationInterface(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QuickSettings::~QuickSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickSettings::executeCommand(const QString &command)
|
||||||
|
{
|
||||||
|
qWarning()<<"Executing"<<command;
|
||||||
|
QProcess::startDetached(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
K_EXPORT_PLASMA_APPLET_WITH_JSON(quicksettings, QuickSettings, "metadata.json")
|
||||||
|
|
||||||
|
#include "quicksettings.moc"
|
||||||
43
applets/quicksettings/quicksettings.h
Normal file
43
applets/quicksettings/quicksettings.h
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2015 Marco Martin <mart@kde.org> *
|
||||||
|
*
|
||||||
|
* *
|
||||||
|
* 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 <Plasma/Applet>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
|
|
||||||
plasma_install_package(homescreen org.kde.phone.homescreen)
|
plasma_install_package(homescreen org.kde.phone.homescreen)
|
||||||
|
plasma_install_package(panel org.kde.phone.panel)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@
|
||||||
<entry name="AppletOrder" type="String">
|
<entry name="AppletOrder" type="String">
|
||||||
<label>encoded order of items</label>
|
<label>encoded order of items</label>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry name="AppOrder" type="StringList">
|
||||||
|
<label>order of apps</label>
|
||||||
|
<default>org.kde.phone.dialer.desktop</default>
|
||||||
|
</entry>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
</kcfg>
|
</kcfg>
|
||||||
|
|
|
||||||
|
|
@ -8,124 +8,18 @@ Item {
|
||||||
width: applicationsView.cellWidth
|
width: applicationsView.cellWidth
|
||||||
height: width
|
height: width
|
||||||
|
|
||||||
property int idx: index
|
property var modelData: model
|
||||||
property int oldIdx: -1
|
property bool isDropTarget: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole
|
||||||
|
|
||||||
Rectangle {
|
opacity: isDropTarget ? 0.3 : 1
|
||||||
anchors.fill: parent
|
|
||||||
color: PlasmaCore.ColorScope.textColor
|
|
||||||
radius: units.gridUnit
|
|
||||||
opacity: (delegateItem.drag.target != null) ? 0.4 : 0
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: units.longDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//animate index change
|
|
||||||
onIdxChanged: {
|
|
||||||
if (delegateItem.drag.target != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldIdx < 0) {
|
|
||||||
oldIdx = idx;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
PlasmaCore.IconItem {
|
||||||
id: icon
|
id: icon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.height / 2
|
width: parent.height / 2
|
||||||
height: width
|
height: width
|
||||||
source: model.ApplicationIconRole
|
source: modelData.ApplicationIconRole
|
||||||
scale: root.reorderingApps && !delegateItem.drag.target ? 0.6 : 1
|
scale: root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole != modelData.ApplicationStorageIdRole ? 0.6 : 1
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: units.longDuration
|
duration: units.longDuration
|
||||||
|
|
@ -149,9 +43,8 @@ Item {
|
||||||
verticalAlignment: Qt.AlignVCenter
|
verticalAlignment: Qt.AlignVCenter
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
|
|
||||||
text: model.ApplicationNameRole
|
text: modelData.ApplicationNameRole
|
||||||
font.pixelSize: theme.smallestFont.pixelSize
|
font.pixelSize: theme.smallestFont.pixelSize
|
||||||
color: PlasmaCore.ColorScope.textColor
|
color: PlasmaCore.ColorScope.textColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
Item {
|
PlasmaCore.ColorScope {
|
||||||
|
colorGroup: PlasmaCore.Theme.NormalColorGroup
|
||||||
|
|
||||||
PlasmaCore.FrameSvgItem {
|
PlasmaCore.FrameSvgItem {
|
||||||
z: -1
|
z: -1
|
||||||
imagePath: "widgets/background"
|
imagePath: "widgets/background"
|
||||||
|
|
@ -11,10 +13,17 @@ Item {
|
||||||
topMargin: -margins.top
|
topMargin: -margins.top
|
||||||
bottomMargin: -margins.bottom
|
bottomMargin: -margins.bottom
|
||||||
}
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: parent.margins.top
|
||||||
|
bottomMargin: parent.margins.bottom
|
||||||
|
}
|
||||||
|
color: PlasmaCore.ColorScope.backgroundColor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opacity: 0.6
|
height: applicationsView.cellWidth
|
||||||
height: Math.max(100, units.gridUnit * 2.5)
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
y: parent.height / 2 - height / 2
|
y: parent.height / 2 - height / 2
|
||||||
x: 0
|
x: 0
|
||||||
|
|
|
||||||
|
|
@ -16,18 +16,18 @@
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
|
* 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 QtQuick.Layouts 1.1
|
||||||
|
|
||||||
import org.kde.plasma.plasmoid 2.0
|
import org.kde.plasma.plasmoid 2.0
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 2.0 as PlasmaComponents
|
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 org.kde.satellite.components 0.1 as SatelliteComponents
|
||||||
|
|
||||||
import "plasmapackage:/code/LayoutManager.js" as LayoutManager
|
import "plasmapackage:/code/LayoutManager.js" as LayoutManager
|
||||||
|
|
||||||
Item {
|
MouseEventListener {
|
||||||
id: root
|
id: root
|
||||||
width: 480
|
width: 480
|
||||||
height: 640
|
height: 640
|
||||||
|
|
@ -42,6 +42,12 @@ Item {
|
||||||
LayoutManager.save();
|
LayoutManager.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Plasmoid.onFocusChanged: {
|
||||||
|
if (!plasmoid.focus && applicationsView.contentY > -(applicationsView.headerItem.height - root.height/2)) {
|
||||||
|
applicationsView.contentY = -root.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function addApplet(applet, x, y) {
|
function addApplet(applet, x, y) {
|
||||||
var container = appletContainerComponent.createObject(appletsSpace.layout)
|
var container = appletContainerComponent.createObject(appletsSpace.layout)
|
||||||
container.visible = true
|
container.visible = true
|
||||||
|
|
@ -65,7 +71,8 @@ Item {
|
||||||
container.animationsEnabled = false;
|
container.animationsEnabled = false;
|
||||||
|
|
||||||
if (appletsSpace.lastSpacer.parent === appletsSpace.layout) {
|
if (appletsSpace.lastSpacer.parent === appletsSpace.layout) {
|
||||||
before = appletsSpace.lastSpacer;
|
//Uncomment to make the spacer the last element again
|
||||||
|
// before = appletsSpace.lastSpacer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (before) {
|
if (before) {
|
||||||
|
|
@ -92,24 +99,28 @@ Item {
|
||||||
LayoutManager.lastSpacer = appletsSpace.lastSpacer;
|
LayoutManager.lastSpacer = appletsSpace.lastSpacer;
|
||||||
LayoutManager.restore();
|
LayoutManager.restore();
|
||||||
applicationsView.contentY = -root.height;
|
applicationsView.contentY = -root.height;
|
||||||
|
|
||||||
|
appListModel.appOrder = plasmoid.configuration.AppOrder;
|
||||||
|
appListModel.loadApplications();
|
||||||
}
|
}
|
||||||
|
|
||||||
SatelliteComponents.ApplicationListModel {
|
SatelliteComponents.ApplicationListModel {
|
||||||
id: appListModel
|
id: appListModel
|
||||||
|
onAppOrderChanged: {
|
||||||
|
plasmoid.configuration.AppOrder = appListModel.appOrder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: autoScrollTimer
|
id: autoScrollTimer
|
||||||
property bool scrollDown: true
|
property bool scrollDown: true
|
||||||
repeat: true
|
repeat: true
|
||||||
interval: 10
|
interval: 1500
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
applicationsView.contentY += scrollDown ? 8 : -8;
|
scrollAnim.to = scrollDown ?
|
||||||
if (applicationsView.draggingItem) {
|
Math.min(applicationsView.contentItem.height - applicationsView.headerItem.height - root.height, applicationsView.contentY + root.height/2) :
|
||||||
applicationsView.draggingItem.y += scrollDown ? 8 : -8;
|
Math.max(0, applicationsView.contentY - root.height/2);
|
||||||
|
scrollAnim.running = true;
|
||||||
applicationsView.draggingItem.updateRow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +139,7 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.minimumWidth: root.width
|
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.preferredWidth: root.width
|
||||||
Layout.preferredHeight: Layout.minimumHeight
|
Layout.preferredHeight: Layout.minimumHeight
|
||||||
|
|
@ -138,15 +149,197 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
onPressAndHold: {
|
||||||
color: Qt.rgba(0, 0, 0, 0.9 * (Math.min(applicationsView.contentY + root.height, root.height) / root.height))
|
var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y);
|
||||||
anchors.fill: parent
|
//in favorites area?
|
||||||
|
var item;
|
||||||
|
if (applicationsView.headerItem.favoritesStrip.contains(pos)) {
|
||||||
|
item = applicationsView.headerItem.favoritesStrip.itemAt(pos.x, pos.y);
|
||||||
|
} else {
|
||||||
|
pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y);
|
||||||
|
item = applicationsView.itemAt(pos.x, pos.y)
|
||||||
|
}
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applicationsView.dragData = new Object;
|
||||||
|
applicationsView.dragData.ApplicationNameRole = item.modelData.ApplicationNameRole;
|
||||||
|
applicationsView.dragData.ApplicationIconRole = item.modelData.ApplicationIconRole;
|
||||||
|
applicationsView.dragData.ApplicationStorageIdRole = item.modelData.ApplicationStorageIdRole;
|
||||||
|
applicationsView.dragData.ApplicationEntryPathRole = item.modelData.ApplicationEntryPathRole;
|
||||||
|
applicationsView.dragData.ApplicationOriginalRowRole = item.modelData.ApplicationOriginalRowRole;
|
||||||
|
|
||||||
|
dragDelegate.modelData = applicationsView.dragData;
|
||||||
|
applicationsView.interactive = false;
|
||||||
|
root.reorderingApps = true;
|
||||||
|
dragDelegate.x = Math.floor(mouse.x / root.buttonHeight) * root.buttonHeight
|
||||||
|
dragDelegate.y = Math.floor(mouse.y / root.buttonHeight) * root.buttonHeight
|
||||||
|
dragDelegate.xTarget = mouse.x - dragDelegate.width/2;
|
||||||
|
dragDelegate.yTarget = mouse.y - dragDelegate.width/2;
|
||||||
|
dragDelegate.opacity = 1;
|
||||||
|
}
|
||||||
|
onPositionChanged: {
|
||||||
|
if (!applicationsView.dragData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dragDelegate.x = mouse.x - dragDelegate.width/2;
|
||||||
|
dragDelegate.y = mouse.y - dragDelegate.height/2;
|
||||||
|
|
||||||
|
var pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y);
|
||||||
|
|
||||||
|
//in favorites area?
|
||||||
|
if (applicationsView.headerItem.favoritesStrip.contains(mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y))) {
|
||||||
|
pos.y = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newRow = (Math.round(applicationsView.width / applicationsView.cellWidth) * Math.floor(pos.y / applicationsView.cellHeight) + Math.floor(pos.x / applicationsView.cellWidth));
|
||||||
|
|
||||||
|
if (applicationsView.dragData.ApplicationOriginalRowRole != newRow) {
|
||||||
|
appListModel.moveItem(applicationsView.dragData.ApplicationOriginalRowRole, newRow);
|
||||||
|
applicationsView.dragData.ApplicationOriginalRowRole = newRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y);
|
||||||
|
//FAVORITES
|
||||||
|
if (applicationsView.headerItem.favoritesStrip.contains(pos)) {
|
||||||
|
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;
|
||||||
|
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();
|
||||||
|
autoScrollTimer.running = false;
|
||||||
|
scrollUpIndicator.opacity = 0;
|
||||||
|
scrollDownIndicator.opacity = 0;
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
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 {
|
PlasmaCore.ColorScope {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: PlasmaCore.ColorScope.backgroundColor
|
||||||
|
opacity: 0.9 * (Math.min(applicationsView.contentY + root.height, root.height) / root.height)
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaCore.Svg {
|
||||||
|
id: arrowsSvg
|
||||||
|
imagePath: "widgets/arrows"
|
||||||
|
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||||
|
}
|
||||||
|
PlasmaCore.SvgItem {
|
||||||
|
id: scrollUpIndicator
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 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
|
||||||
|
property int xTarget
|
||||||
|
property int yTarget
|
||||||
|
|
||||||
|
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 {
|
GridView {
|
||||||
id: applicationsView
|
id: applicationsView
|
||||||
anchors {
|
anchors {
|
||||||
|
|
@ -156,7 +349,7 @@ Item {
|
||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
|
|
||||||
property Item draggingItem
|
property var dragData
|
||||||
|
|
||||||
cellWidth: root.buttonHeight
|
cellWidth: root.buttonHeight
|
||||||
cellHeight: cellWidth
|
cellHeight: cellWidth
|
||||||
|
|
@ -183,29 +376,50 @@ Item {
|
||||||
duration: units.longDuration
|
duration: units.longDuration
|
||||||
easing.type: Easing.InOutQuad
|
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
|
//clip: true
|
||||||
delegate: HomeLauncher {}
|
delegate: HomeLauncher {
|
||||||
|
visible: index > 3
|
||||||
|
}
|
||||||
header: MouseArea {
|
header: MouseArea {
|
||||||
z: 999
|
z: 999
|
||||||
property Item layout: appletsLayout
|
property Item layout: appletsLayout
|
||||||
property Item lastSpacer: spacer
|
property Item lastSpacer: spacer
|
||||||
|
property Item favoritesStrip: favoritesView
|
||||||
width: root.width
|
width: root.width
|
||||||
height: mainLayout.Layout.minimumHeight
|
height: mainLayout.Layout.minimumHeight
|
||||||
|
property int margin: stripe.height + units.gridUnit * 2
|
||||||
|
|
||||||
onPressAndHold: {
|
onPressAndHold: {
|
||||||
|
print(favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y)))
|
||||||
|
if (!favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y))) {
|
||||||
plasmoid.action("configure").trigger();
|
plasmoid.action("configure").trigger();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: mainLayout
|
id: mainLayout
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
bottomMargin: stripe.height + units.gridUnit * 2
|
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: root.height
|
Layout.minimumHeight: root.height
|
||||||
|
Layout.maximumHeight: root.height
|
||||||
Clock {
|
Clock {
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
|
@ -226,13 +440,20 @@ Item {
|
||||||
}
|
}
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: appletsLayout
|
id: appletsLayout
|
||||||
Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height)
|
|
||||||
Item {
|
Item {
|
||||||
id: spacer
|
id: spacer
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
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 {
|
SatelliteStripe {
|
||||||
id: stripe
|
id: stripe
|
||||||
|
|
@ -240,48 +461,42 @@ Item {
|
||||||
property int viewPos: applicationsView.contentItem.height * applicationsView.visibleArea.yPosition
|
property int viewPos: applicationsView.contentItem.height * applicationsView.visibleArea.yPosition
|
||||||
|
|
||||||
y: Math.max(viewPos,
|
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 {
|
PlasmaCore.Svg {
|
||||||
id: stripeIcons
|
id: stripeIcons
|
||||||
imagePath: Qt.resolvedUrl("../images/homescreenicons.svg")
|
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
|
anchors.fill: parent
|
||||||
property int columns: 4
|
property int columns: 4
|
||||||
property alias buttonHeight: stripe.height
|
interactive: false
|
||||||
|
flow: GridView.FlowTopToBottom
|
||||||
|
cellWidth: root.buttonHeight
|
||||||
|
cellHeight: cellWidth
|
||||||
|
|
||||||
HomeLauncherSvg {
|
model: appListModel
|
||||||
id: phoneIcon
|
delegate: HomeLauncher {}
|
||||||
svg: stripeIcons
|
|
||||||
elementId: "phone"
|
move: Transition {
|
||||||
callback: function() {
|
NumberAnimation {
|
||||||
console.log("Start phone")
|
duration: units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
properties: "x,y"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
moveDisplaced: Transition {
|
||||||
HomeLauncherSvg {
|
NumberAnimation {
|
||||||
id: messagingIcon
|
duration: units.longDuration
|
||||||
svg: stripeIcons
|
easing.type: Easing.InOutQuad
|
||||||
elementId: "messaging"
|
properties: "x,y"
|
||||||
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") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -310,7 +525,7 @@ Item {
|
||||||
radius: width
|
radius: width
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
y: applicationsView.height * applicationsView.visibleArea.yPosition
|
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 {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: units.longDuration
|
duration: units.longDuration
|
||||||
|
|
|
||||||
189
containments/panel/contents/code/LayoutManager.js
Normal file
189
containments/panel/contents/code/LayoutManager.js
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
containments/panel/contents/config/main.xml
Normal file
14
containments/panel/contents/config/main.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||||
|
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||||
|
<kcfgfile name=""/>
|
||||||
|
|
||||||
|
<group name="General">
|
||||||
|
<entry name="AppletOrder" type="String">
|
||||||
|
<label>encoded order of items</label>
|
||||||
|
</entry>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</kcfg>
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import QtQuick.Window 2.0
|
import QtQuick.Window 2.2
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
|
|
@ -26,8 +26,10 @@ Window {
|
||||||
flags: Qt.WindowDoesNotAcceptFocus
|
flags: Qt.WindowDoesNotAcceptFocus
|
||||||
|
|
||||||
property int offset: 0
|
property int offset: 0
|
||||||
|
property int overShoot: units.gridUnit * 2
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
property alias contents: contentArea.data
|
||||||
|
|
||||||
function updateState() {
|
function updateState() {
|
||||||
var delta = offset - mouseArea.startOffset;
|
var delta = offset - mouseArea.startOffset;
|
||||||
|
|
@ -50,15 +52,17 @@ Window {
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
y: units.iconSizes.small
|
y: 0
|
||||||
width: window.width
|
width: window.width
|
||||||
height: window.height - y
|
height: window.height - y
|
||||||
clip: true
|
clip: true
|
||||||
state: "closed"
|
state: "closed"
|
||||||
|
drag.filterChildren: true
|
||||||
|
|
||||||
property int oldMouseY: 0
|
property int oldMouseY: 0
|
||||||
property int startOffset: units.iconSizes.large;
|
property int startOffset: units.iconSizes.large;
|
||||||
property string startState: "closed"
|
property string startState: "closed"
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
startState = state;
|
startState = state;
|
||||||
startOffset = window.offset;
|
startOffset = window.offset;
|
||||||
|
|
@ -67,26 +71,63 @@ Window {
|
||||||
window.offset = startOffset;
|
window.offset = startOffset;
|
||||||
}
|
}
|
||||||
onPositionChanged: {
|
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;
|
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 {
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.6-Math.abs(slidingArea.y/slidingArea.height))
|
||||||
|
}
|
||||||
|
PlasmaCore.ColorScope {
|
||||||
id: slidingArea
|
id: slidingArea
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height/1.5
|
||||||
y: Math.min(0, -height + window.offset)
|
y: Math.min(0, -height + window.offset)
|
||||||
|
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||||
color: Qt.rgba(0, 0, 0, 0.7)
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width / 4
|
anchors.fill: parent
|
||||||
height: units.gridUnit/2
|
|
||||||
color: "yellow"
|
Item {
|
||||||
|
id: contentArea
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
fill: parent
|
||||||
bottom: parent.bottom
|
topMargin: overShoot
|
||||||
bottomMargin: units.gridUnit/2
|
}
|
||||||
|
}
|
||||||
|
color: PlasmaCore.ColorScope.backgroundColor
|
||||||
|
|
||||||
|
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 +144,7 @@ Window {
|
||||||
name: "open"
|
name: "open"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: window
|
target: window
|
||||||
offset: slidingArea.height
|
offset: slidingArea.height - overShoot
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
|
|
@ -127,7 +168,7 @@ Window {
|
||||||
PropertyAnimation {
|
PropertyAnimation {
|
||||||
target: window
|
target: window
|
||||||
duration: units.longDuration
|
duration: units.longDuration
|
||||||
easing: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
properties: "offset"
|
properties: "offset"
|
||||||
}
|
}
|
||||||
ScriptAction {
|
ScriptAction {
|
||||||
309
containments/panel/contents/ui/main.qml
Normal file
309
containments/panel/contents/ui/main.qml
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
property QtObject expandedApplet
|
||||||
|
|
||||||
|
Containment.onAppletAdded: {
|
||||||
|
addApplet(applet, x, y);
|
||||||
|
LayoutManager.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addApplet(applet, x, y) {
|
||||||
|
var container = appletContainerComponent.createObject(layout)
|
||||||
|
container.visible = true
|
||||||
|
print("Applet added: " + applet)
|
||||||
|
|
||||||
|
var appletWidth = applet.width;
|
||||||
|
var appletHeight = applet.height;
|
||||||
|
applet.parent = container;
|
||||||
|
container.applet = applet;
|
||||||
|
//applet.anchors.fill = container;
|
||||||
|
applet.anchors.left = container.left;
|
||||||
|
applet.anchors.right = container.right;
|
||||||
|
applet.height = units.iconSizes.medium
|
||||||
|
applet.visible = true;
|
||||||
|
container.visible = true;
|
||||||
|
|
||||||
|
// If the provided position is valid, use it.
|
||||||
|
if (x >= 0 && y >= 0) {
|
||||||
|
var index = LayoutManager.insertAtCoordinates(container, x , y);
|
||||||
|
|
||||||
|
// Fall through to determining an appropriate insert position.
|
||||||
|
} else {
|
||||||
|
var before = null;
|
||||||
|
|
||||||
|
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 = layout;
|
||||||
|
LayoutManager.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: appletsLayout
|
||||||
|
Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: appletContainerComponent
|
||||||
|
Item {
|
||||||
|
property Item applet
|
||||||
|
Layout.fillWidth: true
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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: {
|
||||||
|
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.visibility = Qt.WindowFullScreen;
|
||||||
|
}
|
||||||
|
onPositionChanged: {
|
||||||
|
//var factor = (mouse.y - oldMouseY > 0) ? (1 - Math.max(0, (slidingArea.y + slidingPanel.overShoot) / slidingPanel.overShoot)) : 1
|
||||||
|
var factor = 1;
|
||||||
|
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
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: lastSpacer
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
id: layout
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: units.smallSpacing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
containments/panel/metadata.desktop
Normal file
18
containments/panel/metadata.desktop
Normal file
|
|
@ -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
|
||||||
5
dialer/CMakeLists.txt
Normal file
5
dialer/CMakeLists.txt
Normal file
|
|
@ -0,0 +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)
|
||||||
99
dialer/package/contents/ui/Call/AnswerSwipe.qml
Normal file
99
dialer/package/contents/ui/Call/AnswerSwipe.qml
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
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
|
||||||
|
height: parent.height
|
||||||
|
width: height
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
44
dialer/package/contents/ui/Call/Avatar.qml
Normal file
44
dialer/package/contents/ui/Call/Avatar.qml
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
201
dialer/package/contents/ui/Call/CallPage.qml
Normal file
201
dialer/package/contents/ui/Call/CallPage.qml
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Aaron Seigo <aseigo@kde.org>
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import "../Dialpad"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: callPage
|
||||||
|
|
||||||
|
state: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.statusText : "disconnected"
|
||||||
|
property int status: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.status : 0
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status != 1) {
|
||||||
|
dialerButton.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: activeCallUi
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: topFlickable
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumHeight: parent.height/2
|
||||||
|
|
||||||
|
contentWidth: topContents.width
|
||||||
|
contentHeight: topContents.height
|
||||||
|
interactive: status == 1;
|
||||||
|
Row {
|
||||||
|
id: topContents
|
||||||
|
Avatar {
|
||||||
|
width: topFlickable.width
|
||||||
|
height: topFlickable.height
|
||||||
|
}
|
||||||
|
Dialpad {
|
||||||
|
width: topFlickable.width
|
||||||
|
height: topFlickable.height
|
||||||
|
|
||||||
|
callback: function (string) {
|
||||||
|
if (voiceCallmanager.activeVoiceCall) {
|
||||||
|
voiceCallmanager.activeVoiceCall.sendDtmf(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
Layout.minimumHeight: implicitHeight
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
font.pointSize: theme.defaultFont.pointSize * 2
|
||||||
|
text: voiceCallmanager.activeVoiceCall ? voiceCallmanager.activeVoiceCall.lineId : ""
|
||||||
|
}
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: implicitHeight
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
text: {
|
||||||
|
if (!voiceCallmanager.activeVoiceCall) {
|
||||||
|
return '';
|
||||||
|
//STATUS_DIALING
|
||||||
|
} else if (voiceCallmanager.activeVoiceCall.status == 3) {
|
||||||
|
return i18n("Calling...");
|
||||||
|
} else if (voiceCallmanager.activeVoiceCall.duration > 0) {
|
||||||
|
return secondsToTimeString(voiceCallmanager.activeVoiceCall.duration);
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlasmaComponents.ButtonRow {
|
||||||
|
opacity: status == 1 ? 1 : 0
|
||||||
|
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"
|
||||||
|
checkable: true
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (checked) {
|
||||||
|
topSlideAnim.to = topFlickable.width;
|
||||||
|
} else {
|
||||||
|
topSlideAnim.to = 0;
|
||||||
|
}
|
||||||
|
topSlideAnim.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.minimumHeight: units.gridUnit * 5
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
dialer/package/contents/ui/Dialer/Contacts.qml
Normal file
128
dialer/package/contents/ui/Dialer/Contacts.qml
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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.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
|
||||||
|
}
|
||||||
|
|
||||||
|
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: KQuickControlsAddons.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
dialer/package/contents/ui/Dialer/DialPage.qml
Normal file
67
dialer/package/contents/ui/Dialer/DialPage.qml
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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 {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
105
dialer/package/contents/ui/Dialer/Dialer.qml
Normal file
105
dialer/package/contents/ui/Dialer/Dialer.qml
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Aaron Seigo <aseigo@kde.org>
|
||||||
|
* Copyright 2014 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
import "../Dialpad"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dialer
|
||||||
|
|
||||||
|
property alias numberEntryText: status.text
|
||||||
|
|
||||||
|
property string providerId: voiceCallmanager.providers.id(0)
|
||||||
|
|
||||||
|
function addNumber(number) {
|
||||||
|
status.text = status.text + number
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: dialPadArea
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
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.pointSize: 1024
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialpad {
|
||||||
|
callback: function (string) {
|
||||||
|
addNumber(string);
|
||||||
|
}
|
||||||
|
pressedCallback: function (string) {
|
||||||
|
voiceCallmanager.startDtmfTone(string);
|
||||||
|
}
|
||||||
|
releasedCallback: function (string) {
|
||||||
|
voiceCallmanager.stopDtmfTone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: function() {
|
||||||
|
call(status.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.minimumWidth: dialPadArea.width/3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
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(text) {
|
||||||
|
if (status.text.length > 0) {
|
||||||
|
status.text = status.text.substr(0, status.text.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
dialer/package/contents/ui/Dialer/History.qml
Normal file
108
dialer/package/contents/ui/Dialer/History.qml
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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 {
|
||||||
|
|
||||||
|
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 {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: i18n("No recent calls")
|
||||||
|
visible: historyModel.count == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
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"
|
||||||
|
delegate: PlasmaComponents.ListItem {
|
||||||
|
id: sectionItem
|
||||||
|
sectionDelegate: true
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
text: Qt.formatDate(section, Qt.locale().dateFormat(Locale.LongFormat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: HistoryDelegate {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
dialer/package/contents/ui/Dialer/HistoryDelegate.qml
Normal file
122
dialer/package/contents/ui/Dialer/HistoryDelegate.qml
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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(model.originalIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.time, Qt.locale().timeFormat(Locale.ShortFormat));
|
||||||
|
}
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
text: i18n("Duration: %1", secondsToTimeString(model.duration));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
125
dialer/package/contents/ui/Dialpad/DialerButton.qml
Normal file
125
dialer/package/contents/ui/Dialpad/DialerButton.qml
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Aaron Seigo <aseigo@kde.org>
|
||||||
|
* Copyright 2014 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
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
|
||||||
|
font.pointSize: 1024
|
||||||
|
fontSizeMode: Text.VerticalFit
|
||||||
|
|
||||||
|
property alias sub: longHold.text
|
||||||
|
property var callback
|
||||||
|
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) {
|
||||||
|
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(parent.text);
|
||||||
|
} else if (pad.callback) {
|
||||||
|
pad.callback(parent.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressAndHold: {
|
||||||
|
var text;
|
||||||
|
if (longHold.visible) {
|
||||||
|
text = longHold.text;
|
||||||
|
} else {
|
||||||
|
text = parent.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.length > 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(text);
|
||||||
|
} else if (pad.callback) {
|
||||||
|
pad.callback(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: longHold
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: units.largeSpacing
|
||||||
|
}
|
||||||
|
height: parent.height * 0.4
|
||||||
|
width: parent.width / 4
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
visible: text.length > 0
|
||||||
|
opacity: 0.6
|
||||||
|
|
||||||
|
font.pointSize: 1024
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
}
|
||||||
|
}
|
||||||
96
dialer/package/contents/ui/Dialpad/DialerIconButton.qml
Normal file
96
dialer/package/contents/ui/Dialpad/DialerIconButton.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Aaron Seigo <aseigo@kde.org>
|
||||||
|
* Copyright 2014 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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: buttonRoot
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
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
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
id: mouse
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (callback) {
|
||||||
|
callback(parent.text);
|
||||||
|
} else {
|
||||||
|
addNumber(parent.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressAndHold: {
|
||||||
|
var text;
|
||||||
|
if (longHold.visible) {
|
||||||
|
text = longHold.text;
|
||||||
|
} else {
|
||||||
|
text = parent.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(text);
|
||||||
|
} else if (pad.callback) {
|
||||||
|
pad.callback(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
dialer/package/contents/ui/Dialpad/Dialpad.qml
Normal file
55
dialer/package/contents/ui/Dialpad/Dialpad.qml
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Aaron Seigo <aseigo@kde.org>
|
||||||
|
* Copyright 2014 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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 var callback
|
||||||
|
property var pressedCallback
|
||||||
|
property var releasedCallback
|
||||||
|
|
||||||
|
property int buttonHeight: parent.height / 6
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
DialerButton { id: one; text: "1" }
|
||||||
|
DialerButton { text: "2"; sub: "ABC" }
|
||||||
|
DialerButton { text: "3"; sub: "DEF" }
|
||||||
|
|
||||||
|
DialerButton { text: "4"; sub: "GHI" }
|
||||||
|
DialerButton { text: "5"; sub: "JKL" }
|
||||||
|
DialerButton { text: "6"; sub: "MNO" }
|
||||||
|
|
||||||
|
DialerButton { text: "7"; sub: "PQRS" }
|
||||||
|
DialerButton { text: "8"; sub: "TUV" }
|
||||||
|
DialerButton { text: "9"; sub: "WXYZ" }
|
||||||
|
|
||||||
|
DialerButton { text: "*"; }
|
||||||
|
DialerButton { text: "0"; sub: "+"; }
|
||||||
|
DialerButton { text: "#" }
|
||||||
|
}
|
||||||
|
|
||||||
296
dialer/package/contents/ui/main.qml
Normal file
296
dialer/package/contents/ui/main.qml
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2014 Aaron Seigo <aseigo@kde.org>
|
||||||
|
* Copyright 2014 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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.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
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
//BEGIN PROPERTIES
|
||||||
|
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
|
||||||
|
//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
|
||||||
|
} 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));
|
||||||
|
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) {
|
||||||
|
insertCallInHistory(voiceCallmanager.activeVoiceCall.lineId, voiceCallmanager.activeVoiceCall.duration, isIncoming ? 1 : 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != 5) {
|
||||||
|
dialerUtils.stopRinging();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
//BEGIN FUNCTIONS
|
||||||
|
function call(number) {
|
||||||
|
if (!voiceCallmanager.activeVoiceCall) {
|
||||||
|
console.log("Calling: " + providerId + " " + number);
|
||||||
|
voiceCallmanager.dial(providerId, number);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log("Hanging up: " + voiceCallmanager.activeVoiceCall.lineId);
|
||||||
|
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, ?, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//index is historyModel row number, not db id and not sortmodel row number
|
||||||
|
function removeCallFromHistory(index) {
|
||||||
|
var item = historyModel.get(index);
|
||||||
|
|
||||||
|
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=?", [item.id]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
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, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//END DATABASE
|
||||||
|
|
||||||
|
//BEGIN MODELS
|
||||||
|
ListModel {
|
||||||
|
id: historyModel
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
VoiceCallManager {
|
||||||
|
id: voiceCallmanager
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//END MODELS
|
||||||
|
|
||||||
|
//BEGIN UI
|
||||||
|
PlasmaExtras.ConditionalLoader {
|
||||||
|
anchors.fill: parent
|
||||||
|
when: root.visible && root.status == 0
|
||||||
|
source: Qt.resolvedUrl("Dialer/DialPage.qml")
|
||||||
|
z: root.status == 0 ? 2 : 0
|
||||||
|
opacity: root.status == 0 ? 1 : 0
|
||||||
|
Behavior on opacity {
|
||||||
|
OpacityAnimator {
|
||||||
|
duration: units.shortDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaExtras.ConditionalLoader {
|
||||||
|
anchors.fill: parent
|
||||||
|
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
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//END UI
|
||||||
|
}
|
||||||
21
dialer/package/metadata.desktop
Normal file
21
dialer/package/metadata.desktop
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
[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=plasmaphonedialer %u
|
||||||
|
|
||||||
|
X-Plasma-MainScript=ui/main.qml
|
||||||
|
X-Plasma-RemoteLocation=
|
||||||
|
MimeType=x-scheme-handler/call
|
||||||
26
dialer/src/CMakeLists.txt
Normal file
26
dialer/src/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
|
||||||
|
set(plasmaphonedialer_SRCS
|
||||||
|
main.cpp
|
||||||
|
dialerutils.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
|
||||||
|
KF5::Notifications
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS plasmaphonedialer ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||||
|
install(FILES plasma_dialer.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR})
|
||||||
95
dialer/src/dialerutils.cpp
Normal file
95
dialer/src/dialerutils.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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 <QDebug>
|
||||||
|
|
||||||
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
DialerUtils::DialerUtils(QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_missedCalls(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DialerUtils::~DialerUtils()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialerUtils::notifyMissedCall(const QString &caller, const QString &description)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
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"));
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
49
dialer/src/dialerutils.h
Normal file
49
dialer/src/dialerutils.h
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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 <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <KNotification>
|
||||||
|
|
||||||
|
class DialerUtils : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
DialerUtils(QObject *parent = 0);
|
||||||
|
virtual ~DialerUtils();
|
||||||
|
|
||||||
|
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 <KNotification> m_callsNotification;
|
||||||
|
QPointer <KNotification> m_ringingNotification;
|
||||||
|
int m_missedCalls;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
130
dialer/src/main.cpp
Normal file
130
dialer/src/main.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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 <QApplication>
|
||||||
|
|
||||||
|
#include "dialerutils.h"
|
||||||
|
|
||||||
|
#include <klocalizedstring.h>
|
||||||
|
#include <qcommandlineparser.h>
|
||||||
|
#include <qcommandlineoption.h>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QtQml>
|
||||||
|
|
||||||
|
#include <kpackage/package.h>
|
||||||
|
#include <kpackage/packageloader.h>
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQmlExpression>
|
||||||
|
#include <QQmlProperty>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#include <kdeclarative/qmlobject.h>
|
||||||
|
#include <KAboutData>
|
||||||
|
#include <KDBusService>
|
||||||
|
|
||||||
|
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(QStringList() << QStringLiteral("d") <<
|
||||||
|
QStringLiteral("daemon"),
|
||||||
|
i18n("Daemon mode. run without displaying anything."));
|
||||||
|
|
||||||
|
parser.addPositionalArgument("number", i18n("Call the given number"));
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
DialerUtils *dialerUtils = new DialerUtils;
|
||||||
|
obj->engine()->rootContext()->setContextProperty("dialerUtils", QVariant::fromValue(dialerUtils));
|
||||||
|
|
||||||
|
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<QWindow *>(obj->rootObject());
|
||||||
|
if (window) {
|
||||||
|
QObject::connect(&service, &KDBusService::activateRequested, [=](const QStringList &arguments, const QString &workingDirectory) {
|
||||||
|
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();
|
||||||
|
window->requestActivate();
|
||||||
|
}
|
||||||
|
window->setTitle(obj->package().metadata().name());
|
||||||
|
window->setIcon(QIcon::fromTheme(obj->package().metadata().iconName()));
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
15
dialer/src/plasma_dialer.notifyrc
Normal file
15
dialer/src/plasma_dialer.notifyrc
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
[Global]
|
||||||
|
IconName=call-start
|
||||||
|
Comment=Phone
|
||||||
|
|
||||||
|
[Event/callMissed]
|
||||||
|
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
|
||||||
|
|
||||||
187
phonebook/contents/ui/ContactsList.qml
Normal file
187
phonebook/contents/ui/ContactsList.qml
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Martin Klapetek <mklapetek@kde.org>
|
||||||
|
*
|
||||||
|
* 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.ScrollBarAlwaysOff
|
||||||
|
|
||||||
|
contentItem: ListView {
|
||||||
|
id: contactsList
|
||||||
|
|
||||||
|
property bool delegateSelected: false
|
||||||
|
|
||||||
|
|
||||||
|
section.property: "display"
|
||||||
|
section.criteria: ViewSection.FirstCharacter
|
||||||
|
clip: true
|
||||||
|
model: PlasmaCore.SortFilterModel {
|
||||||
|
sortRole: "display"
|
||||||
|
sourceModel: KPeople.PersonsModel {
|
||||||
|
id: contactsModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
highlight: PlasmaComponents.Highlight {
|
||||||
|
hover: contactsList.focus
|
||||||
|
}
|
||||||
|
highlightMoveDuration: 0
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
//TODO: needs the proper number
|
||||||
|
onClicked: Qt.openUrlExternally("call://" + "605909123")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSectionScroller {
|
||||||
|
listView: contactsList
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
205
phonebook/contents/ui/CustomSectionScroller.qml
Normal file
205
phonebook/contents/ui/CustomSectionScroller.qml
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
61
phonebook/contents/ui/main.qml
Normal file
61
phonebook/contents/ui/main.qml
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Martin Klapetek <mklapetek@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
71
phonebook/contents/ui/private/SectionScroller.js
Normal file
71
phonebook/contents/ui/private/SectionScroller.js
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
20
phonebook/metadata.desktop
Normal file
20
phonebook/metadata.desktop
Normal file
|
|
@ -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=
|
||||||
253
prototypes/gridmultitasking/main.qml
Normal file
253
prototypes/gridmultitasking/main.qml
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
|
||||||
|
import QtQuick 2.1
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
width: 300
|
||||||
|
height: 500
|
||||||
|
property Item currentApp
|
||||||
|
state: "switcher"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
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
|
||||||
|
|
||||||
|
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 + root.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.state = "scrolling"
|
||||||
|
root.currentApp = appRect
|
||||||
|
root.state = "app"
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "App " + modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: bottomBar
|
||||||
|
z: 99
|
||||||
|
color: "blue"
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
height: 50
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
property int oldY
|
||||||
|
property real startY
|
||||||
|
onClicked: {
|
||||||
|
root.state = "switcher"
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
if (root.state == "app") {
|
||||||
|
root.state = "zooming";
|
||||||
|
} else {
|
||||||
|
root.state = "dragging";
|
||||||
|
}
|
||||||
|
oldY = mouse.y;
|
||||||
|
startY = mouse.y;
|
||||||
|
}
|
||||||
|
onPositionChanged: {
|
||||||
|
if (root.state == "app" || root.state == "zooming") {
|
||||||
|
mainFlickable.scale = (1 - (startY - mouse.y) / root.height);
|
||||||
|
} else {
|
||||||
|
mainFlickable.contentY += oldY - mouse.y;
|
||||||
|
}
|
||||||
|
oldY = mouse.y;
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
if (root.state == "app" || root.state == "zooming") {
|
||||||
|
if (mainFlickable.scale < 0.7) {
|
||||||
|
root.state = "switcher"
|
||||||
|
} else {
|
||||||
|
root.state = "app"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mainFlickable.contentY < root.height) {
|
||||||
|
root.state = "homescreen"
|
||||||
|
} else {
|
||||||
|
root.state = "switcher"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: [
|
||||||
|
State {
|
||||||
|
name: "switcher"
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainFlickable
|
||||||
|
scale: 0.5
|
||||||
|
x: -root.width / 2
|
||||||
|
y: -root.height / 2
|
||||||
|
interactive: true
|
||||||
|
contentY: root.height*2 + (root.currentApp ? root.currentApp.y : 0)
|
||||||
|
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: "zooming"
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainFlickable
|
||||||
|
scale: scale
|
||||||
|
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)) * (1 - mainFlickable.zoomFactor) + (root.height*2 + root.currentApp.y) * mainFlickable.zoomFactor
|
||||||
|
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"
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
root.currentApp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Transition {
|
||||||
|
to: "zooming"
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
root.currentApp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -20,4 +20,4 @@ install(TARGETS satellitecomponentsplugin DESTINATION ${QML_INSTALL_DIR}/org/kde
|
||||||
|
|
||||||
install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/satellite/components)
|
install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/satellite/components)
|
||||||
|
|
||||||
|
add_subdirectory(modeltest)
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,16 @@
|
||||||
#include <KService>
|
#include <KService>
|
||||||
#include <KServiceGroup>
|
#include <KServiceGroup>
|
||||||
#include <KServiceTypeTrader>
|
#include <KServiceTypeTrader>
|
||||||
|
#include <KSycoca>
|
||||||
#include <KSycocaEntry>
|
#include <KSycocaEntry>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
ApplicationListModel::ApplicationListModel(QObject *parent)
|
ApplicationListModel::ApplicationListModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
loadApplications();
|
//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()
|
ApplicationListModel::~ApplicationListModel()
|
||||||
|
|
@ -55,15 +58,35 @@ QHash<int, QByteArray> ApplicationListModel::roleNames() const
|
||||||
return roleNames;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
void ApplicationListModel::loadApplications()
|
void ApplicationListModel::loadApplications()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|
||||||
|
m_applicationList.clear();
|
||||||
|
|
||||||
KServiceGroup::Ptr group = KServiceGroup::root();
|
KServiceGroup::Ptr group = KServiceGroup::root();
|
||||||
if (!group || !group->isValid()) return;
|
if (!group || !group->isValid()) return;
|
||||||
KServiceGroup::List subGroupList = group->entries(true);
|
KServiceGroup::List subGroupList = group->entries(true);
|
||||||
|
|
||||||
|
QMap<int, ApplicationData> orderedList;
|
||||||
|
QList<ApplicationData> unorderedList;
|
||||||
|
|
||||||
// Iterate over all entries in the group
|
// Iterate over all entries in the group
|
||||||
for(KServiceGroup::List::ConstIterator it = subGroupList.begin();it != subGroupList.end(); it++) {
|
for(KServiceGroup::List::ConstIterator it = subGroupList.begin();it != subGroupList.end(); it++) {
|
||||||
KSycocaEntry::Ptr groupEntry = (*it);
|
KSycocaEntry::Ptr groupEntry = (*it);
|
||||||
|
|
@ -85,13 +108,22 @@ void ApplicationListModel::loadApplications()
|
||||||
data.icon = service->icon();
|
data.icon = service->icon();
|
||||||
data.storageId = service->storageId();
|
data.storageId = service->storageId();
|
||||||
data.entryPath = service->exec();
|
data.entryPath = service->exec();
|
||||||
m_applicationList << data;
|
|
||||||
|
if (m_appPositions.contains(service->storageId())) {
|
||||||
|
orderedList[m_appPositions.value(service->storageId())] = data;
|
||||||
|
} else {
|
||||||
|
unorderedList << data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(unorderedList.begin(), unorderedList.end(), appNameLessThan);
|
||||||
|
m_applicationList << orderedList.values();
|
||||||
|
m_applicationList << unorderedList;
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
emit countChanged();
|
emit countChanged();
|
||||||
|
|
@ -121,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
|
int ApplicationListModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
if (parent.isValid()) {
|
if (parent.isValid()) {
|
||||||
|
|
@ -130,6 +169,11 @@ int ApplicationListModel::rowCount(const QModelIndex &parent) const
|
||||||
return m_applicationList.count();
|
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)
|
Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination)
|
||||||
{
|
{
|
||||||
if (row < 0 || destination < 0 || row >= m_applicationList.length() ||
|
if (row < 0 || destination < 0 || row >= m_applicationList.length() ||
|
||||||
|
|
@ -141,8 +185,27 @@ Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
beginMoveRows(QModelIndex(), row, row, QModelIndex(), destination);
|
beginMoveRows(QModelIndex(), row, row, QModelIndex(), destination);
|
||||||
|
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);
|
ApplicationData data = m_applicationList.takeAt(row);
|
||||||
m_applicationList.insert(destination, data);
|
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();
|
endMoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,7 +217,29 @@ void ApplicationListModel::runApplication(const QString &storageId)
|
||||||
|
|
||||||
KService::Ptr service = KService::serviceByStorageId(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
|
||||||
|
{
|
||||||
|
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"
|
#include "applicationlistmodel.moc"
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ class ApplicationListModel : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
|
Q_PROPERTY(QStringList appOrder READ appOrder WRITE setAppOrder NOTIFY appOrderChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ApplicationListModel(QObject *parent = 0);
|
ApplicationListModel(QObject *parent = 0);
|
||||||
|
|
@ -45,10 +46,14 @@ public:
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
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(); }
|
int count() { return m_applicationList.count(); }
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
|
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
enum Roles {
|
enum Roles {
|
||||||
|
|
@ -59,16 +64,27 @@ public:
|
||||||
ApplicationOriginalRowRole = Qt::UserRole + 6
|
ApplicationOriginalRowRole = Qt::UserRole + 6
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QStringList appOrder() const;
|
||||||
|
void setAppOrder(const QStringList &order);
|
||||||
|
|
||||||
Q_INVOKABLE void moveItem(int row, int order);
|
Q_INVOKABLE void moveItem(int row, int order);
|
||||||
|
|
||||||
Q_INVOKABLE void runApplication(const QString &storageId);
|
Q_INVOKABLE void runApplication(const QString &storageId);
|
||||||
|
|
||||||
|
Q_INVOKABLE void loadApplications();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void sycocaDbChanged(const QStringList &change);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void countChanged();
|
void countChanged();
|
||||||
|
void appOrderChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<ApplicationData> m_applicationList;
|
QList<ApplicationData> m_applicationList;
|
||||||
void loadApplications();
|
|
||||||
|
QStringList m_appOrder;
|
||||||
|
QHash<QString, int> m_appPositions;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // APPLICATIONLISTMODEL_H
|
#endif // APPLICATIONLISTMODEL_H
|
||||||
|
|
|
||||||
15
qmlcomponents/modeltest/CMakeLists.txt
Normal file
15
qmlcomponents/modeltest/CMakeLists.txt
Normal file
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
||||||
54
qmlcomponents/modeltest/main.cpp
Normal file
54
qmlcomponents/modeltest/main.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
592
qmlcomponents/modeltest/modeltest.cpp
Normal file
592
qmlcomponents/modeltest/modeltest.cpp
Normal file
|
|
@ -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 <QtCore/QtCore>
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
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<int, QVariant> 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<QString>() );
|
||||||
|
}
|
||||||
|
variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
|
||||||
|
if ( variant.isValid() ) {
|
||||||
|
QVERIFY( variant.canConvert<QString>() );
|
||||||
|
}
|
||||||
|
variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
|
||||||
|
if ( variant.isValid() ) {
|
||||||
|
QVERIFY( variant.canConvert<QString>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// General Purpose roles that should return a QSize
|
||||||
|
variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
|
||||||
|
if ( variant.isValid() ) {
|
||||||
|
QVERIFY( variant.canConvert<QSize>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// General Purpose roles that should return a QFont
|
||||||
|
QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
|
||||||
|
if ( fontVariant.isValid() ) {
|
||||||
|
QVERIFY( fontVariant.canConvert<QFont>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<QColor>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
|
||||||
|
if ( colorVariant.isValid() ) {
|
||||||
|
QVERIFY( colorVariant.canConvert<QColor>() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
88
qmlcomponents/modeltest/modeltest.h
Normal file
88
qmlcomponents/modeltest/modeltest.h
Normal file
|
|
@ -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 <QtCore/QObject>
|
||||||
|
#include <QtCore/QAbstractItemModel>
|
||||||
|
#include <QtCore/QStack>
|
||||||
|
|
||||||
|
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<Changing> insert;
|
||||||
|
QStack<Changing> remove;
|
||||||
|
|
||||||
|
bool fetchingMore;
|
||||||
|
|
||||||
|
QList<QPersistentModelIndex> changing;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
51
shell/contents/applet/AppletError.qml
Normal file
51
shell/contents/applet/AppletError.qml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
117
shell/contents/applet/CompactApplet.qml
Normal file
117
shell/contents/applet/CompactApplet.qml
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
|
||||||
|
*/
|
||||||
|
import QtQuick 2.4
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import 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) {
|
||||||
|
appletParent.width = fullRepresentation.width;
|
||||||
|
} else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredWidth > 0) {
|
||||||
|
appletParent.width = fullRepresentation.Layout.preferredWidth
|
||||||
|
} else if (fullRepresentation.implicitWidth > 0) {
|
||||||
|
appletParent.width = fullRepresentation.implicitWidth
|
||||||
|
} else {
|
||||||
|
appletParent.width = theme.mSize(theme.defaultFont).width * 35
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullRepresentation.height > 0) {
|
||||||
|
appletParent.height = fullRepresentation.height;
|
||||||
|
} else if (fullRepresentation.Layout && fullRepresentation.Layout.preferredHeight > 0) {
|
||||||
|
appletParent.height = fullRepresentation.Layout.preferredHeight
|
||||||
|
} else if (fullRepresentation.implicitHeight > 0) {
|
||||||
|
appletParent.height = fullRepresentation.implicitHeight
|
||||||
|
} else {
|
||||||
|
appletParent.height = theme.mSize(theme.defaultFont).height * 25
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fullRepresentation.parent = appletParent;
|
||||||
|
fullRepresentation.anchors.fill = fullRepresentation.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: expandedItem
|
||||||
|
anchors.fill: parent
|
||||||
|
color: PlasmaCore.ColorScope.highlightColor
|
||||||
|
opacity: plasmoid.expanded ? 0.3 : 0
|
||||||
|
Behavior on opacity {
|
||||||
|
OpacityAnimator {
|
||||||
|
duration: units.shortDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: expandedSync
|
||||||
|
interval: 100
|
||||||
|
onTriggered: plasmoid.expanded = popupWindow.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
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
|
||||||
|
width: Math.max(parent.width, Layout.minimumWidth)
|
||||||
|
height: Layout.minimumHeight
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
OpacityAnimator {
|
||||||
|
duration: units.shortDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
83
shell/contents/applet/DefaultCompactRepresentation.qml
Normal file
83
shell/contents/applet/DefaultCompactRepresentation.qml
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: main
|
||||||
|
spacing: units.largeSpacing
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
property bool wasExpanded: false
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onPressed: wasExpanded = plasmoid.expanded
|
||||||
|
onClicked: plasmoid.expanded = !wasExpanded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
LookAndFeelPackage=org.kde.satellite.phone
|
LookAndFeelPackage=org.kde.satellite.phone
|
||||||
|
|
||||||
[Desktop]
|
[Desktop]
|
||||||
Containment=org.kde.desktopcontainment
|
Containment=org.kde.phone.homescreen
|
||||||
ToolBox=
|
ToolBox=
|
||||||
|
|
||||||
[Desktop][ContainmentActions]
|
[Desktop][ContainmentActions]
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,7 @@ for (var j = 0; j < desktopsArray.length; j++) {
|
||||||
desktopsArray[j].writeConfig("Image", "org.kde.satellite.lockers");
|
desktopsArray[j].writeConfig("Image", "org.kde.satellite.lockers");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desktopsForActivity(id)[0].addWidget("org.kde.phone.notifications");
|
||||||
|
|
||||||
|
var panel = new Panel("org.kde.phone.panel");
|
||||||
|
panel.addWidget("org.kde.phone.quicksettings");
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ Item {
|
||||||
property int notificationId: 0;
|
property int notificationId: 0;
|
||||||
property int buttonHeight: width/4
|
property int buttonHeight: width/4
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
/*
|
/*
|
||||||
Notification data object has the following properties:
|
Notification data object has the following properties:
|
||||||
appIcon
|
appIcon
|
||||||
|
|
@ -63,29 +64,19 @@ Item {
|
||||||
tmp.summary == data.summary &&
|
tmp.summary == data.summary &&
|
||||||
tmp.body == data.body);
|
tmp.body == data.body);
|
||||||
var sameSource = tmp.source == source;
|
var sameSource = tmp.source == source;
|
||||||
|
=======
|
||||||
|
onContainmentChanged: {
|
||||||
|
containment.parent = homescreen;
|
||||||
|
>>>>>>> mart/newContainmentShell
|
||||||
|
|
||||||
if (sameSource && matches) {
|
if (containment != null) {
|
||||||
return;
|
containment.visible = true;
|
||||||
}
|
}
|
||||||
|
if (containment != null) {
|
||||||
if (sameSource || matches) {
|
containment.anchors.left = homescreen.left;
|
||||||
notificationsModel.remove(i)
|
containment.anchors.top = homescreen.top;
|
||||||
break;
|
containment.anchors.right = homescreen.right;
|
||||||
}
|
containment.anchors.bottom = homescreen.bottom;
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,39 +122,6 @@ Item {
|
||||||
modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : ""
|
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 {
|
OfonoNetworkOperator {
|
||||||
id: netop
|
id: netop
|
||||||
}
|
}
|
||||||
|
|
@ -190,88 +148,11 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
//pass the focus to the containment, so it can react to homescreen activate/inactivate
|
||||||
id: pendingTimer
|
Connections {
|
||||||
interval: 5000
|
target: desktop
|
||||||
repeat: false
|
onActiveChanged: {
|
||||||
onTriggered: {
|
containment.focus = desktop.active;
|
||||||
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?"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,255 +185,6 @@ Item {
|
||||||
source: simManager.pinRequired != OfonoSimManager.NoPin ? Qt.resolvedUrl("Pin.qml") : ""
|
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: {
|
Component.onCompleted: {
|
||||||
//configure the view behavior
|
//configure the view behavior
|
||||||
if (desktop) {
|
if (desktop) {
|
||||||
|
|
|
||||||
41
shell/contents/views/Panel.qml
Normal file
41
shell/contents/views/Panel.qml
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Marco Martin <mart@kde.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue