Commit b93baf5d authored by Łukasz Korbel's avatar Łukasz Korbel Committed by Nate Graham

Add settings page for predefined timers

Add new page in settings of Timer applet for editing "predefined timers" values. Those values are used in context menu of the applet.

FEATURE: 353091
CCBUG: 301861
CCBUG: 397841
FIXED-IN: 5.21
parent 12d97a8f
......@@ -15,6 +15,11 @@ ConfigModel {
source: "configAppearance.qml"
}
ConfigCategory {
name: i18nc("@title", "Predefined Timers")
icon: "chronometer"
source: "configTimes.qml"
}
ConfigCategory {
name: i18nc("@title", "Advanced")
icon: "preferences-other"
......
/*
* SPDX-FileCopyrightText: 2008, 2014 Davide Bettio <davide.bettio@kdemail.net>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.2
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.kquickcontrolsaddons 2.0 as QtExtra
PlasmaCore.SvgItem {
property int meaning;
property int num;
property string suffix;
width: digitW;
height: digitH;
svg: timerSvg
elementId: num + suffix;
MouseArea {
anchors.fill: parent;
onWheel: {
if (!root.running) {
if (wheel.angleDelta.y > 0) {
if (root.seconds + meaning < 360000) {
root.seconds += meaning;
}
main.digitChanged();
}else if (wheel.angleDelta.y < 0) {
if (root.seconds - meaning >= 0) {
root.seconds -= meaning;
}
main.digitChanged();
}
}
}
}
}
/*
* Copyright 2020 Łukasz Korbel <corebell.it@gmail.com>
*
* 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.5
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 as QtExtra
import org.kde.plasma.private.timer 0.1 as TimerPlasmoid
Row {
id: timerEdit
property int value // time in seconds
property bool editable: true
// in alert mode 2nd set of digits from svg file will be drawn
property bool alertMode: false
// display seconds in addition to hours and minutes (default: enabled)
readonly property bool showSeconds: plasmoid.configuration.showSeconds;
signal digitModified(int valueDelta)
QtObject {
id: internal
readonly property string digitSuffix: alertMode ? "_1" : ""
// digits count include separators with 50% of digit width
readonly property real digits: showSeconds ? 7 : 4.5;
readonly property int digitH: (parent.height / 2) * digits < parent.width ? parent.height : parent.width / digits * 2
readonly property int digitW: digitH / 2;
property string valueString: "000000"
}
Component {
id: digit
PlasmaCore.SvgItem {
property int meaning
property int num
readonly property int mouseWheelAngleThreshold: 5
width: internal.digitW
height: internal.digitH
svg: timerSvg
elementId: num + internal.digitSuffix
MouseArea {
anchors.fill: parent
enabled: editable
onWheel: {
wheel.accepted = true
if (wheel.angleDelta.y > mouseWheelAngleThreshold) {
if (value + meaning < 24*60*60) {
timerEdit.digitModified(meaning)
}
} else if (wheel.angleDelta.y < -mouseWheelAngleThreshold) {
if (value - meaning >= 0) {
timerEdit.digitModified(-meaning)
}
}
}
}
}
}
Component {
id: separator
PlasmaCore.SvgItem {
svg: timerSvg
width: internal.digitW / 2;
height: internal.digitH;
elementId: "separator" + internal.digitSuffix
}
}
PlasmaCore.Svg {
id: timerSvg
imagePath: "widgets/timer"
}
// Following 8 loaders will prepare all elements
// to display time in format "hh:mm:ss"
Loader {
id: hour1
sourceComponent: digit
onLoaded: {
item.meaning = 60*60*10 //10h
item.num = internal.valueString[0]
}
}
Loader {
id: hour2
sourceComponent: digit
onLoaded: {
item.meaning = 60*60 //1h
item.num = internal.valueString[1]
}
}
Loader { sourceComponent: separator } // ":"
Loader {
id: minute1
sourceComponent: digit
onLoaded: {
item.meaning = 600 //10min
item.num = internal.valueString[2]
}
}
Loader {
id: minute2
sourceComponent: digit
onLoaded: {
item.meaning = 60 //1min
item.num = internal.valueString[3]
}
}
Loader { sourceComponent: showSeconds ? separator : undefined } // ":"
Loader {
id: second1
sourceComponent: showSeconds ? digit : undefined
onLoaded: {
item.meaning = 10 //10s
item.num = internal.valueString[4]
}
}
Loader {
id: second2
sourceComponent: showSeconds ? digit : undefined
onLoaded: {
item.meaning = 1 //1s
item.num = internal.valueString[5]
}
}
onValueChanged: {
// update all 6 digits in one signal handler
internal.valueString = TimerPlasmoid.Timer.secondsToString(value, "hhmmss")
if (hour1.item === null) return
hour1.item.num = internal.valueString[0]
hour2.item.num = internal.valueString[1]
minute1.item.num = internal.valueString[2]
minute2.item.num = internal.valueString[3]
second1.item.num = internal.valueString[4]
second2.item.num = internal.valueString[5]
}
}
......@@ -12,32 +12,27 @@ import org.kde.kquickcontrolsaddons 2.0 as QtExtra
Item {
id: main
readonly property date savedAt: plasmoid.configuration.savedAt;
// display seconds in addition to hours and minutes (default: enabled)
readonly property bool showSeconds: plasmoid.configuration.showSeconds;
readonly property real digits: (showSeconds) ? 7 : 4.5;
readonly property int digitH: ((height / 2) * digits < width ? height : ((width - (digits - 1)) / digits) * 2);
readonly property int digitW: digitH / 2;
PlasmaCore.Svg {
id: timerSvg
imagePath: "widgets/timer"
}
readonly property int secondsForAlert: 60
Column {
anchors.centerIn: parent;
Text {
id: titleLabel;
text: root.title;
id: titleLabel
text: root.title
visible: root.showTitle;
horizontalAlignment: Text.AlignHCenter
font.pixelSize: parent.parent.height - digitH;
height: 0.25 * main.height
font.pixelSize: 0.5 * height
}
Row {
id: timerDigits;
TimerEdit {
id: timerDigits
value: root.seconds
editable: !root.running
alertMode: root.running && (root.seconds < main.secondsForAlert)
width: main.width
height: main.height - titleLabel.height
onDigitModified: root.seconds += valueDelta
SequentialAnimation on opacity {
running: root.suspended;
loops: Animation.Infinite;
......@@ -60,54 +55,6 @@ Item {
duration: 400;
}
}
TimerDigit {
meaning: 60*60*10;
num: ~~((root.seconds / (60*60)) / 10);
suffix: (root.running && root.seconds < 60) ? "_1" : "";
}
TimerDigit {
meaning: 60*60;
num: ~~(~~(root.seconds / (60*60))) % 10;
suffix: (root.running && root.seconds < 60) ? "_1" : "";
}
PlasmaCore.SvgItem {
svg: timerSvg;
width: digitW / 2;
height: digitH;
elementId: "separator" + ((root.running && root.seconds < 60) ? "_1" : "");
}
TimerDigit {
meaning: 600;
num: ~~(~~((root.seconds % (60*60)) / 60) / 10);
suffix: (root.running && root.seconds < 60) ? "_1" : "";
}
TimerDigit {
meaning: 60;
num: ~~((root.seconds % (60*60)) / 60) % 10;
suffix: (root.running && root.seconds < 60) ? "_1" : "";
}
PlasmaCore.SvgItem {
svg: timerSvg;
width: digitW / 2;
height: digitH;
elementId: "separator" + ((root.running && root.seconds < 60) ? "_1" : "");
visible: showSeconds;
}
TimerDigit {
meaning: 10;
num: ~~((root.seconds % 60) / 10);
suffix: (root.running && root.seconds < 60) ? "_1" : "";
visible: showSeconds;
}
TimerDigit {
meaning: 1;
num: (root.seconds % 60) % 10;
suffix: (root.running && root.seconds < 60) ? "_1" : "";
visible: showSeconds;
}
}
}
......
/*
* Copyright 2020 Łukasz Korbel <corebell.it@gmail.com>
*
* 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.5
import QtQuick.Controls 2.5 as QQC2
import QtQuick.Layouts 1.0
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
import org.kde.kirigami 2.12 as Kirigami
import org.kde.kcm 1.2 as KCM
ColumnLayout {
id: timesPage
property var cfg_predefinedTimers: []
readonly property int maxListSize: 15
Component.onCompleted: {
for (var i of plasmoid.configuration.predefinedTimers) {
timeListModel.append({"time": i})
}
}
ListModel {
id: timeListModel
function addTimer(value) {
timeListModel.append({"time": value})
cfg_predefinedTimers.splice(count, 0, value)
cfg_predefinedTimersChanged()
}
function removeTimer(index) {
remove(index)
cfg_predefinedTimers.splice(index, 1)
cfg_predefinedTimersChanged()
}
function moveTimer(oldIndex, newIndex) {
move(oldIndex, newIndex, 1)
cfg_predefinedTimers[oldIndex] = get(oldIndex).time
cfg_predefinedTimers[newIndex] = get(newIndex).time
cfg_predefinedTimersChanged()
}
function setTimer(index, newValue) {
setProperty(index, "time", newValue)
cfg_predefinedTimers[index] = newValue
cfg_predefinedTimersChanged()
}
}
KCM.ScrollViewKCM {
Layout.fillWidth: true
Layout.fillHeight: true
view: ListView {
id: timeListView
anchors.margins: 4
model: timeListModel
spacing: parent.spacing
clip: true
delegate: Kirigami.DelegateRecycler {
width: timeListView.width
sourceComponent: timeEditDelegate
}
add: Transition {
NumberAnimation { properties: "y"; duration: Kirigami.Units.longDuration }
}
displaced: Transition {
NumberAnimation { properties: "y"; duration: Kirigami.Units.longDuration }
}
Kirigami.PlaceholderMessage {
visible: timeListView.count === 0
anchors.centerIn: parent
width: parent.width - (units.largeSpacing * 4)
text: i18n("If you add predefined timers here, they will appear in plasmoid context menu.");
}
}
}
QQC2.Button {
icon.name: "list-add"
text: i18n("Add")
enabled: timeListModel.count < maxListSize
onClicked: {
timeListModel.addTimer("0")
timeListView.positionViewAtEnd()
}
}
Component {
id: timeEditDelegate
Kirigami.SwipeListItem {
id: timeEditItem
Row {
spacing: units.largeSpacing
Kirigami.ListItemDragHandle {
anchors.verticalCenter: parent.verticalCenter
enabled: !editor.editable
listItem: timeEditItem
listView: timeListView
onMoveRequested: {
timeListModel.moveTimer(oldIndex, newIndex)
}
}
TimerEdit {
id: editor
alertMode: editable
value: time
property int oldValue: 0
onDigitModified: set(value + valueDelta)
function set(newValue) {
timeListModel.setTimer(index, (newValue).toString())
}
Component.onCompleted: editable = (value === 0)
}
QQC2.Label {
id: hintText
visible: editor.editable
text: i18n("Scroll over digits to change time")
anchors.verticalCenter: parent.verticalCenter
}
}
actions: [
Kirigami.Action {
text: i18n("Apply")
iconName: "dialog-ok-apply"
visible: editor.editable
onTriggered: {
editor.editable = false
}
},
Kirigami.Action {
text: i18n("Cancel")
iconName: "dialog-cancel"
visible: editor.editable
onTriggered: {
editor.editable = false
editor.set(editor.oldValue)
}
},
Kirigami.Action {
text: i18n("Edit")
iconName: "edit-entry"
visible: editor.editable === false
onTriggered: {
editor.oldValue = editor.value
editor.editable = true
}
},
Kirigami.Action {
text: i18n("Delete")
iconName: "entry-delete"
visible: editor.editable === false
onTriggered: {
timeListModel.removeTimer(index)
}
}
]
}
}
}
......@@ -107,27 +107,27 @@ Item {
service.startOperationCall(operation);
}
Component.onCompleted: {
Component.onCompleted: rebuildMenu()
Connections {
target: plasmoid.configuration
onPredefinedTimersChanged: rebuildMenu()
}
function rebuildMenu() {
plasmoid.clearActions();
plasmoid.setAction("timerStart", i18nc("@action", "&Start"));
plasmoid.setAction("timerStop", i18nc("@action", "S&top"));
plasmoid.setAction("timerReset", i18nc("@action", "&Reset"));
plasmoid.setActionSeparator("separator0");
for (var predefinedTimer in plasmoid.configuration.predefinedTimers) {
plasmoid.setAction("predefined_timer_" + plasmoid.configuration.predefinedTimers[predefinedTimer],
secondsToDisplayableString(plasmoid.configuration.predefinedTimers[predefinedTimer]));
for (var predefinedTimer of plasmoid.configuration.predefinedTimers) {
plasmoid.setAction("predefined_timer_" + predefinedTimer,
TimerPlasmoid.Timer.secondsToString(predefinedTimer, "hh:mm:ss"));
}
plasmoid.setActionSeparator("separator1");
}
function secondsToDisplayableString(sec) {
return ~~((sec / (60*60)) / 10) + "" +
(~~(~~(sec / (60*60))) % 10) + ":" +
~~(~~((sec % (60*60)) / 60) / 10) + "" +
~~((sec % (60*60)) / 60) % 10 + ":" +
~~((sec % 60) / 10) + "" +
(sec % 60) % 10;
}
function startTimer() {
running = true;
suspended = false;
......@@ -159,7 +159,7 @@ Item {
}
function actionTriggered(actionName) {
if (actionName.indexOf("predefined_timer_") == 0) {
if (actionName.indexOf("predefined_timer_") === 0) {
seconds = actionName.replace("predefined_timer_", "");
startTimer();
}
......
......@@ -8,6 +8,7 @@
#include <KProcess>
#include <KShell>
#include <QDateTime>
Timer::Timer(QObject *parent) : QObject(parent)
{
......@@ -23,3 +24,7 @@ void Timer::runCommand(const QString &command)
}
}
QString Timer::secondsToString(int seconds, const QString &format)
{
return QTime::fromMSecsSinceStartOfDay(seconds * 1000).toString(format);
}
......@@ -16,6 +16,11 @@ class Timer : public QObject
public:
explicit Timer(QObject *parent = nullptr);
Q_INVOKABLE void runCommand(const QString &command);
/*!
* Represent \a seconds as s string representing time duration with
* given format based on QTime.
*/
Q_INVOKABLE QString secondsToString(int seconds, const QString &format);
};
#endif // TIMER_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment