From 46022538ac7f8ed0b8efddf362d968049adea411 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Mon, 31 Aug 2020 01:38:46 +0000 Subject: [PATCH] Implement lockscreen mockups --- look-and-feel/contents/lockscreen/Clock.qml | 57 +++- .../contents/lockscreen/DraggableDelegate.qml | 56 ++++ look-and-feel/contents/lockscreen/Keypad.qml | 227 ++++++++++++++ .../contents/lockscreen/LockScreen.qml | 286 ++++++++++-------- .../contents/lockscreen/NotificationsList.qml | 58 ++++ .../contents/lockscreen/SimpleHeaderBar.qml | 108 +++++++ .../lockscreen/SimpleNotification.qml | 169 +++++++++++ .../lockscreen/indicators/Battery.qml | 56 ++++ .../lockscreen/indicators/Bluetooth.qml | 62 ++++ .../lockscreen/indicators/Indicator.qml | 44 +++ .../lockscreen/indicators/SignalStrength.qml | 99 ++++++ .../contents/lockscreen/indicators/Volume.qml | 176 +++++++++++ .../contents/lockscreen/indicators/Wifi.qml | 60 ++++ 13 files changed, 1326 insertions(+), 132 deletions(-) create mode 100644 look-and-feel/contents/lockscreen/DraggableDelegate.qml create mode 100644 look-and-feel/contents/lockscreen/Keypad.qml create mode 100644 look-and-feel/contents/lockscreen/NotificationsList.qml create mode 100644 look-and-feel/contents/lockscreen/SimpleHeaderBar.qml create mode 100644 look-and-feel/contents/lockscreen/SimpleNotification.qml create mode 100644 look-and-feel/contents/lockscreen/indicators/Battery.qml create mode 100644 look-and-feel/contents/lockscreen/indicators/Bluetooth.qml create mode 100644 look-and-feel/contents/lockscreen/indicators/Indicator.qml create mode 100644 look-and-feel/contents/lockscreen/indicators/SignalStrength.qml create mode 100644 look-and-feel/contents/lockscreen/indicators/Volume.qml create mode 100644 look-and-feel/contents/lockscreen/indicators/Wifi.qml diff --git a/look-and-feel/contents/lockscreen/Clock.qml b/look-and-feel/contents/lockscreen/Clock.qml index f5c01535..1a3e9b18 100644 --- a/look-and-feel/contents/lockscreen/Clock.qml +++ b/look-and-feel/contents/lockscreen/Clock.qml @@ -1,26 +1,65 @@ +/* +Copyright (C) 2019 Nicolas Fella + +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, see . +*/ + import QtQuick 2.8 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.5 +import QtGraphicalEffects 1.12 import org.kde.plasma.core 2.0 ColumnLayout { readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software - + + property int alignment + Layout.alignment: alignment + spacing: units.gridUnit + Label { - text: Qt.formatTime(timeSource.data["Local"]["DateTime"]) + text: Qt.formatTime(timeSource.data["Local"]["DateTime"], "h:mm ap") color: ColorScope.textColor style: softwareRendering ? Text.Outline : Text.Normal - styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" //no outline, doesn't matter - font.pointSize: 40 - Layout.alignment: Qt.AlignHCenter + styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" // no outline, doesn't matter + + Layout.alignment: alignment + font.weight: Font.Light // this font weight may switch to regular on distros that don't have a light variant + font.pointSize: 36 + layer.enabled: true + layer.effect: DropShadow { + verticalOffset: 1 + radius: 4 + samples: 6 + color: "#757575" + } } Label { - text: Qt.formatDate(timeSource.data["Local"]["DateTime"], Qt.DefaultLocaleLongDate) + text: Qt.formatDate(timeSource.data["Local"]["DateTime"], "ddd, MMM d") color: ColorScope.textColor style: softwareRendering ? Text.Outline : Text.Normal - styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" //no outline, doesn't matter - font.pointSize: 16 - Layout.alignment: Qt.AlignHCenter + styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" // no outline, doesn't matter + + Layout.alignment: alignment + font.pointSize: 10 + layer.enabled: true + layer.effect: DropShadow { + verticalOffset: 1 + radius: 4 + samples: 6 + color: "#757575" + } } DataSource { id: timeSource diff --git a/look-and-feel/contents/lockscreen/DraggableDelegate.qml b/look-and-feel/contents/lockscreen/DraggableDelegate.qml new file mode 100644 index 00000000..2d71edcc --- /dev/null +++ b/look-and-feel/contents/lockscreen/DraggableDelegate.qml @@ -0,0 +1,56 @@ +/* + * Copyright 2019 Marco Martin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see + */ + +import QtQuick 2.10 +import org.kde.kirigami 2.11 as Kirigami + +MouseArea { + id: delegate + + property Item contentItem + property bool draggable: false + signal dismissRequested + + anchors.fill: contentItem + implicitWidth: contentItem ? contentItem.implicitWidth : 0 + implicitHeight: contentItem ? contentItem.implicitHeight : 0 + opacity: 1 - Math.min(1, 1.5 * Math.abs(x) / width) + + drag { + axis: Drag.XAxis + target: draggable && Kirigami.Settings.tabletMode ? this : null + } + + onReleased: { + if (Math.abs(x) > width / 2) { + delegate.dismissRequested(); + } else { + slideAnim.restart(); + } + } + + NumberAnimation { + id: slideAnim + target: delegate + property: "x" + to: 0 + duration: units.longDuration + } +} diff --git a/look-and-feel/contents/lockscreen/Keypad.qml b/look-and-feel/contents/lockscreen/Keypad.qml new file mode 100644 index 00000000..1a13c1c1 --- /dev/null +++ b/look-and-feel/contents/lockscreen/Keypad.qml @@ -0,0 +1,227 @@ +/* +Copyright (C) 2020 Devin Lin + +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, see . +*/ + +import QtQuick 2.12 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.12 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.workspace.keyboardlayout 1.0 + +Rectangle { + color: Qt.rgba(250, 250, 250, 0.85) // slightly translucent background, for key contrast + property string pinLabel: qsTr("Enter PIN") + + // for displaying temporary number in pin dot display + property string lastKeyPressValue: "0" + property int indexWithNumber: -2 + + // keypad functions + function backspace() { + lastKeyPressValue = "0"; + indexWithNumber = -2; + root.password = root.password.substr(0, root.password.length - 1); + } + + function enter() { + authenticator.tryUnlock(root.password); + } + + function keyPress(data) { + if (keypad.pinLabel !== qsTr("Enter PIN")) { + keypad.pinLabel = qsTr("Enter PIN"); + } + lastKeyPressValue = data; + indexWithNumber = root.password.length; + root.password += data + + // trigger turning letter into dot later + letterTimer.restart(); + } + + Connections { + target: authenticator + function onFailed() { + root.password = ""; + pinLabel = qsTr("Wrong PIN") + } + } + + // listen for keyboard events + Keys.onPressed: { + if (event.modifiers === Qt.NoModifier) { + if (event.key === Qt.Key_Backspace) { + backspace(); + } else if (event.key === Qt.Key_Return) { + enter(); + } else if (event.text != "") { + keyPress(event.text); + } + } + } + + // trigger turning letter into dot after 500 milliseconds + Timer { + id: letterTimer + interval: 500 + running: false + repeat: false + onTriggered: { + lastKeyPressValue = 0; + indexWithNumber = -2; + } + } + + ColumnLayout { + anchors { + left: parent.left + right: parent.right + top: parent.top + bottom: parent.bottom + topMargin: units.gridUnit + bottomMargin: units.gridUnit + } + spacing: units.gridUnit + + // pin dot display + Item { + Layout.alignment: Qt.AlignHCenter + Layout.minimumHeight: units.gridUnit * 0.5 + Layout.maximumWidth: parent.width + + Label { + visible: root.password.length === 0 + anchors.centerIn: parent + text: pinLabel + font.pointSize: 12 + color: "#616161" + } + RowLayout { + id: dotDisplay + anchors.centerIn: parent + height: units.gridUnit // maintain height when letter is shown + spacing: 6 + + Repeater { + model: root.password.length + delegate: Rectangle { // dot + visible: index !== indexWithNumber // hide dot if number is shown + Layout.preferredWidth: units.gridUnit * 0.25 + Layout.preferredHeight: Layout.preferredWidth + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + radius: width + color: "#424242" + } + } + Label { // number/letter + visible: root.password.length-1 === indexWithNumber // hide label if no label needed + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: "#424242" + text: lastKeyPressValue + font.pointSize: 10 + } + } + } + + // separator + Rectangle { + Layout.fillWidth: true + height: 1 + color: "#eeeeee" + } + + // number keys + GridLayout { + property string thePw + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + Layout.leftMargin: units.gridUnit * 0.5 + Layout.rightMargin: units.gridUnit * 0.5 + Layout.maximumWidth: units.gridUnit * 22 + Layout.maximumHeight: units.gridUnit * 12.5 + columns: 4 + + // numpad keys + Repeater { + model: ["1", "2", "3", "R", "4", "5", "6", "0", "7", "8", "9", "E"] + + delegate: Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Rectangle { + id: keyRect + anchors.centerIn: parent + width: parent.width + height: parent.height + radius: 5 + color: "white" + visible: modelData.length > 0 + + MouseArea { + anchors.fill: parent + onPressed: parent.color = "#e0e0e0" + onReleased: parent.color = "white" + onClicked: { + if (modelData === "R") { + backspace(); + } else if (modelData === "E") { + enter(); + } else { + keyPress(modelData); + } + } + } + } + + DropShadow { + anchors.fill: keyRect + source: keyRect + cached: true + horizontalOffset: 0 + verticalOffset: 1 + radius: 4 + samples: 6 + color: "#e0e0e0" + } + + PlasmaComponents.Label { + visible: modelData !== "R" && modelData !== "E" + text: modelData + anchors.centerIn: parent + font.pointSize: 18 + color: "#424242" + } + + PlasmaCore.IconItem { + visible: modelData === "R" + anchors.centerIn: parent +// colorGroup: PlasmaCore.ColorScope.backgroundColor + source: "edit-clear" + } + + PlasmaCore.IconItem { + visible: modelData === "E" + anchors.centerIn: parent +// colorGroup: PlasmaCore.ColorScope.backgroundColor + source: "go-next" + } + } + } + } + } +} diff --git a/look-and-feel/contents/lockscreen/LockScreen.qml b/look-and-feel/contents/lockscreen/LockScreen.qml index f4177cc6..520aefab 100644 --- a/look-and-feel/contents/lockscreen/LockScreen.qml +++ b/look-and-feel/contents/lockscreen/LockScreen.qml @@ -22,68 +22,183 @@ import QtGraphicalEffects 1.12 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.workspace.keyboardlayout 1.0 +import org.kde.notificationmanager 1.1 as Notifications import "../components" PlasmaCore.ColorScope { id: root property string password + + property bool isWidescreen: root.height < root.width * 0.75 + property bool notificationsShown: phoneNotificationsList.count !== 0 colorGroup: PlasmaCore.Theme.ComplementaryColorGroup anchors.fill: parent - BrightnessContrast { - id: darken + function isPinDrawerOpen() { + return passwordFlickable.contentY === passwordFlickable.columnHeight; + } + + // blur background once keypad is open + FastBlur { + id: blur + cached: true anchors.fill: parent source: wallpaper - brightness: -(passwordFlickable.contentY / passwordFlickable.columnHeight * 0.6) - } - - DropShadow { - id: clockShadow - anchors.fill: clock - source: clock - horizontalOffset: 1 - verticalOffset: 1 - radius: 6 - samples: 14 - spread: 0.3 - color: PlasmaCore.ColorScope.backgroundColor + visible: true - // hide when keypad is shown - opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight) + property bool doBlur: notificationsShown || isPinDrawerOpen() // only blur once animation finished for performance + + Behavior on doBlur { + NumberAnimation { + target: blur + property: "radius" + duration: 1000 + to: blur.doBlur ? 0 : 50 + easing.type: Easing.InOutQuad + } + PropertyAction { + target: blur + property: "visible" + value: blur.doBlur + } + } } - - Clock { - id: clock - - property Item shadow: clockShadow - - anchors.leftMargin: units.gridUnit - anchors.rightMargin: units.gridUnit - anchors.topMargin: units.gridUnit * 3 - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + + Notifications.WatchedNotificationsModel { + id: notifModel + } + + // header bar + SimpleHeaderBar { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: units.gridUnit opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight) } - MediaControls { + // phone clock component + ColumnLayout { + id: phoneClockComponent + visible: !isWidescreen + anchors { + top: parent.top + topMargin: root.height / 2 - (height / 2 + units.gridUnit * 2) + left: parent.left + right: parent.right + } + spacing: 0 + opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight) + + states: State { + name: "notification"; when: notificationsShown + PropertyChanges { target: phoneClockComponent; anchors.topMargin: units.gridUnit * 5 } + } + + transitions: Transition { + NumberAnimation { + properties: "anchors.topMargin" + easing.type: Easing.InOutQuad + } + } + + Clock { + id: phoneClock + alignment: Qt.AlignHCenter + Layout.bottomMargin: units.gridUnit * 2 // keep spacing even if media controls are gone + } + MediaControls { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.maximumWidth: units.gridUnit * 25 + Layout.minimumWidth: units.gridUnit * 15 + Layout.leftMargin: units.gridUnit + Layout.rightMargin: units.gridUnit + z: 5 + } + } + + // tablet clock component + Item { + id: tabletClockComponent + visible: isWidescreen + width: parent.width / 2 + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + leftMargin: units.gridUnit * 3 + } + + ColumnLayout { + id: tabletLayout + anchors.centerIn: parent + spacing: units.gridUnit + opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight) + + Clock { + id: tabletClock + alignment: Qt.AlignLeft + Layout.fillWidth: true + Layout.minimumWidth: units.gridUnit * 20 + } + MediaControls { + Layout.alignment: Qt.AlignLeft + Layout.fillWidth: true + Layout.maximumWidth: units.gridUnit * 25 + Layout.minimumWidth: units.gridUnit * 20 + z: 5 + } + } + } + + // phone notifications list + NotificationsList { + id: phoneNotificationsList + visible: !isWidescreen + z: passwordFlickable.contentY === 0 ? 5 : 0 // prevent mousearea from interfering with pin drawer + anchors { + top: phoneClockComponent.bottom + topMargin: units.gridUnit + bottom: scrollUpIcon.top + bottomMargin: units.gridUnit left: parent.left leftMargin: units.gridUnit right: parent.right rightMargin: units.gridUnit - verticalCenter: parent.verticalCenter } - opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight) - z: 5 } - + + // tablet notifications list + ColumnLayout { + visible: isWidescreen + z: passwordFlickable.contentY === 0 ? 5 : 0 // prevent mousearea from interfering with pin drawer + anchors { + top: parent.top + bottom: parent.bottom + left: tabletClockComponent.right + right: parent.right + rightMargin: units.gridUnit + } + + NotificationsList { + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Layout.fillWidth: true + Layout.minimumHeight: this.notificationListHeight + Layout.minimumWidth: units.gridUnit * 15 + Layout.maximumWidth: units.gridUnit * 25 + } + } + + // scroll up icon PlasmaCore.IconItem { + id: scrollUpIcon anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right anchors.bottomMargin: units.gridUnit + passwordFlickable.contentY * 0.5 anchors.horizontalCenter: parent.horizontalCenter opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight) @@ -116,8 +231,10 @@ PlasmaCore.ColorScope { // wipe password if it is more than half way down the screen onContentYChanged: { - if (contentY < columnHeight / 2) + if (contentY < columnHeight / 2) { root.password = ""; + keypad.pinLabel = qsTr("Enter PIN"); + } } ColumnLayout { @@ -125,99 +242,22 @@ PlasmaCore.ColorScope { anchors.bottom: parent.bottom width: parent.width - spacing: units.gridUnit * 2 + spacing: units.gridUnit opacity: Math.sin((Math.PI / 2) * (passwordFlickable.contentY / passwordFlickable.columnHeight) + 1.5 * Math.PI) + 1 - PlasmaComponents.Label { + // scroll down icon + PlasmaCore.IconItem { Layout.alignment: Qt.AlignHCenter - text: qsTr("Enter PIN") - font.pointSize: 12 - } - - Row { - id: dotDisplay - Layout.alignment: Qt.AlignHCenter - spacing: 6 - - Layout.minimumHeight: units.gridUnit - Layout.maximumWidth: parent.width - - Repeater { - model: root.password.length - delegate: Rectangle { - width: units.gridUnit - height: width - radius: width - color: Qt.rgba(255, 255, 255, 0.3) - } - } + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + source: "arrow-down" } - GridLayout { - id: numBlock - property string thePw - + Keypad { + id: keypad + focus: passwordFlickable.contentY === passwordFlickable.columnHeight Layout.fillWidth: true - Layout.minimumHeight: units.gridUnit * 16 + Layout.minimumHeight: units.gridUnit * 17 Layout.maximumWidth: root.width - Layout.bottomMargin: units.gridUnit * 2 - Layout.leftMargin: units.gridUnit * 2 - Layout.rightMargin: units.gridUnit * 2 - rowSpacing: units.gridUnit - - columns: 3 - - Repeater { - model: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "R", "0", "E"] - delegate: Item { - Layout.fillWidth: true - Layout.fillHeight: true - - Rectangle { - anchors.centerIn: parent - width: units.gridUnit * 3 - height: width - radius: 12 - color: Qt.rgba(PlasmaCore.ColorScope.backgroundColor.r, PlasmaCore.ColorScope.backgroundColor.g, PlasmaCore.ColorScope.backgroundColor.b, ma.pressed ? 0.8 : 0.3) - visible: modelData.length > 0 - - MouseArea { - id: ma - anchors.fill: parent - onClicked: { - if (modelData === "R") { - root.password = root.password.substr(0, root.password.length - 1); - } else if (modelData === "E") { - authenticator.tryUnlock(root.password); - } else { - root.password += modelData - } - } - } - } - - PlasmaComponents.Label { - visible: modelData !== "R" && modelData !== "E" - text: modelData - anchors.centerIn: parent - font.pointSize: 16 - } - - PlasmaCore.IconItem { - visible: modelData === "R" - anchors.centerIn: parent - colorGroup: PlasmaCore.Theme.ComplementaryColorGroup - source: "edit-clear" - } - - PlasmaCore.IconItem { - visible: modelData === "E" - anchors.centerIn: parent - colorGroup: PlasmaCore.Theme.ComplementaryColorGroup - source: "go-next" - } - } - } } } } diff --git a/look-and-feel/contents/lockscreen/NotificationsList.qml b/look-and-feel/contents/lockscreen/NotificationsList.qml new file mode 100644 index 00000000..85dd03d1 --- /dev/null +++ b/look-and-feel/contents/lockscreen/NotificationsList.qml @@ -0,0 +1,58 @@ +/* +Copyright (C) 2020 Devin Lin + +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, see . +*/ + +import QtQuick 2.12 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.12 +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.notificationmanager 1.1 as Notifications +import "../components" + +Item { + property alias notificationListHeight: notificationListView.contentHeight + property int count: notificationListView.count + + ListView { + id: notificationListView + model: notifModel + + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: Math.min(contentHeight, parent.height) // don't take up the entire screen for notification list view + + interactive: contentHeight > parent.height // only allow scrolling on notifications list if it is long enough + clip: true + opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight) + spacing: units.gridUnit + + delegate: Column { + width: notificationListView.width + spacing: units.smallSpacing + + // insert application heading here once application grouping is implemented + + SimpleNotification { + notification: model + } + } + } +} diff --git a/look-and-feel/contents/lockscreen/SimpleHeaderBar.qml b/look-and-feel/contents/lockscreen/SimpleHeaderBar.qml new file mode 100644 index 00000000..364c874f --- /dev/null +++ b/look-and-feel/contents/lockscreen/SimpleHeaderBar.qml @@ -0,0 +1,108 @@ +/* + * Copyright 2019 Marco Martin + * 2020 Devin Lin + * + * 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.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.12 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PlasmaComponents +import org.kde.plasma.workspace.components 2.0 as PW + +import "indicators" as Indicators + +// a simple version of the task panel +// in the future, it should share components with the existing task panel +PlasmaCore.ColorScope { + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + + layer.enabled: true + layer.effect: DropShadow { + anchors.fill: icons + visible: !showingApp + cached: true + horizontalOffset: 0 + verticalOffset: 1 + radius: 4.0 + samples: 17 + color: Qt.rgba(0,0,0,0.8) + source: icons + } + + PlasmaCore.DataSource { + id: timeSource + engine: "time" + connectedSources: ["Local"] + interval: 60 * 1000 + } + + Rectangle { + anchors.fill: parent + gradient: Gradient { + GradientStop { + position: 1.0 + color: "transparent" + } + GradientStop { + position: 0.0 + color: Qt.rgba(0, 0, 0, 0.1) + } + } + } + + Loader { + id: strengthLoader + height: parent.height + width: item ? item.width : 0 + source: Qt.resolvedUrl("indicators/SignalStrength.qml") + } + + 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 + } + + RowLayout { + id: appletIconsRow + anchors { + bottom: parent.bottom + right: simpleIndicatorsLayout.left + } + height: parent.height + } + + RowLayout { + id: simpleIndicatorsLayout + anchors { + top: parent.top + bottom: parent.bottom + right: parent.right + rightMargin: units.smallSpacing + } + Indicators.Bluetooth {} + Indicators.Wifi {} + Indicators.Volume {} + Indicators.Battery {} + } +} diff --git a/look-and-feel/contents/lockscreen/SimpleNotification.qml b/look-and-feel/contents/lockscreen/SimpleNotification.qml new file mode 100644 index 00000000..39d00151 --- /dev/null +++ b/look-and-feel/contents/lockscreen/SimpleNotification.qml @@ -0,0 +1,169 @@ +/* +Copyright (C) 2020 Devin Lin + +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, see . +*/ + +import QtQuick 2.12 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.12 + +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.notificationmanager 1.1 as Notifications + +import org.kde.kquickcontrolsaddons 2.0 as KQCAddons + +import "../components" + +// meant to be temporary, until the notifications components in plasma-workspace are available to used +// https://invent.kde.org/plasma/plasma-workspace/-/blob/master/applets/notifications/package/contents/ui/NotificationItem.qml +Item { + property var notification + + anchors.left: parent.left + anchors.right: parent.right + height: notifLayout.height + units.gridUnit + + opacity: 1 - Math.min(1, 1.5 * Math.abs(rect.x) / width) // opacity during dismiss swipe + + // notification + Rectangle { + id: rect + + radius: 5 + color: "white" + + height: parent.height + width: parent.width + + border.color: "#bdbdbd" + border.width: 1 + + RowLayout { + id: notifLayout + height: textLayout.height + anchors { + left: parent.left + leftMargin: units.gridUnit * 0.5 + right: parent.right + rightMargin: units.gridUnit * 0.5 + verticalCenter: parent.verticalCenter + } + spacing: units.smallSpacing / 2 + + // notif body + ColumnLayout { + id: textLayout + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + spacing: units.gridUnit / 2 + + Label { + text: notification.summary + color: "#212121" + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + maximumLineCount: 3 + wrapMode: Text.WordWrap + elide: Text.ElideRight + font.pointSize: 11 + } + Label { + text: notification.body + color: "#616161" + Layout.fillWidth: true + wrapMode: Text.WordWrap + elide: Text.ElideRight + font.pointSize: 10 + } + } + + // notification icon + Item { + id: iconContainer + + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + Layout.preferredWidth: units.iconSizes.large + Layout.preferredHeight: units.iconSizes.large + Layout.topMargin: units.smallSpacing + Layout.bottomMargin: units.smallSpacing + + visible: iconItem.active || imageItem.active + + PlasmaCore.IconItem { + id: iconItem + // don't show two identical icons + readonly property bool active: valid && source != notification.applicationIconSource + anchors.fill: parent + usesPlasmaTheme: false + smooth: true + source: { + let icon = notification.icon; + if (typeof icon !== "string") return ""; + if (icon === "dialog-information") return ""; + return icon; + } + visible: active + } + + KQCAddons.QImageItem { + id: imageItem + readonly property bool active: !null && nativeWidth > 0 + anchors.fill: parent + smooth: true + fillMode: KQCAddons.QImageItem.PreserveAspectFit + visible: active + image: typeof notification.icon === "object" ? notification.icon : undefined + } + } + } + + // swipe gesture for dismissing notification (left/right) + MouseArea { + id: dismissSwipe + anchors.fill: parent + drag.axis: Drag.XAxis + drag.target: rect + + onReleased: { + if (Math.abs(rect.x) > width / 2) { // dismiss notification when finished swipe + notifModel.close(notification.id); + } else { + slideAnim.restart(); + } + } + + NumberAnimation { + id: slideAnim + target: rect + property: "x" + to: 0 + duration: units.longDuration + } + } + } + + DropShadow { + anchors.fill: rect + source: rect + horizontalOffset: 1 + verticalOffset: 1 + radius: 4 + samples: 6 + color: "#616161" + } +} diff --git a/look-and-feel/contents/lockscreen/indicators/Battery.qml b/look-and-feel/contents/lockscreen/indicators/Battery.qml new file mode 100644 index 00000000..4e1dc9f6 --- /dev/null +++ b/look-and-feel/contents/lockscreen/indicators/Battery.qml @@ -0,0 +1,56 @@ +/* + * Copyright 2019 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.4 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PlasmaComponents +import org.kde.plasma.workspace.components 2.0 as PW + + +RowLayout { + visible: pmSource.data["Battery"]["Has Cumulative"] + + PW.BatteryIcon { + id: battery + Layout.preferredWidth: height + Layout.fillHeight: true + hasBattery: true + percent: pmSource.data["Battery"]["Percent"] + pluggedIn: pmSource.data["AC Adapter"] ? pmSource.data["AC Adapter"]["Plugged in"] : false + + height: batteryLabel.height + width: batteryLabel.height + + PlasmaCore.DataSource { + id: pmSource + engine: "powermanagement" + connectedSources: ["Battery", "AC Adapter"] + } + } + + PlasmaComponents.Label { + id: batteryLabel + text: i18n("%1%", battery.percent) + Layout.alignment: Qt.AlignVCenter + + color: PlasmaCore.ColorScope.textColor + font.pixelSize: parent.height / 2 + } +} diff --git a/look-and-feel/contents/lockscreen/indicators/Bluetooth.qml b/look-and-feel/contents/lockscreen/indicators/Bluetooth.qml new file mode 100644 index 00000000..b0d01540 --- /dev/null +++ b/look-and-feel/contents/lockscreen/indicators/Bluetooth.qml @@ -0,0 +1,62 @@ +/* + Copyright 2019 MArco MArtni + Copyright 2013-2017 Jan Grulich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.4 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.bluezqt 1.0 as BluezQt + +PlasmaCore.IconItem { + id: connectionIcon + + property bool deviceConnected : false + + source: deviceConnected ? "preferences-system-bluetooth-activated" : "preferences-system-bluetooth"; + colorGroup: PlasmaCore.ColorScope.colorGroup + + visible: BluezQt.Manager.bluetoothOperational + + Layout.fillHeight: true + Layout.preferredWidth: height + function updateStatus() + { + var connectedDevices = []; + + for (var i = 0; i < BluezQt.Manager.devices.length; ++i) { + var device = BluezQt.Manager.devices[i]; + if (device.connected) { + connectedDevices.push(device); + } + } + deviceConnected = connectedDevices.length > 0; + } + + Component.onCompleted: { + BluezQt.Manager.deviceAdded.connect(updateStatus); + BluezQt.Manager.deviceRemoved.connect(updateStatus); + BluezQt.Manager.deviceChanged.connect(updateStatus); + BluezQt.Manager.bluetoothBlockedChanged.connect(updateStatus); + BluezQt.Manager.bluetoothOperationalChanged.connect(updateStatus); + + updateStatus(); + } +} diff --git a/look-and-feel/contents/lockscreen/indicators/Indicator.qml b/look-and-feel/contents/lockscreen/indicators/Indicator.qml new file mode 100644 index 00000000..5710c5ee --- /dev/null +++ b/look-and-feel/contents/lockscreen/indicators/Indicator.qml @@ -0,0 +1,44 @@ +/* + * Copyright 2019 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.4 + +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PlasmaComponents + + +RowLayout { + + property alias icon: icon.source + property alias text: label.text + PlasmaCore.IconItem { + id: icon + colorGroup: PlasmaCore.ColorScope.colorGroup + + Layout.fillHeight: true + Layout.preferredWidth: height + } + PlasmaComponents.Label { + id: label + visible: text.length > 0 + color: PlasmaCore.ColorScope.textColor + font.pixelSize: parent.height / 2 + } +} diff --git a/look-and-feel/contents/lockscreen/indicators/SignalStrength.qml b/look-and-feel/contents/lockscreen/indicators/SignalStrength.qml new file mode 100644 index 00000000..50935573 --- /dev/null +++ b/look-and-feel/contents/lockscreen/indicators/SignalStrength.qml @@ -0,0 +1,99 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 + +import MeeGo.QOfono 0.2 + +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + + +Item { + + width: strengthIcon.height + strengthLabel.width + Layout.minimumWidth: strengthIcon.height + strengthLabel.width + 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() + } + } + + + + PlasmaCore.IconItem { + id: strengthIcon + colorGroup: PlasmaCore.ColorScope.colorGroup + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + width: height + height: parent.height + } + PlasmaComponents.Label { + id: strengthLabel + anchors { + left: strengthIcon.right + verticalCenter: parent.verticalCenter + } + text: netreg.strength + "% " + netreg.name + color: PlasmaCore.ColorScope.textColor + font.pixelSize: parent.height / 2 + } +} diff --git a/look-and-feel/contents/lockscreen/indicators/Volume.qml b/look-and-feel/contents/lockscreen/indicators/Volume.qml new file mode 100644 index 00000000..0283cebc --- /dev/null +++ b/look-and-feel/contents/lockscreen/indicators/Volume.qml @@ -0,0 +1,176 @@ +/* + Copyright 2019 Aditya Mehra + Copyright 2014-2015 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.4 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.volume 0.1 + +PlasmaCore.IconItem { + + id: paIcon + Layout.fillHeight: true + Layout.preferredWidth: height + property bool volumeFeedback: true + property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0) + property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0) + readonly property string dummyOutputName: "auto_null" + source: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink) + ? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted) + : iconName(0, true) + + colorGroup: PlasmaCore.ColorScope.colorGroup + + visible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted + + function iconName(volume, muted, prefix) { + if (!prefix) { + prefix = "audio-volume"; + } + var icon = null; + var percent = volume / maxVolumeValue; + if (percent <= 0.0 || muted) { + icon = prefix + "-muted"; + } else if (percent <= 0.25) { + icon = prefix + "-low"; + } else if (percent <= 0.75) { + icon = prefix + "-medium"; + } else { + icon = prefix + "-high"; + } + return icon; + } + + function isDummyOutput(output) { + return output && output.name === dummyOutputName; + } + + function boundVolume(volume) { + return Math.max(PulseAudio.MinimalVolume, Math.min(volume, maxVolumeValue)); + } + + function volumePercent(volume, max){ + if(!max) { + max = PulseAudio.NormalVolume; + } + return Math.round(volume / max * 100.0); + } + + function playFeedback(sinkIndex) { + if(!volumeFeedback){ + return; + } + if(sinkIndex == undefined) { + sinkIndex = paSinkModel.preferredSink.index; + } + feedback.play(sinkIndex) + } + + function increaseVolume() { + if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { + return; + } + + var volume = boundVolume(paSinkModel.preferredSink.volume + volumeStep); + var percent = volumePercent(volume, maxVolumeValue); + paSinkModel.preferredSink.muted = percent == 0; + paSinkModel.preferredSink.volume = volume; + osd.show(percent); + playFeedback(); + + } + + function decreaseVolume() { + if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { + return; + } + + var volume = boundVolume(paSinkModel.preferredSink.volume - volumeStep); + var percent = volumePercent(volume, maxVolumeValue); + paSinkModel.preferredSink.muted = percent == 0; + paSinkModel.preferredSink.volume = volume; + osd.show(percent); + playFeedback(); + } + + + + function muteVolume() { + if (!paSinkModel.preferredSink || isDummyOutput(paSinkModel.preferredSink)) { + return; + } + + var toMute = !paSinkModel.preferredSink.muted; + paSinkModel.preferredSink.muted = toMute; + osd.show(toMute ? 0 : volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue)); + if (!toMute) { + playFeedback(); + } + } + + SinkModel { + id: paSinkModel + } + + VolumeOSD { + id: osd + } + + VolumeFeedback { + id: feedback + } + + GlobalActionCollection { + // KGlobalAccel cannot transition from kmix to something else, so if + // the user had a custom shortcut set for kmix those would get lost. + // To avoid this we hijack kmix name and actions. Entirely mental but + // best we can do to not cause annoyance for the user. + // The display name actually is updated to whatever registered last + // though, so as far as user visible strings go we should be fine. + // As of 2015-07-21: + // componentName: kmix + // actions: increase_volume, decrease_volume, mute + name: "kmix" + displayName: main.displayName + + GlobalAction { + objectName: "increase_volume" + text: i18n("Increase Volume") + shortcut: Qt.Key_VolumeUp + onTriggered: increaseVolume() + } + + GlobalAction { + objectName: "decrease_volume" + text: i18n("Decrease Volume") + shortcut: Qt.Key_VolumeDown + onTriggered: decreaseVolume() + } + + GlobalAction { + objectName: "mute" + text: i18n("Mute") + shortcut: Qt.Key_VolumeMute + onTriggered: muteVolume() + } + } +} diff --git a/look-and-feel/contents/lockscreen/indicators/Wifi.qml b/look-and-feel/contents/lockscreen/indicators/Wifi.qml new file mode 100644 index 00000000..85e50387 --- /dev/null +++ b/look-and-feel/contents/lockscreen/indicators/Wifi.qml @@ -0,0 +1,60 @@ +/* + Copyright 2019 MArco MArtni + Copyright 2013-2017 Jan Grulich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.4 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.networkmanagement 0.2 as PlasmaNM + +PlasmaCore.IconItem { + id: connectionIcon + + source: connectionIconProvider.connectionIcon + colorGroup: PlasmaCore.ColorScope.colorGroup + + Layout.fillHeight: true + Layout.preferredWidth: height + + PlasmaComponents.BusyIndicator { + id: connectingIndicator + + anchors.fill: parent + running: connectionIconProvider.connecting + visible: running + } + + PlasmaNM.NetworkStatus { + id: networkStatus + } + + PlasmaNM.NetworkModel { + id: connectionModel + } + + PlasmaNM.Handler { + id: handler + } + + PlasmaNM.ConnectionIcon { + id: connectionIconProvider + } +}