mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03:09 +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(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 REQUIRED COMPONENTS PlasmaQuick I18n)
|
||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n)
|
||||
find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons Notifications)
|
||||
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
|
@ -44,9 +44,13 @@ install(DIRECTORY compositor/
|
|||
PATTERN Messages.sh EXCLUDE
|
||||
PATTERN dummydata EXCLUDE)
|
||||
|
||||
kpackage_install_package(phonebook org.kde.phone.phonebook genericqml)
|
||||
install(FILES phonebook/metadata.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME org.kde.phone.dialer.desktop)
|
||||
|
||||
add_subdirectory(bin)
|
||||
add_subdirectory(qmlcomponents)
|
||||
add_subdirectory(services)
|
||||
add_subdirectory(settingsmodules)
|
||||
add_subdirectory(applets)
|
||||
add_subdirectory(containments)
|
||||
add_subdirectory(dialer)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
|
||||
plasma_install_package(clock org.kde.phone.clock)
|
||||
plasma_install_package(notifications org.kde.phone.notifications)
|
||||
|
||||
add_subdirectory(quicksettings)
|
||||
|
|
|
|||
|
|
@ -18,25 +18,24 @@
|
|||
*/
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
anchors.bottomMargin: 10
|
||||
drag.axis: Drag.XAxis
|
||||
drag.target: root
|
||||
drag.target: notificationItem
|
||||
|
||||
property bool expanded: false
|
||||
property var textGradient: Gradient {
|
||||
GradientStop { position: 1.0; color: "#FF00000C" }
|
||||
GradientStop { position: 0.0; color: "#00000C00" }
|
||||
}
|
||||
property color textGradientOverlay: "#9900000C"
|
||||
property string source: model.source
|
||||
property var actions: model.actions
|
||||
|
||||
Behavior on x {
|
||||
SpringAnimation { spring: 2; damping: 0.2 }
|
||||
|
|
@ -46,112 +45,105 @@ MouseArea {
|
|||
SpringAnimation { spring: 5; damping: 0.3 }
|
||||
}
|
||||
|
||||
onExpandedChanged: {
|
||||
if (expanded && body) {
|
||||
height = units.gridUnit * 4;
|
||||
} else {
|
||||
height = units.gridUnit * 2;
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (drag.active) {
|
||||
if (x > width / 4 || x < width / -4) {
|
||||
//if there is an action, execute the first when swiping left
|
||||
if (x < 0 && actions) {
|
||||
var action = actions.get(0)
|
||||
root.executeAction(source, action.id)
|
||||
}
|
||||
notificationsModel.remove(index);
|
||||
} else {
|
||||
x = 0;
|
||||
}
|
||||
} else if (body) {
|
||||
} else if (body || actions) {
|
||||
expanded = !expanded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: background
|
||||
imagePath: "widgets/background"
|
||||
anchors {
|
||||
fill: parent
|
||||
rightMargin: -notificationItem.width
|
||||
leftMargin: units.gridUnit
|
||||
}
|
||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: units.gridUnit / 2
|
||||
}
|
||||
iconSource: "window-close"
|
||||
flat: false
|
||||
onClicked: {
|
||||
notificationsModel.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: units.gridUnit * 3
|
||||
}
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
text: model.appName
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: summaryText
|
||||
anchors {
|
||||
right: icon.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
rightMargin: units.smallSpacing
|
||||
}
|
||||
horizontalAlignment: Qt.AlignRight
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
text: summary + (notificationItem.expanded ? (body ? "\n" + body : '') :
|
||||
(body ? '...' : ''))
|
||||
}
|
||||
|
||||
PlasmaCore.IconItem {
|
||||
id: icon
|
||||
anchors {
|
||||
right: notificationItem.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
width: units.iconSizes.medium
|
||||
height: width
|
||||
visible: !root.expanded
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: units.largeSpacing
|
||||
y: 0
|
||||
source: appIcon && appIcon.length > 0 ? appIcon : "im-user"
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rounded
|
||||
clip: true
|
||||
height: parent.height
|
||||
width: height / 2
|
||||
visible: !root.expanded
|
||||
RowLayout {
|
||||
id: actionsLayout
|
||||
anchors {
|
||||
left: icon.right
|
||||
leftMargin: units.largeSpacing
|
||||
right: summaryText.right
|
||||
top: summaryText.bottom
|
||||
topMargin: units.smallSpacing
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: roundedRect
|
||||
height: parent.height
|
||||
width: parent.width * 2
|
||||
radius: height //Math.max(height, units.gridUnit)
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
opacity: notificationItem.expanded && notificationItem.actions && notificationItem.actions.count > 0 ? 1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: units.shortDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
gradient: root.textGradient
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: height / 2
|
||||
color: textGradientOverlay
|
||||
}
|
||||
Repeater {
|
||||
model: notificationItem.actions
|
||||
delegate: PlasmaComponents.Button {
|
||||
text: model.text
|
||||
onClicked: {
|
||||
root.executeAction(notificationItem.source, model.id)
|
||||
notificationsModel.remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: summaryArea
|
||||
width: parent.width - icon.width - rounded.width - (units.largeSpacing * 2)
|
||||
height: parent.height
|
||||
anchors {
|
||||
left: root.expanded ? root.left : rounded.right
|
||||
right: root.right
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
gradient: root.textGradient
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: textGradientOverlay
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: summaryText
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
horizontalAlignment: root.expanded ? Qt.AlignHCenter : Qt.AlignLeft
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
text: summary + (root.expanded ? (body ? "\n" + body : '') :
|
||||
(body ? '...' : ''))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: extraArea
|
||||
width: parent.width
|
||||
height: parent.width
|
||||
anchors {
|
||||
left: summaryArea.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
gradient: root.textGradient
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: textGradientOverlay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,59 @@ import org.kde.plasma.components 2.0 as PlasmaComponents
|
|||
|
||||
Item {
|
||||
id: root
|
||||
property int notificationId: 0
|
||||
|
||||
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 {
|
||||
id: notificationsSource
|
||||
|
||||
|
|
@ -58,7 +108,7 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
homescreen.addNotification(
|
||||
root.addNotification(
|
||||
sourceName,
|
||||
data,
|
||||
actions);
|
||||
|
|
@ -70,17 +120,26 @@ Item {
|
|||
id: notificationsModel
|
||||
|
||||
ListElement {
|
||||
source: "call1Source"
|
||||
appIcon: "call-start"
|
||||
summary: "Missed call from Joe"
|
||||
appName: "Phone"
|
||||
body: "Called at 8:42 from +41 56 373 37 31"
|
||||
actions: []
|
||||
}
|
||||
ListElement {
|
||||
source: "im1Source"
|
||||
appIcon: "im-google"
|
||||
appName: "Message"
|
||||
summary: "July: Hey! Are you around?"
|
||||
actions: []
|
||||
}
|
||||
ListElement {
|
||||
source: "im2Source"
|
||||
appIcon: "im-google"
|
||||
appName: "Message"
|
||||
summary: "July: Hello?"
|
||||
actions: []
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
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(panel org.kde.phone.panel)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
<entry name="AppletOrder" type="String">
|
||||
<label>encoded order of items</label>
|
||||
</entry>
|
||||
<entry name="AppOrder" type="StringList">
|
||||
<label>order of apps</label>
|
||||
<default>org.kde.phone.dialer.desktop</default>
|
||||
</entry>
|
||||
</group>
|
||||
|
||||
</kcfg>
|
||||
|
|
|
|||
|
|
@ -8,150 +8,43 @@ Item {
|
|||
width: applicationsView.cellWidth
|
||||
height: width
|
||||
|
||||
property int idx: index
|
||||
property int oldIdx: -1
|
||||
property var modelData: model
|
||||
property bool isDropTarget: delegateRoot != dragDelegate && root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole == modelData.ApplicationStorageIdRole
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
radius: units.gridUnit
|
||||
opacity: (delegateItem.drag.target != null) ? 0.4 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
opacity: isDropTarget ? 0.3 : 1
|
||||
|
||||
PlasmaCore.IconItem {
|
||||
id: icon
|
||||
anchors.centerIn: parent
|
||||
width: parent.height / 2
|
||||
height: width
|
||||
source: modelData.ApplicationIconRole
|
||||
scale: root.reorderingApps && applicationsView.dragData && applicationsView.dragData.ApplicationStorageIdRole != modelData.ApplicationStorageIdRole ? 0.6 : 1
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
//animate index change
|
||||
onIdxChanged: {
|
||||
if (delegateItem.drag.target != null) {
|
||||
return;
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: label
|
||||
visible: text.length > 0
|
||||
|
||||
anchors {
|
||||
top: icon.bottom
|
||||
left: icon.left
|
||||
right: icon.right
|
||||
}
|
||||
|
||||
if (oldIdx < 0) {
|
||||
oldIdx = idx;
|
||||
return;
|
||||
}
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
maximumLineCount: 2
|
||||
|
||||
delegateItem.x = ((oldIdx % 4) * GridView.view.cellWidth) - ((idx % 4) * GridView.view.cellWidth);
|
||||
delegateItem.y = (Math.floor(oldIdx / 4) * GridView.view.cellHeight) - (Math.floor(idx / 4) * GridView.view.cellHeight);
|
||||
|
||||
translAnim.running = true;
|
||||
|
||||
oldIdx = idx;
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: translAnim
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
target: delegateItem
|
||||
properties: "x,y"
|
||||
to: 0
|
||||
}
|
||||
MouseArea {
|
||||
id: delegateItem
|
||||
|
||||
width: applicationsView.cellWidth
|
||||
height: width
|
||||
|
||||
states: [
|
||||
State {
|
||||
when: delegateItem.drag.target != null
|
||||
ParentChange {
|
||||
target: delegateItem
|
||||
parent: delegateRoot.parent
|
||||
}
|
||||
PropertyChanges {
|
||||
target: delegateItem
|
||||
z: 9999
|
||||
}
|
||||
}
|
||||
]
|
||||
function updateRow() {
|
||||
var pos = mapToItem(delegateRoot.parent, 0, 0);
|
||||
|
||||
var newRow = (Math.round(delegateRoot.GridView.view.width / delegateRoot.GridView.view.cellWidth) * Math.round(pos.y / delegateRoot.GridView.view.cellHeight) + Math.round(pos.x / delegateRoot.GridView.view.cellWidth));
|
||||
|
||||
if (model.ApplicationOriginalRowRole != newRow) {
|
||||
appListModel.moveItem(model.ApplicationOriginalRowRole, newRow);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
console.log("Clicked: " + model.ApplicationStorageIdRole)
|
||||
appListModel.runApplication(model.ApplicationStorageIdRole)
|
||||
oldX = x
|
||||
oldY = y
|
||||
}
|
||||
onPressAndHold: {
|
||||
delegateRoot.GridView.view.draggingItem = delegateItem;
|
||||
delegateItem.drag.target = delegateItem;
|
||||
root.reorderingApps = true;
|
||||
}
|
||||
onReleased: {
|
||||
delegateRoot.GridView.view.draggingItem = delegateItem;
|
||||
delegateItem.drag.target = null;
|
||||
root.reorderingApps = false;
|
||||
|
||||
translAnim.running = true;
|
||||
autoScrollTimer.running = false;
|
||||
}
|
||||
onPositionChanged: {
|
||||
if (!autoScrollTimer.running && delegateItem.drag.target) {
|
||||
updateRow();
|
||||
|
||||
var screenPos = mapToItem(delegateRoot.GridView.view, 0, 0);
|
||||
|
||||
if (applicationsView.contentY > 0 && screenPos.y < root.height / 4) {
|
||||
autoScrollTimer.scrollDown = false;
|
||||
autoScrollTimer.running = true;
|
||||
} else if (!applicationsView.atYEnd && screenPos.y > 3 * (root.height / 4)) {
|
||||
autoScrollTimer.scrollDown = true;
|
||||
autoScrollTimer.running = true;
|
||||
} else {
|
||||
autoScrollTimer.running = false;
|
||||
}
|
||||
} else {
|
||||
autoScrollTimer.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaCore.IconItem {
|
||||
id: icon
|
||||
anchors.centerIn: parent
|
||||
width: parent.height / 2
|
||||
height: width
|
||||
source: model.ApplicationIconRole
|
||||
scale: root.reorderingApps && !delegateItem.drag.target ? 0.6 : 1
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: label
|
||||
visible: text.length > 0
|
||||
|
||||
anchors {
|
||||
top: icon.bottom
|
||||
left: icon.left
|
||||
right: icon.right
|
||||
}
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
maximumLineCount: 2
|
||||
|
||||
text: model.ApplicationNameRole
|
||||
font.pixelSize: theme.smallestFont.pixelSize
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
}
|
||||
text: modelData.ApplicationNameRole
|
||||
font.pixelSize: theme.smallestFont.pixelSize
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import QtQuick 2.0
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
|
||||
Item {
|
||||
PlasmaCore.ColorScope {
|
||||
colorGroup: PlasmaCore.Theme.NormalColorGroup
|
||||
|
||||
PlasmaCore.FrameSvgItem {
|
||||
z: -1
|
||||
imagePath: "widgets/background"
|
||||
|
|
@ -11,10 +13,17 @@ Item {
|
|||
topMargin: -margins.top
|
||||
bottomMargin: -margins.bottom
|
||||
}
|
||||
Rectangle {
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: parent.margins.top
|
||||
bottomMargin: parent.margins.bottom
|
||||
}
|
||||
color: PlasmaCore.ColorScope.backgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
opacity: 0.6
|
||||
height: Math.max(100, units.gridUnit * 2.5)
|
||||
height: applicationsView.cellWidth
|
||||
width: parent.width
|
||||
y: parent.height / 2 - height / 2
|
||||
x: 0
|
||||
|
|
|
|||
|
|
@ -16,18 +16,18 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
|
||||
*/
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick 2.4
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||
|
||||
import org.kde.kquickcontrolsaddons 2.0
|
||||
import org.kde.satellite.components 0.1 as SatelliteComponents
|
||||
|
||||
import "plasmapackage:/code/LayoutManager.js" as LayoutManager
|
||||
|
||||
Item {
|
||||
MouseEventListener {
|
||||
id: root
|
||||
width: 480
|
||||
height: 640
|
||||
|
|
@ -42,6 +42,12 @@ Item {
|
|||
LayoutManager.save();
|
||||
}
|
||||
|
||||
Plasmoid.onFocusChanged: {
|
||||
if (!plasmoid.focus && applicationsView.contentY > -(applicationsView.headerItem.height - root.height/2)) {
|
||||
applicationsView.contentY = -root.height;
|
||||
}
|
||||
}
|
||||
|
||||
function addApplet(applet, x, y) {
|
||||
var container = appletContainerComponent.createObject(appletsSpace.layout)
|
||||
container.visible = true
|
||||
|
|
@ -65,7 +71,8 @@ Item {
|
|||
container.animationsEnabled = false;
|
||||
|
||||
if (appletsSpace.lastSpacer.parent === appletsSpace.layout) {
|
||||
before = appletsSpace.lastSpacer;
|
||||
//Uncomment to make the spacer the last element again
|
||||
// before = appletsSpace.lastSpacer;
|
||||
}
|
||||
|
||||
if (before) {
|
||||
|
|
@ -92,24 +99,28 @@ Item {
|
|||
LayoutManager.lastSpacer = appletsSpace.lastSpacer;
|
||||
LayoutManager.restore();
|
||||
applicationsView.contentY = -root.height;
|
||||
|
||||
appListModel.appOrder = plasmoid.configuration.AppOrder;
|
||||
appListModel.loadApplications();
|
||||
}
|
||||
|
||||
SatelliteComponents.ApplicationListModel {
|
||||
id: appListModel
|
||||
onAppOrderChanged: {
|
||||
plasmoid.configuration.AppOrder = appListModel.appOrder;
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autoScrollTimer
|
||||
property bool scrollDown: true
|
||||
repeat: true
|
||||
interval: 10
|
||||
interval: 1500
|
||||
onTriggered: {
|
||||
applicationsView.contentY += scrollDown ? 8 : -8;
|
||||
if (applicationsView.draggingItem) {
|
||||
applicationsView.draggingItem.y += scrollDown ? 8 : -8;
|
||||
|
||||
applicationsView.draggingItem.updateRow();
|
||||
}
|
||||
scrollAnim.to = scrollDown ?
|
||||
Math.min(applicationsView.contentItem.height - applicationsView.headerItem.height - root.height, applicationsView.contentY + root.height/2) :
|
||||
Math.max(0, applicationsView.contentY - root.height/2);
|
||||
scrollAnim.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +139,7 @@ Item {
|
|||
}
|
||||
|
||||
Layout.minimumWidth: root.width
|
||||
Layout.minimumHeight: Math.max(applet.Layout.minimumHeight, root.height / 2)
|
||||
Layout.minimumHeight: Math.max(applet.Layout.minimumHeight, (root.height-applicationsView.headerItem.margin) / 2)
|
||||
|
||||
Layout.preferredWidth: root.width
|
||||
Layout.preferredHeight: Layout.minimumHeight
|
||||
|
|
@ -138,15 +149,197 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Qt.rgba(0, 0, 0, 0.9 * (Math.min(applicationsView.contentY + root.height, root.height) / root.height))
|
||||
anchors.fill: parent
|
||||
}
|
||||
onPressAndHold: {
|
||||
var pos = mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y);
|
||||
//in favorites area?
|
||||
var item;
|
||||
if (applicationsView.headerItem.favoritesStrip.contains(pos)) {
|
||||
item = applicationsView.headerItem.favoritesStrip.itemAt(pos.x, pos.y);
|
||||
} else {
|
||||
pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y);
|
||||
item = applicationsView.itemAt(pos.x, pos.y)
|
||||
}
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
applicationsView.dragData = new Object;
|
||||
applicationsView.dragData.ApplicationNameRole = item.modelData.ApplicationNameRole;
|
||||
applicationsView.dragData.ApplicationIconRole = item.modelData.ApplicationIconRole;
|
||||
applicationsView.dragData.ApplicationStorageIdRole = item.modelData.ApplicationStorageIdRole;
|
||||
applicationsView.dragData.ApplicationEntryPathRole = item.modelData.ApplicationEntryPathRole;
|
||||
applicationsView.dragData.ApplicationOriginalRowRole = item.modelData.ApplicationOriginalRowRole;
|
||||
|
||||
dragDelegate.modelData = applicationsView.dragData;
|
||||
applicationsView.interactive = false;
|
||||
root.reorderingApps = true;
|
||||
dragDelegate.x = Math.floor(mouse.x / root.buttonHeight) * root.buttonHeight
|
||||
dragDelegate.y = Math.floor(mouse.y / root.buttonHeight) * root.buttonHeight
|
||||
dragDelegate.xTarget = mouse.x - dragDelegate.width/2;
|
||||
dragDelegate.yTarget = mouse.y - dragDelegate.width/2;
|
||||
dragDelegate.opacity = 1;
|
||||
}
|
||||
onPositionChanged: {
|
||||
if (!applicationsView.dragData) {
|
||||
return;
|
||||
}
|
||||
dragDelegate.x = mouse.x - dragDelegate.width/2;
|
||||
dragDelegate.y = mouse.y - dragDelegate.height/2;
|
||||
|
||||
var pos = mapToItem(applicationsView.contentItem, mouse.x, mouse.y);
|
||||
|
||||
//in favorites area?
|
||||
if (applicationsView.headerItem.favoritesStrip.contains(mapToItem(applicationsView.headerItem.favoritesStrip, mouse.x, mouse.y))) {
|
||||
pos.y = 1;
|
||||
}
|
||||
|
||||
var newRow = (Math.round(applicationsView.width / applicationsView.cellWidth) * Math.floor(pos.y / applicationsView.cellHeight) + Math.floor(pos.x / applicationsView.cellWidth));
|
||||
|
||||
if (applicationsView.dragData.ApplicationOriginalRowRole != newRow) {
|
||||
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 {
|
||||
anchors.fill: parent
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
|
||||
Rectangle {
|
||||
color: PlasmaCore.ColorScope.backgroundColor
|
||||
opacity: 0.9 * (Math.min(applicationsView.contentY + root.height, root.height) / root.height)
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
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 {
|
||||
id: applicationsView
|
||||
anchors {
|
||||
|
|
@ -156,7 +349,7 @@ Item {
|
|||
right: parent.right
|
||||
}
|
||||
|
||||
property Item draggingItem
|
||||
property var dragData
|
||||
|
||||
cellWidth: root.buttonHeight
|
||||
cellHeight: cellWidth
|
||||
|
|
@ -183,29 +376,50 @@ Item {
|
|||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
move: Transition {
|
||||
NumberAnimation {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
moveDisplaced: Transition {
|
||||
NumberAnimation {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
|
||||
//clip: true
|
||||
delegate: HomeLauncher {}
|
||||
delegate: HomeLauncher {
|
||||
visible: index > 3
|
||||
}
|
||||
header: MouseArea {
|
||||
z: 999
|
||||
property Item layout: appletsLayout
|
||||
property Item lastSpacer: spacer
|
||||
property Item favoritesStrip: favoritesView
|
||||
width: root.width
|
||||
height: mainLayout.Layout.minimumHeight
|
||||
height: mainLayout.Layout.minimumHeight
|
||||
property int margin: stripe.height + units.gridUnit * 2
|
||||
|
||||
onPressAndHold: {
|
||||
plasmoid.action("configure").trigger();
|
||||
print(favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y)))
|
||||
if (!favoritesView.contains(mapToItem(favoritesView, mouse.x, mouse.y))) {
|
||||
plasmoid.action("configure").trigger();
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
bottomMargin: stripe.height + units.gridUnit * 2
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: root.height
|
||||
Layout.maximumHeight: root.height
|
||||
Clock {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
|
|
@ -226,13 +440,20 @@ Item {
|
|||
}
|
||||
ColumnLayout {
|
||||
id: appletsLayout
|
||||
Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height)
|
||||
Item {
|
||||
id: spacer
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: plasmoid.applets.length % 2 == 0 ? 0 : (root.height - margin)/2
|
||||
Layout.maximumHeight: Layout.minimumHeight
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: margin
|
||||
Layout.maximumHeight: Layout.minimumHeight
|
||||
}
|
||||
}
|
||||
SatelliteStripe {
|
||||
id: stripe
|
||||
|
|
@ -240,48 +461,42 @@ Item {
|
|||
property int viewPos: applicationsView.contentItem.height * applicationsView.visibleArea.yPosition
|
||||
|
||||
y: Math.max(viewPos,
|
||||
Math.min(parent.height, viewPos + root.height) - height + Math.max(0, -(parent.height - height + applicationsView.contentY)))
|
||||
Math.min(parent.height, viewPos + root.height - height) + Math.max(0, -(parent.height - height + applicationsView.contentY)))
|
||||
|
||||
PlasmaCore.Svg {
|
||||
id: stripeIcons
|
||||
imagePath: Qt.resolvedUrl("../images/homescreenicons.svg")
|
||||
}
|
||||
|
||||
Row {
|
||||
GridView {
|
||||
id: favoritesView
|
||||
//FIXME: QQuickItem has a contains, but seems to not work
|
||||
function contains(point) {
|
||||
return point.x > 0 && point.x < width && point.y > 0 && point.y < height;
|
||||
}
|
||||
anchors.fill: parent
|
||||
property int columns: 4
|
||||
property alias buttonHeight: stripe.height
|
||||
interactive: false
|
||||
flow: GridView.FlowTopToBottom
|
||||
cellWidth: root.buttonHeight
|
||||
cellHeight: cellWidth
|
||||
|
||||
HomeLauncherSvg {
|
||||
id: phoneIcon
|
||||
svg: stripeIcons
|
||||
elementId: "phone"
|
||||
callback: function() {
|
||||
console.log("Start phone")
|
||||
model: appListModel
|
||||
delegate: HomeLauncher {}
|
||||
|
||||
move: Transition {
|
||||
NumberAnimation {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
|
||||
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") }
|
||||
moveDisplaced: Transition {
|
||||
NumberAnimation {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +525,7 @@ Item {
|
|||
radius: width
|
||||
anchors.right: parent.right
|
||||
y: applicationsView.height * applicationsView.visibleArea.yPosition
|
||||
opacity: scrollbarMouse.pressed || applicationsView.flicking ? 0.8 : 0
|
||||
opacity: scrollbarMouse.pressed || applicationsView.flicking || scrollDownIndicator.opacity > 0 || scrollUpIndicator.opacity > 0 ? 0.8 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: units.longDuration
|
||||
|
|
|
|||
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.Window 2.0
|
||||
import QtQuick.Window 2.2
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
|
||||
Window {
|
||||
|
|
@ -26,8 +26,10 @@ Window {
|
|||
flags: Qt.WindowDoesNotAcceptFocus
|
||||
|
||||
property int offset: 0
|
||||
property int overShoot: units.gridUnit * 2
|
||||
|
||||
color: "transparent"
|
||||
property alias contents: contentArea.data
|
||||
|
||||
function updateState() {
|
||||
var delta = offset - mouseArea.startOffset;
|
||||
|
|
@ -50,15 +52,17 @@ Window {
|
|||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
y: units.iconSizes.small
|
||||
y: 0
|
||||
width: window.width
|
||||
height: window.height - y
|
||||
clip: true
|
||||
state: "closed"
|
||||
drag.filterChildren: true
|
||||
|
||||
property int oldMouseY: 0
|
||||
property int startOffset: units.iconSizes.large;
|
||||
property string startState: "closed"
|
||||
|
||||
onPressed: {
|
||||
startState = state;
|
||||
startOffset = window.offset;
|
||||
|
|
@ -67,26 +71,63 @@ Window {
|
|||
window.offset = startOffset;
|
||||
}
|
||||
onPositionChanged: {
|
||||
window.offset = window.offset + (mouse.y - oldMouseY);
|
||||
var factor = (mouse.y - oldMouseY > 0) ? (1 - Math.max(0, (slidingArea.y + overShoot) / overShoot)) : 1
|
||||
|
||||
window.offset = window.offset + (mouse.y - oldMouseY) * factor;
|
||||
oldMouseY = mouse.y;
|
||||
}
|
||||
onReleased: window.updateState()
|
||||
onReleased: {
|
||||
if (Math.abs(window.offset - mouseArea.startOffset) < units.gridUnit &&
|
||||
slidingArea.y + slidingArea.height < mouse.y) {
|
||||
mouseArea.state = "closed";
|
||||
} else {
|
||||
window.updateState();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.6-Math.abs(slidingArea.y/slidingArea.height))
|
||||
}
|
||||
PlasmaCore.ColorScope {
|
||||
id: slidingArea
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
height: parent.height/1.5
|
||||
y: Math.min(0, -height + window.offset)
|
||||
|
||||
color: Qt.rgba(0, 0, 0, 0.7)
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
Rectangle {
|
||||
width: parent.width / 4
|
||||
height: units.gridUnit/2
|
||||
color: "yellow"
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: units.gridUnit/2
|
||||
anchors.fill: parent
|
||||
|
||||
Item {
|
||||
id: contentArea
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: overShoot
|
||||
}
|
||||
}
|
||||
color: PlasmaCore.ColorScope.backgroundColor
|
||||
|
||||
Rectangle {
|
||||
height: units.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"
|
||||
PropertyChanges {
|
||||
target: window
|
||||
offset: slidingArea.height
|
||||
offset: slidingArea.height - overShoot
|
||||
}
|
||||
},
|
||||
State {
|
||||
|
|
@ -127,7 +168,7 @@ Window {
|
|||
PropertyAnimation {
|
||||
target: window
|
||||
duration: units.longDuration
|
||||
easing: Easing.InOutQuad
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "offset"
|
||||
}
|
||||
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)
|
||||
|
||||
|
||||
add_subdirectory(modeltest)
|
||||
|
|
|
|||
|
|
@ -30,13 +30,16 @@
|
|||
#include <KService>
|
||||
#include <KServiceGroup>
|
||||
#include <KServiceTypeTrader>
|
||||
#include <KSycoca>
|
||||
#include <KSycocaEntry>
|
||||
#include <QDebug>
|
||||
|
||||
ApplicationListModel::ApplicationListModel(QObject *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()
|
||||
|
|
@ -55,15 +58,35 @@ QHash<int, QByteArray> ApplicationListModel::roleNames() const
|
|||
return roleNames;
|
||||
}
|
||||
|
||||
void ApplicationListModel::sycocaDbChanged(const QStringList &changes)
|
||||
{
|
||||
if (!changes.contains("apps") && !changes.contains("xdgdata-apps")) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_applicationList.clear();
|
||||
|
||||
loadApplications();
|
||||
}
|
||||
|
||||
bool appNameLessThan(const ApplicationData &a1, const ApplicationData &a2)
|
||||
{
|
||||
return a1.name.toLower() < a2.name.toLower();
|
||||
}
|
||||
|
||||
void ApplicationListModel::loadApplications()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_applicationList.clear();
|
||||
|
||||
KServiceGroup::Ptr group = KServiceGroup::root();
|
||||
if (!group || !group->isValid()) return;
|
||||
KServiceGroup::List subGroupList = group->entries(true);
|
||||
|
||||
QMap<int, ApplicationData> orderedList;
|
||||
QList<ApplicationData> unorderedList;
|
||||
|
||||
// Iterate over all entries in the group
|
||||
for(KServiceGroup::List::ConstIterator it = subGroupList.begin();it != subGroupList.end(); it++) {
|
||||
KSycocaEntry::Ptr groupEntry = (*it);
|
||||
|
|
@ -85,7 +108,12 @@ void ApplicationListModel::loadApplications()
|
|||
data.icon = service->icon();
|
||||
data.storageId = service->storageId();
|
||||
data.entryPath = service->exec();
|
||||
m_applicationList << data;
|
||||
|
||||
if (m_appPositions.contains(service->storageId())) {
|
||||
orderedList[m_appPositions.value(service->storageId())] = data;
|
||||
} else {
|
||||
unorderedList << data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -93,6 +121,10 @@ void ApplicationListModel::loadApplications()
|
|||
}
|
||||
}
|
||||
|
||||
std::sort(unorderedList.begin(), unorderedList.end(), appNameLessThan);
|
||||
m_applicationList << orderedList.values();
|
||||
m_applicationList << unorderedList;
|
||||
|
||||
endResetModel();
|
||||
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
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
|
|
@ -130,6 +169,11 @@ int ApplicationListModel::rowCount(const QModelIndex &parent) const
|
|||
return m_applicationList.count();
|
||||
}
|
||||
|
||||
void ApplicationListModel::moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild)
|
||||
{
|
||||
moveItem(sourceRow, destinationChild);
|
||||
}
|
||||
|
||||
Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination)
|
||||
{
|
||||
if (row < 0 || destination < 0 || row >= m_applicationList.length() ||
|
||||
|
|
@ -141,8 +185,27 @@ Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination)
|
|||
}
|
||||
|
||||
beginMoveRows(QModelIndex(), row, row, QModelIndex(), destination);
|
||||
ApplicationData data = m_applicationList.takeAt(row);
|
||||
m_applicationList.insert(destination, data);
|
||||
if (destination > row) {
|
||||
ApplicationData data = m_applicationList.at(row);
|
||||
m_applicationList.insert(destination, data);
|
||||
m_applicationList.takeAt(row);
|
||||
} else {
|
||||
ApplicationData data = m_applicationList.takeAt(row);
|
||||
m_applicationList.insert(destination, data);
|
||||
}
|
||||
|
||||
|
||||
m_appOrder.clear();
|
||||
m_appPositions.clear();
|
||||
int i = 0;
|
||||
for (auto app : m_applicationList) {
|
||||
m_appOrder << app.storageId;
|
||||
m_appPositions[app.storageId] = i;
|
||||
++i;
|
||||
}
|
||||
|
||||
|
||||
emit appOrderChanged();
|
||||
endMoveRows();
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +217,29 @@ void ApplicationListModel::runApplication(const QString &storageId)
|
|||
|
||||
KService::Ptr service = KService::serviceByStorageId(storageId);
|
||||
|
||||
QProcess::startDetached(service->exec());
|
||||
//ignore parameters like %u
|
||||
QProcess::startDetached(service->exec().replace(QRegExp("%\\w"), ""));
|
||||
}
|
||||
|
||||
QStringList ApplicationListModel::appOrder() const
|
||||
{
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class ApplicationListModel : public QAbstractListModel {
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
Q_PROPERTY(QStringList appOrder READ appOrder WRITE setAppOrder NOTIFY appOrderChanged)
|
||||
|
||||
public:
|
||||
ApplicationListModel(QObject *parent = 0);
|
||||
|
|
@ -45,10 +46,14 @@ public:
|
|||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
|
||||
void moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild);
|
||||
|
||||
int count() { return m_applicationList.count(); }
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
|
||||
|
||||
enum Roles {
|
||||
|
|
@ -59,16 +64,27 @@ public:
|
|||
ApplicationOriginalRowRole = Qt::UserRole + 6
|
||||
};
|
||||
|
||||
QStringList appOrder() const;
|
||||
void setAppOrder(const QStringList &order);
|
||||
|
||||
Q_INVOKABLE void moveItem(int row, int order);
|
||||
|
||||
Q_INVOKABLE void runApplication(const QString &storageId);
|
||||
|
||||
Q_INVOKABLE void loadApplications();
|
||||
|
||||
public Q_SLOTS:
|
||||
void sycocaDbChanged(const QStringList &change);
|
||||
|
||||
Q_SIGNALS:
|
||||
void countChanged();
|
||||
void appOrderChanged();
|
||||
|
||||
private:
|
||||
QList<ApplicationData> m_applicationList;
|
||||
void loadApplications();
|
||||
|
||||
QStringList m_appOrder;
|
||||
QHash<QString, int> m_appPositions;
|
||||
};
|
||||
|
||||
#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
|
||||
|
||||
[Desktop]
|
||||
Containment=org.kde.desktopcontainment
|
||||
Containment=org.kde.phone.homescreen
|
||||
ToolBox=
|
||||
|
||||
[Desktop][ContainmentActions]
|
||||
|
|
|
|||
|
|
@ -14,3 +14,7 @@ for (var j = 0; j < desktopsArray.length; j++) {
|
|||
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 buttonHeight: width/4
|
||||
|
||||
<<<<<<< HEAD
|
||||
/*
|
||||
Notification data object has the following properties:
|
||||
appIcon
|
||||
|
|
@ -63,29 +64,19 @@ Item {
|
|||
tmp.summary == data.summary &&
|
||||
tmp.body == data.body);
|
||||
var sameSource = tmp.source == source;
|
||||
=======
|
||||
onContainmentChanged: {
|
||||
containment.parent = homescreen;
|
||||
>>>>>>> mart/newContainmentShell
|
||||
|
||||
if (sameSource && matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sameSource || matches) {
|
||||
notificationsModel.remove(i)
|
||||
break;
|
||||
}
|
||||
if (containment != null) {
|
||||
containment.visible = true;
|
||||
}
|
||||
|
||||
data["id"] = ++notificationId;
|
||||
data["source"] = source;
|
||||
if (data["summary"].length < 1) {
|
||||
data["summary"] = data["body"];
|
||||
data["body"] = '';
|
||||
}
|
||||
data["actions"] = actions;
|
||||
|
||||
notificationsModel.insert(0, data);
|
||||
if (!data["isPersistent"]) {
|
||||
pendingRemovals.push(notificationId);
|
||||
pendingTimer.start();
|
||||
if (containment != null) {
|
||||
containment.anchors.left = homescreen.left;
|
||||
containment.anchors.top = homescreen.top;
|
||||
containment.anchors.right = homescreen.right;
|
||||
containment.anchors.bottom = homescreen.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,39 +122,6 @@ Item {
|
|||
modemPath: ofonoManager.modems.length > 0 ? ofonoManager.modems[0] : ""
|
||||
}
|
||||
|
||||
OfonoNetworkRegistration {
|
||||
id: netreg
|
||||
Component.onCompleted: {
|
||||
netreg.scan()
|
||||
updateStrengthIcon()
|
||||
}
|
||||
|
||||
onNetworkOperatorsChanged : {
|
||||
console.log("operators :"+netreg.currentOperator["Name"].toString())
|
||||
}
|
||||
modemPath: ofonoManager.modems.length ? ofonoManager.modems[0] : ""
|
||||
function updateStrengthIcon() {
|
||||
if (netreg.strength >= 100) {
|
||||
strengthIcon.source = "network-mobile-100";
|
||||
} else if (netreg.strength >= 80) {
|
||||
strengthIcon.source = "network-mobile-80";
|
||||
} else if (netreg.strength >= 60) {
|
||||
strengthIcon.source = "network-mobile-60";
|
||||
} else if (netreg.strength >= 40) {
|
||||
strengthIcon.source = "network-mobile-40";
|
||||
} else if (netreg.strength >= 20) {
|
||||
strengthIcon.source = "network-mobile-20";
|
||||
} else {
|
||||
strengthIcon.source = "network-mobile-0";
|
||||
}
|
||||
}
|
||||
|
||||
onStrengthChanged: {
|
||||
console.log("Strength changed to " + netreg.strength)
|
||||
updateStrengthIcon()
|
||||
}
|
||||
}
|
||||
|
||||
OfonoNetworkOperator {
|
||||
id: netop
|
||||
}
|
||||
|
|
@ -190,88 +148,11 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: pendingTimer
|
||||
interval: 5000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
for (var i = 0; i < pendingRemovals.length; ++i) {
|
||||
var id = pendingRemovals[i];
|
||||
for (var j = 0; j < notificationsModel.count; ++j) {
|
||||
if (notificationsModel.get(j).id == id) {
|
||||
notificationsModel.remove(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingRemovals = [];
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
z: 1
|
||||
color: Qt.rgba(0, 0, 0, 0.9 * (Math.min(applications.contentY + homescreen.height, homescreen.height) / homescreen.height))
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
PlasmaCore.DataSource {
|
||||
id: timeSource
|
||||
engine: "time"
|
||||
connectedSources: ["Local"]
|
||||
interval: 60 * 1000
|
||||
}
|
||||
PlasmaCore.DataSource {
|
||||
id: notificationsSource
|
||||
|
||||
engine: "notifications"
|
||||
interval: 0
|
||||
|
||||
onSourceAdded: {
|
||||
connectSource(source);
|
||||
}
|
||||
|
||||
onSourceRemoved: {
|
||||
for (var i = 0; i < notificationsModel.count; ++i) {
|
||||
if (notificationsModel.get(i) == source) {
|
||||
notificationsModel.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onNewData: {
|
||||
var actions = new Array()
|
||||
if (data["actions"] && data["actions"].length % 2 == 0) {
|
||||
for (var i = 0; i < data["actions"].length; i += 2) {
|
||||
var action = new Object();
|
||||
action["id"] = data["actions"][i];
|
||||
action["text"] = data["actions"][i+1];
|
||||
actions.push(action);
|
||||
}
|
||||
}
|
||||
|
||||
homescreen.addNotification(
|
||||
sourceName,
|
||||
data,
|
||||
actions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: notificationsModel
|
||||
|
||||
ListElement {
|
||||
appIcon: "call-start"
|
||||
summary: "Missed call from Joe"
|
||||
body: "Called at 8:42 from +41 56 373 37 31"
|
||||
}
|
||||
ListElement {
|
||||
appIcon: "im-google"
|
||||
summary: "July: Hey! Are you around?"
|
||||
}
|
||||
ListElement {
|
||||
appIcon: "im-google"
|
||||
summary: "July: Hello?"
|
||||
//pass the focus to the containment, so it can react to homescreen activate/inactivate
|
||||
Connections {
|
||||
target: desktop
|
||||
onActiveChanged: {
|
||||
containment.focus = desktop.active;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -304,255 +185,6 @@ Item {
|
|||
source: simManager.pinRequired != OfonoSimManager.NoPin ? Qt.resolvedUrl("Pin.qml") : ""
|
||||
}
|
||||
|
||||
PlasmaCore.ColorScope {
|
||||
id: statusPanel
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
height: units.iconSizes.small
|
||||
z: 2
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.9)
|
||||
|
||||
PlasmaCore.IconItem {
|
||||
id: strengthIcon
|
||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
width: units.iconSizes.small
|
||||
height: width
|
||||
}
|
||||
PlasmaComponents.Label {
|
||||
anchors {
|
||||
left: strengthIcon.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text: netreg.strength + "% " + netreg.name
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
font.pixelSize: parent.height / 2
|
||||
}
|
||||
PlasmaComponents.Label {
|
||||
id: clock
|
||||
anchors.fill: parent
|
||||
text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm")
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
font.pixelSize: height / 2
|
||||
}
|
||||
MouseArea {
|
||||
property int oldMouseY: 0
|
||||
|
||||
anchors.fill: parent
|
||||
enabled: !dialerOverlay.item.visible
|
||||
onPressed: {
|
||||
oldMouseY = mouse.y;
|
||||
slidingPanel.visible = true;
|
||||
}
|
||||
onPositionChanged: {
|
||||
slidingPanel.offset = slidingPanel.offset + (mouse.y - oldMouseY);
|
||||
oldMouseY = mouse.y;
|
||||
}
|
||||
onReleased: slidingPanel.updateState();
|
||||
}
|
||||
|
||||
|
||||
PlasmaWorkspace.BatteryIcon {
|
||||
id: batteryIcon
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
width: units.iconSizes.small
|
||||
height: width
|
||||
hasBattery: pmSource.data["Battery"]["Has Battery"]
|
||||
batteryType: "Phone"
|
||||
percent: pmSource.data["Battery0"] ? pmSource.data["Battery0"]["Percent"] : 0
|
||||
|
||||
PlasmaCore.DataSource {
|
||||
id: pmSource
|
||||
engine: "powermanagement"
|
||||
connectedSources: sources
|
||||
onSourceAdded: {
|
||||
disconnectSource(source);
|
||||
connectSource(source);
|
||||
}
|
||||
onSourceRemoved: {
|
||||
disconnectSource(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SlidingPanel {
|
||||
id: slidingPanel
|
||||
width: homescreen.width
|
||||
height: homescreen.height
|
||||
}
|
||||
|
||||
|
||||
PlasmaCore.ColorScope {
|
||||
z: 1
|
||||
anchors {
|
||||
fill: parent
|
||||
}
|
||||
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
|
||||
SatelliteComponents.ApplicationListModel {
|
||||
id: appListModel
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: applications
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
z: 1
|
||||
cellWidth: homescreen.buttonHeight
|
||||
cellHeight: cellWidth
|
||||
model: appListModel
|
||||
snapMode: GridView.SnapToRow
|
||||
clip: true
|
||||
header: MouseArea {
|
||||
z: 999
|
||||
width: homescreen.width
|
||||
height: homescreen.height - units.iconSizes.medium
|
||||
|
||||
onPressAndHold: {
|
||||
containment.action("configure").trigger();
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: bigClock
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
bottom: notificationView.top
|
||||
}
|
||||
text: Qt.formatTime(timeSource.data.Local.DateTime, "hh:mm")
|
||||
color: PlasmaCore.ColorScope.textColor
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
font.pointSize: 40
|
||||
style: Text.Raised
|
||||
styleColor: "black"
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: notificationView
|
||||
spacing: units.smallSpacing
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottomMargin: stripe.height * 2
|
||||
}
|
||||
height: parent.height / 3
|
||||
interactive: false
|
||||
|
||||
z: 1
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
model: notificationsModel
|
||||
|
||||
add: Transition {
|
||||
NumberAnimation {
|
||||
properties: "x"
|
||||
from: notificationView.width
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
NumberAnimation {
|
||||
properties: "x"
|
||||
to: notificationView.width
|
||||
duration: 500
|
||||
}
|
||||
NumberAnimation {
|
||||
properties: "opacity"
|
||||
to: 0
|
||||
duration: 500
|
||||
}
|
||||
}
|
||||
|
||||
removeDisplaced: Transition {
|
||||
SequentialAnimation {
|
||||
PauseAnimation { duration: 600 }
|
||||
NumberAnimation { properties: "x,y"; duration: 100 }
|
||||
}
|
||||
}
|
||||
|
||||
delegate: NotificationStripe {}
|
||||
|
||||
}
|
||||
SatelliteStripe {
|
||||
id: stripe
|
||||
z: 99
|
||||
y: Math.max(applications.contentY + parent.height, parent.height - height)
|
||||
|
||||
PlasmaCore.Svg {
|
||||
id: stripeIcons
|
||||
imagePath: Qt.resolvedUrl("../images/homescreenicons.svg")
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
property int columns: 4
|
||||
property alias buttonHeight: stripe.height
|
||||
|
||||
HomeLauncherSvg {
|
||||
id: phoneIcon
|
||||
svg: stripeIcons
|
||||
elementId: "phone"
|
||||
callback: function() {
|
||||
dialerOverlay.open()
|
||||
}
|
||||
}
|
||||
|
||||
HomeLauncherSvg {
|
||||
id: messagingIcon
|
||||
svg: stripeIcons
|
||||
elementId: "messaging"
|
||||
callback: function() { console.log("Start messaging") }
|
||||
}
|
||||
|
||||
|
||||
HomeLauncherSvg {
|
||||
id: emailIcon
|
||||
svg: stripeIcons
|
||||
elementId: "email"
|
||||
callback: function() { console.log("Start email") }
|
||||
}
|
||||
|
||||
|
||||
HomeLauncherSvg {
|
||||
id: webIcon
|
||||
svg: stripeIcons
|
||||
elementId: "web"
|
||||
callback: function() { console.log("Start web") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: HomeLauncher {}
|
||||
Component.onCompleted : { console.log("WTF " + width) }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
//configure the view behavior
|
||||
if (desktop) {
|
||||
|
|
|
|||
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