From 0b9717c2a5f3fd044bfa38f8ae2b99c7c4773b51 Mon Sep 17 00:00:00 2001
From: Nyx <189459385+nyx-4@users.noreply.github.com>
Date: Sat, 2 Aug 2025 22:17:56 +0500
Subject: [PATCH 01/23] Added pomodoro timer in sidebarRight, closes #1477
Signed-off-by: Nyx <189459385+nyx-4@users.noreply.github.com>
---
.../sidebarRight/BottomWidgetGroup.qml | 13 +-
.../sidebarRight/pomodoro/PomodoroWidget.qml | 356 ++++++++++++++++++
.config/quickshell/ii/services/Pomodoro.qml | 69 ++++
3 files changed, 437 insertions(+), 1 deletion(-)
create mode 100644 .config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
create mode 100644 .config/quickshell/ii/services/Pomodoro.qml
diff --git a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml
index 384011634..cd5385c75 100644
--- a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml
@@ -4,6 +4,7 @@ import qs
import qs.services
import "./calendar"
import "./todo"
+import "./pomodoro"
import QtQuick
import QtQuick.Layouts
@@ -17,7 +18,8 @@ Rectangle {
property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed
property var tabs: [
{"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": calendarWidget},
- {"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": todoWidget}
+ {"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": todoWidget},
+ {"type": "timer", "name": Translation.tr("Timer"), "icon": "schedule", "widget": pomodoroWidget},
]
Behavior on implicitHeight {
@@ -238,4 +240,13 @@ Rectangle {
anchors.margins: 5
}
}
+
+ // Pomodoro component
+ Component {
+ id: pomodoroWidget
+ PomodoroWidget {
+ anchors.fill: parent
+ anchors.margins: 5
+ }
+ }
}
\ No newline at end of file
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
new file mode 100644
index 000000000..b39be28ce
--- /dev/null
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -0,0 +1,356 @@
+import qs
+import qs.services
+import qs.modules.common
+import qs.modules.common.widgets
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Item {
+ id: root
+ property int currentTab: 0
+ property var tabButtonList: [
+ {"name": Translation.tr("Pomodoro"), "icon": "timer_play"},
+ {"name": Translation.tr("Stopwatch"), "icon": "timer"}
+ ]
+ property bool showDialog: false
+ property int dialogMargins: 20
+ property int fabSize: 48
+ property int fabMargins: 14
+
+
+ // These are keybinds, make sure to change them.
+ Keys.onPressed: (event) => {
+ if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
+ if (event.key === Qt.Key_PageDown) {
+ currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
+ } else if (event.key === Qt.Key_PageUp) {
+ currentTab = Math.max(currentTab - 1, 0)
+ }
+ event.accepted = true
+ } else if (event.key === Qt.Key_Space && !showDialog) {
+ // Toggle start/pause with Space key
+ if (currentTab === 0) {
+ Pomodoro.togglePomodoro()
+ } else {
+ Pomodoro.toggleStopwatch()
+ }
+ event.accepted = true
+ } else if (event.key === Qt.Key_R && !showDialog) {
+ // Reset with R key
+ if (currentTab === 0) {
+ Pomodoro.pomodoroReset()
+ } else {
+ Pomodoro.stopwatchReset()
+ }
+ event.accepted = true
+ } else if (event.key === Qt.Key_Escape && showDialog) {
+ showDialog = false
+ event.accepted = true
+ }
+ }
+
+ Timer {
+ id: pomodoroTimer
+ interval: 1000
+ running: Pomodoro.isPomodoroRunning
+ repeat: true
+ onTriggered: Pomodoro.tickSecond()
+ }
+
+ Timer {
+ id: stopwatchTimer
+ interval: 1000
+ running: Pomodoro.isStopwatchRunning
+ repeat: true
+ onTriggered: {
+ Pomodoro.stopwatchTime += 1
+ }
+ }
+
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 0
+
+ TabBar {
+ id: tabBar
+ Layout.fillWidth: true
+ currentIndex: currentTab
+ onCurrentIndexChanged: currentTab = currentIndex
+
+ background: Item {
+ WheelHandler {
+ onWheel: (event) => {
+ if (event.angleDelta.y < 0)
+ tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
+ else if (event.angleDelta.y > 0)
+ tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
+ }
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ }
+ }
+
+ Repeater {
+ model: root.tabButtonList
+ delegate: SecondaryTabButton {
+ selected: (index == currentTab)
+ buttonText: modelData.name
+ buttonIcon: modelData.icon
+ }
+ }
+ }
+
+ Item { // Tab indicator
+ id: tabIndicator
+ Layout.fillWidth: true
+ height: 3
+ property bool enableIndicatorAnimation: false
+ Connections {
+ target: root
+ function onCurrentTabChanged() {
+ tabIndicator.enableIndicatorAnimation = true
+ }
+ }
+
+ Rectangle {
+ id: indicator
+ property int tabCount: root.tabButtonList.length
+ property real fullTabSize: root.width / tabCount
+ property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
+
+ implicitWidth: targetWidth
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ }
+
+ x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
+
+ color: Appearance.colors.colPrimary
+ radius: Appearance.rounding.full
+
+ Behavior on x {
+ enabled: tabIndicator.enableIndicatorAnimation
+ animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
+ }
+
+ Behavior on implicitWidth {
+ enabled: tabIndicator.enableIndicatorAnimation
+ animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
+ }
+ }
+ }
+
+ Rectangle { // Tabbar bottom border
+ id: tabBarBottomBorder
+ Layout.fillWidth: true
+ height: 1
+ color: Appearance.colors.colOutlineVariant
+ }
+
+ SwipeView {
+ id: swipeView
+ Layout.topMargin: 10
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ spacing: 10
+ clip: true
+ currentIndex: currentTab
+ onCurrentIndexChanged: {
+ tabIndicator.enableIndicatorAnimation = true
+ currentTab = currentIndex
+ }
+
+ // Pomodoro Timer Tab
+ Item {
+ ColumnLayout {
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: 18
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Pomodoro.timeFormattedPomodoro()
+ font.pixelSize: 50
+ color: Appearance.m3colors.m3onSurface
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 20
+
+ DialogButton {
+ buttonText: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : Translation.tr("Start")
+ Layout.preferredWidth: 90
+ Layout.preferredHeight: 35
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.togglePomodoro()
+ background: Rectangle {
+ color: Appearance.m3colors.m3onSecondary
+ radius: Appearance.rounding.normal
+ border.color: Appearance.m3colors.m3outline
+ border.width: 1
+ }
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Pomodoro.isPomodoroBreak ? Translation.tr("Break time") : Translation.tr("Focus time")
+ font.pixelSize: Appearance.font.pixelSize.largest
+ color: Appearance.m3colors.m3onSurface
+ }
+
+ DialogButton {
+ buttonText: Translation.tr("Reset")
+ Layout.preferredWidth: 90
+ Layout.preferredHeight: 35
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.pomodoroReset()
+ background: Rectangle {
+ color: Appearance.m3colors.m3onError
+ radius: Appearance.rounding.normal
+ border.color: Appearance.m3colors.m3outline
+ border.width: 1
+ }
+ }
+ }
+
+ ColumnLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 0
+
+ StyledText {
+ text: Translation.tr("Focus Duration: %1 min").arg(Pomodoro.pomodoroWorkTime / 60)
+ color: Appearance.m3colors.m3onSurface
+ }
+ Slider {
+ id: workTimeSlider
+ Layout.fillWidth: true
+ from: 5
+ to: 120
+ stepSize: 1
+ value: Pomodoro.pomodoroWorkTime / 60
+ onValueChanged: Pomodoro.pomodoroWorkTime = value * 60
+ handle: Rectangle {
+ x: workTimeSlider.leftPadding + workTimeSlider.visualPosition * (workTimeSlider.availableWidth - width)
+ y: workTimeSlider.topPadding + (workTimeSlider.availableHeight - height) / 2
+ implicitWidth: 20
+ implicitHeight: 20
+ radius: 10
+ color: Appearance.m3colors.m3onSecondary
+ border.color: Appearance.m3colors.m3outline
+ }
+ }
+
+ StyledText {
+ text: Translation.tr("Break Duration: %1 min").arg(Pomodoro.pomodoroBreakTime / 60)
+ color: Appearance.m3colors.m3onSurface
+ }
+ Slider {
+ id: breakTimeSlider
+ Layout.fillWidth: true
+ from: 1
+ to: 60
+ stepSize: 1
+ value: Pomodoro.pomodoroBreakTime / 60
+ onValueChanged: Pomodoro.pomodoroBreakTime = value * 60
+ handle: Rectangle {
+ x: breakTimeSlider.leftPadding + breakTimeSlider.visualPosition * (breakTimeSlider.availableWidth - width)
+ y: breakTimeSlider.topPadding + (breakTimeSlider.availableHeight - height) / 2
+ implicitWidth: 20
+ implicitHeight: 20
+ radius: 10
+ color: Appearance.m3colors.m3onSecondary
+ border.color: Appearance.m3colors.m3outline
+ }
+ }
+ }
+ }
+ }
+
+ // Stopwatch Tab
+ Item {
+ ColumnLayout {
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: 18
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Pomodoro.timeFormattedStopwatch()
+ font.pixelSize: 50
+ color: Appearance.m3colors.m3onSurface
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 20
+
+ DialogButton {
+ buttonText: Pomodoro.isStopwatchRunning ? Translation.tr("Pause") : Translation.tr("Start")
+ Layout.preferredWidth: 90
+ Layout.preferredHeight: 35
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.toggleStopwatch()
+ background: Rectangle {
+ color: Appearance.m3colors.m3onSecondary
+ radius: Appearance.rounding.normal
+ border.color: Appearance.m3colors.m3outline
+ border.width: 1
+ }
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Translation.tr("Stopwatch")
+ font.pixelSize: Appearance.font.pixelSize.largest
+ color: Appearance.m3colors.m3onSurface
+ }
+
+ DialogButton {
+ buttonText: Translation.tr("Reset")
+ Layout.preferredWidth: 90
+ Layout.preferredHeight: 35
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.stopwatchReset()
+ background: Rectangle {
+ color: Appearance.m3colors.m3onError
+ radius: Appearance.rounding.normal
+ border.color: Appearance.m3colors.m3outline
+ border.width: 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // + FAB
+ StyledRectangularShadow {
+ target: fabButton
+ radius: fabButton.buttonRadius
+ blur: 0.6 * Appearance.sizes.elevationMargin
+ }
+ FloatingActionButton {
+ id: fabButton
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.rightMargin: root.fabMargins
+ anchors.bottomMargin: root.fabMargins
+
+ onClicked: {
+ if (currentTab === 0) {
+ Pomodoro.togglePomodoro()
+ } else {
+ Pomodoro.toggleStopwatch()
+ }
+ }
+
+ contentItem: MaterialSymbol {
+ text: (currentTab === 0 && Pomodoro.isPomodoroRunning) || (currentTab === 1 && Pomodoro.isStopwatchRunning) ? "pause" : "play_arrow"
+ horizontalAlignment: Text.AlignHCenter
+ iconSize: Appearance.font.pixelSize.huge
+ color: Appearance.m3colors.m3onPrimaryContainer
+ }
+ }
+}
\ No newline at end of file
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
new file mode 100644
index 000000000..e29ef25ab
--- /dev/null
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -0,0 +1,69 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+
+import qs.modules.common
+import Quickshell
+import Quickshell.Io
+import QtQuick
+
+/**
+ * Simple Pomodoro time manager.
+ */
+Singleton {
+ id: root
+
+ property int pomodoroWorkTime: 25 * 60 // 25 minutes in seconds
+ property int pomodoroBreakTime: 5 * 60 // 5 minutes in seconds
+ property int pomodoroTime: pomodoroWorkTime
+ property bool isPomodoroRunning: false
+ property bool isPomodoroBreak: false
+ property int stopwatchTime: 0
+ property bool isStopwatchRunning: false
+
+ function togglePomodoro() {
+ isPomodoroRunning = !isPomodoroRunning;
+ }
+
+ function toggleStopwatch() {
+ isStopwatchRunning = !isStopwatchRunning;
+ }
+
+ function pomodoroReset() {
+ pomodoroTime = pomodoroWorkTime;
+ isPomodoroRunning = false;
+ isPomodoroBreak = false;
+ }
+
+ function stopwatchReset() {
+ stopwatchTime = 0;
+ isStopwatchRunning = false;
+ }
+
+ function tickSecond() {
+ if (pomodoroTime > 0) {
+ pomodoroTime--;
+ } else {
+ isPomodoroBreak = !isPomodoroBreak;
+ pomodoroTime = isPomodoroBreak ? pomodoroBreakTime : pomodoroWorkTime;
+ if (isPomodoroBreak) {
+ Quickshell.execDetached(["bash", "-c", `notify-send "☕ Short Break!" "Relax for ${Math.floor(pomodoroBreakTime / 60)} minutes."`]);
+ } else {
+ Quickshell.execDetached(["bash", "-c", `notify-send "🔴 Pomodoro started!" "Focus for ${Math.floor(pomodoroWorkTime / 60)} minutes."`]);
+ }
+ }
+ }
+
+ function timeFormattedPomodoro() {
+ let minutes = Math.floor(pomodoroTime / 60);
+ let seconds = Math.floor(pomodoroTime % 60);
+ return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+ }
+
+ function timeFormattedStopwatch() {
+ let totalSeconds = Math.floor(stopwatchTime);
+ let hours = Math.floor(totalSeconds / 3600);
+ let minutes = Math.floor((totalSeconds % 3600) / 60);
+ let seconds = Math.floor(totalSeconds % 60);
+ return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+ }
+}
From e4b761917aec42dee92028d9cd295adbf61868b7 Mon Sep 17 00:00:00 2001
From: Nyx <189459385+nyx-4@users.noreply.github.com>
Date: Sun, 3 Aug 2025 20:36:29 +0500
Subject: [PATCH 02/23] Polished the UI and added Persistent Config option.
Signed-off-by: Nyx <189459385+nyx-4@users.noreply.github.com>
Changelog:
Added Config.options.time.pomodoro options in Config.qml
Restructered the Pomodoro logic and added Long break
Used timestamp instead of naively counting down.
Major UI tweaks.
---
.../quickshell/ii/modules/common/Config.qml | 6 +
.../sidebarRight/pomodoro/PomodoroWidget.qml | 195 ++++++++----------
.config/quickshell/ii/services/Pomodoro.qml | 118 ++++++-----
3 files changed, 164 insertions(+), 155 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml
index bcbbd1e33..3ee804fda 100644
--- a/.config/quickshell/ii/modules/common/Config.qml
+++ b/.config/quickshell/ii/modules/common/Config.qml
@@ -253,6 +253,12 @@ Singleton {
// https://doc.qt.io/qt-6/qtime.html#toString
property string format: "hh:mm"
property string dateFormat: "ddd, dd/MM"
+ property JsonObject pomodoro: JsonObject {
+ property int breaktime: 300
+ property int cycle: 4
+ property int focus: 1500
+ property int longbreak: 1200
+ }
}
property JsonObject windows: JsonObject {
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index b39be28ce..ff39f1b6b 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -166,102 +166,102 @@ Item {
Item {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
- spacing: 18
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.timeFormattedPomodoro()
- font.pixelSize: 50
- color: Appearance.m3colors.m3onSurface
- }
+ spacing: 20
RowLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 20
+ spacing: 40
+ // The Pomodoro timer circle
+ CircularProgress {
+ Layout.alignment: Qt.AlignHCenter
+ lineWidth: 7
+ value: {
+ let pomodoroTotalTime = Pomodoro.isPomodoroBreak ? Pomodoro.pomodoroBreakTime : Pomodoro.pomodoroFocusTime
+ return Pomodoro.getPomodoroSecondsLeft / pomodoroTotalTime
+ }
+ size: 125
+ secondaryColor: Appearance.colors.colSecondaryContainer
+ primaryColor: Appearance.m3colors.m3onSecondaryContainer
+ enableAnimation: true
- DialogButton {
- buttonText: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : Translation.tr("Start")
- Layout.preferredWidth: 90
- Layout.preferredHeight: 35
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.togglePomodoro()
- background: Rectangle {
- color: Appearance.m3colors.m3onSecondary
- radius: Appearance.rounding.normal
- border.color: Appearance.m3colors.m3outline
- border.width: 1
+ ColumnLayout {
+ anchors.centerIn: parent
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: {
+ let minutes = Math.floor(Pomodoro.getPomodoroSecondsLeft / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(Pomodoro.getPomodoroSecondsLeft % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}`
+ }
+ font.pixelSize: Appearance.font.pixelSize.hugeass + 4
+ color: Appearance.m3colors.m3onSurface
+ }
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Pomodoro.isPomodoroBreak ? Translation.tr("Break") : Translation.tr("Focus")
+ font.pixelSize: Appearance.font.pixelSize.normal
+ color: Appearance.m3colors.m3onSurface
+ }
}
}
- StyledText {
+ // The Start/Stop and Reset buttons
+ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.isPomodoroBreak ? Translation.tr("Break time") : Translation.tr("Focus time")
- font.pixelSize: Appearance.font.pixelSize.largest
- color: Appearance.m3colors.m3onSurface
- }
+ spacing: 20
- DialogButton {
- buttonText: Translation.tr("Reset")
- Layout.preferredWidth: 90
- Layout.preferredHeight: 35
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.pomodoroReset()
- background: Rectangle {
- color: Appearance.m3colors.m3onError
- radius: Appearance.rounding.normal
- border.color: Appearance.m3colors.m3outline
- border.width: 1
+ RippleButton {
+ buttonText: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : Translation.tr("Start")
+ Layout.preferredHeight: 35
+ Layout.preferredWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.togglePomodoro()
+ colBackground: Appearance.m3colors.m3onSecondary
+ colBackgroundHover: Appearance.m3colors.m3onSecondary
+ }
+
+ RippleButton {
+ buttonText: Translation.tr("Reset")
+ Layout.preferredHeight: 35
+ Layout.preferredWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.pomodoroReset()
+ colBackground: Appearance.m3colors.m3onError
+ colBackgroundHover: Appearance.m3colors.m3onError
}
}
}
+ // The sliders for adjusting duration
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
- spacing: 0
+ spacing: 10
- StyledText {
- text: Translation.tr("Focus Duration: %1 min").arg(Pomodoro.pomodoroWorkTime / 60)
- color: Appearance.m3colors.m3onSurface
+ ConfigSpinBox {
+ text: Translation.tr("Focus Duration: ")
+ value: Pomodoro.pomodoroFocusTime / 60
+ onValueChanged: {
+ Pomodoro.pomodoroFocusTime = value * 60
+ Config.options.time.pomodoro.focus = value * 60
+ }
+ Layout.alignment: Qt.AlignCenter
}
- Slider {
- id: workTimeSlider
- Layout.fillWidth: true
- from: 5
- to: 120
- stepSize: 1
- value: Pomodoro.pomodoroWorkTime / 60
- onValueChanged: Pomodoro.pomodoroWorkTime = value * 60
- handle: Rectangle {
- x: workTimeSlider.leftPadding + workTimeSlider.visualPosition * (workTimeSlider.availableWidth - width)
- y: workTimeSlider.topPadding + (workTimeSlider.availableHeight - height) / 2
- implicitWidth: 20
- implicitHeight: 20
- radius: 10
- color: Appearance.m3colors.m3onSecondary
- border.color: Appearance.m3colors.m3outline
+
+ ConfigSpinBox {
+ text: Translation.tr("Break Duration:")
+ value: Pomodoro.pomodoroBreakTime / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.breaktime = value * 60
+ Pomodoro.pomodoroBreakTime = value * 60
}
}
- StyledText {
- text: Translation.tr("Break Duration: %1 min").arg(Pomodoro.pomodoroBreakTime / 60)
- color: Appearance.m3colors.m3onSurface
- }
- Slider {
- id: breakTimeSlider
- Layout.fillWidth: true
- from: 1
- to: 60
- stepSize: 1
- value: Pomodoro.pomodoroBreakTime / 60
- onValueChanged: Pomodoro.pomodoroBreakTime = value * 60
- handle: Rectangle {
- x: breakTimeSlider.leftPadding + breakTimeSlider.visualPosition * (breakTimeSlider.availableWidth - width)
- y: breakTimeSlider.topPadding + (breakTimeSlider.availableHeight - height) / 2
- implicitWidth: 20
- implicitHeight: 20
- radius: 10
- color: Appearance.m3colors.m3onSecondary
- border.color: Appearance.m3colors.m3outline
+ ConfigSpinBox {
+ text: Translation.tr("Long Break Duration:")
+ value: Pomodoro.pomodoroLongBreakTime / 60
+ onValueChanged:{
+ Pomodoro.pomodoroLongBreakTime = value * 60
+ Config.options.time.pomodoro.longbreak = value * 60
}
}
}
@@ -276,7 +276,13 @@ Item {
StyledText {
Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.timeFormattedStopwatch()
+ text: {
+ let totalSeconds = Math.floor(Pomodoro.stopwatchTime)
+ let hours = Math.floor(totalSeconds / 3600).toString().padStart(2, '0')
+ let minutes = Math.floor((totalSeconds % 3600) / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `${hours}:${minutes}:${seconds}`
+ }
font.pixelSize: 50
color: Appearance.m3colors.m3onSurface
}
@@ -302,7 +308,7 @@ Item {
StyledText {
Layout.alignment: Qt.AlignHCenter
text: Translation.tr("Stopwatch")
- font.pixelSize: Appearance.font.pixelSize.largest
+ font.pixelSize: Appearance.font.pixelSize.large
color: Appearance.m3colors.m3onSurface
}
@@ -324,33 +330,4 @@ Item {
}
}
}
-
- // + FAB
- StyledRectangularShadow {
- target: fabButton
- radius: fabButton.buttonRadius
- blur: 0.6 * Appearance.sizes.elevationMargin
- }
- FloatingActionButton {
- id: fabButton
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.rightMargin: root.fabMargins
- anchors.bottomMargin: root.fabMargins
-
- onClicked: {
- if (currentTab === 0) {
- Pomodoro.togglePomodoro()
- } else {
- Pomodoro.toggleStopwatch()
- }
- }
-
- contentItem: MaterialSymbol {
- text: (currentTab === 0 && Pomodoro.isPomodoroRunning) || (currentTab === 1 && Pomodoro.isStopwatchRunning) ? "pause" : "play_arrow"
- horizontalAlignment: Text.AlignHCenter
- iconSize: Appearance.font.pixelSize.huge
- color: Appearance.m3colors.m3onPrimaryContainer
- }
- }
-}
\ No newline at end of file
+}
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index e29ef25ab..5244e5382 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -1,10 +1,12 @@
pragma Singleton
pragma ComponentBehavior: Bound
+import qs
import qs.modules.common
-import Quickshell
-import Quickshell.Io
-import QtQuick
+
+import Quickshell;
+import Quickshell.Io;
+import QtQuick;
/**
* Simple Pomodoro time manager.
@@ -12,58 +14,82 @@ import QtQuick
Singleton {
id: root
- property int pomodoroWorkTime: 25 * 60 // 25 minutes in seconds
- property int pomodoroBreakTime: 5 * 60 // 5 minutes in seconds
- property int pomodoroTime: pomodoroWorkTime
- property bool isPomodoroRunning: false
+ // TODO: read these values from a config file.
+ property int pomodoroFocusTime: Config.options.time.pomodoro.focus
+ property int pomodoroBreakTime: Config.options.time.pomodoro.breaktime
+ property int pomodoroLongBreakTime: Config.options.time.pomodoro.longbreak
+ property int pomodoroLongBreakCycle: Config.options.time.pomodoro.cycle
+
+ property int pomodoroTimeLeft: pomodoroFocusTime
+ property int getPomodoroSecondsLeft: pomodoroFocusTime
+ property int pomodoroTimeStarted: getCurrentTime() // The time pomodoro was last Resumed
property bool isPomodoroBreak: false
+ property bool isPomodoroRunning: false
+ property int pomodoroCycle: 1
+
property int stopwatchTime: 0
property bool isStopwatchRunning: false
+ // Pause and Resume button
function togglePomodoro() {
- isPomodoroRunning = !isPomodoroRunning;
- }
-
- function toggleStopwatch() {
- isStopwatchRunning = !isStopwatchRunning;
- }
-
- function pomodoroReset() {
- pomodoroTime = pomodoroWorkTime;
- isPomodoroRunning = false;
- isPomodoroBreak = false;
- }
-
- function stopwatchReset() {
- stopwatchTime = 0;
- isStopwatchRunning = false;
- }
-
- function tickSecond() {
- if (pomodoroTime > 0) {
- pomodoroTime--;
- } else {
- isPomodoroBreak = !isPomodoroBreak;
- pomodoroTime = isPomodoroBreak ? pomodoroBreakTime : pomodoroWorkTime;
- if (isPomodoroBreak) {
- Quickshell.execDetached(["bash", "-c", `notify-send "☕ Short Break!" "Relax for ${Math.floor(pomodoroBreakTime / 60)} minutes."`]);
- } else {
- Quickshell.execDetached(["bash", "-c", `notify-send "🔴 Pomodoro started!" "Focus for ${Math.floor(pomodoroWorkTime / 60)} minutes."`]);
- }
+ isPomodoroRunning = !isPomodoroRunning
+ if (isPomodoroRunning) { // Pressed Start button
+ pomodoroTimeStarted = getCurrentTime()
+ } else { // Pressed Pause button
+ pomodoroTimeLeft -= (getCurrentTime() - pomodoroTimeStarted)
}
}
- function timeFormattedPomodoro() {
- let minutes = Math.floor(pomodoroTime / 60);
- let seconds = Math.floor(pomodoroTime % 60);
- return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+ // Reset button
+ function pomodoroReset() {
+ pomodoroTimeLeft = pomodoroFocusTime
+ getPomodoroSecondsLeft = pomodoroFocusTime
+ isPomodoroBreak = false
+ isPomodoroRunning = false
}
- function timeFormattedStopwatch() {
- let totalSeconds = Math.floor(stopwatchTime);
- let hours = Math.floor(totalSeconds / 3600);
- let minutes = Math.floor((totalSeconds % 3600) / 60);
- let seconds = Math.floor(totalSeconds % 60);
- return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+ function tickSecond() {
+ if (getCurrentTime() >= pomodoroTimeStarted + pomodoroTimeLeft) {
+ isPomodoroBreak = !isPomodoroBreak
+ pomodoroTimeStarted += pomodoroTimeLeft
+ pomodoroTimeLeft = isPomodoroBreak ? pomodoroBreakTime : pomodoroFocusTime
+
+ if (isPomodoroBreak && pomodoroCycle % pomodoroLongBreakCycle == 0) { // isPomodoroLongBreak
+ Quickshell.execDetached([
+ "notify-send",
+ Translation.tr("🌿 Long Break!"),
+ Translation.tr(`Relax for %1 minutes.`).arg(Math.floor(pomodoroLongBreakTime / 60))
+ ])
+ } else if(isPomodoroBreak){
+ Quickshell.execDetached([
+ "notify-send",
+ Translation.tr("☕ Short Break!"),
+ Translation.tr(`Relax for %1 minutes.`).arg(Math.floor(pomodoroBreakTime / 60))
+ ])
+ } else {
+ Quickshell.execDetached([
+ "notify-send",
+ Translation.tr("🔴 Pomodoro started!"),
+ Translation.tr(`Focus for %1 minutes.`).arg(Math.floor(pomodoroFocusTime / 60))
+ ])
+ pomodoroCycle += 1
+ }
+ }
+
+ getPomodoroSecondsLeft = (pomodoroTimeStarted + pomodoroTimeLeft) - getCurrentTime()
+ }
+
+ function getCurrentTime() {
+ return Math.floor(Date.now() / 1000)
+ }
+
+ // Stopwatch functions
+ function toggleStopwatch() {
+ isStopwatchRunning = !isStopwatchRunning
+ }
+
+ function stopwatchReset() {
+ stopwatchTime = 0
+ isStopwatchRunning = false
}
}
From bfb7ccffb5e13b941510d058ae5ae0a89eac95b6 Mon Sep 17 00:00:00 2001
From: Nyx <189459385+nyx-4@users.noreply.github.com>
Date: Tue, 5 Aug 2025 16:06:22 +0500
Subject: [PATCH 03/23] Reworked on Pomodoro's UI and added Stopwatch
Signed-off-by: Nyx <189459385+nyx-4@users.noreply.github.com>
---
.../quickshell/ii/modules/common/Config.qml | 5 +-
.../sidebarRight/pomodoro/PomodoroWidget.qml | 329 +++++++++++++-----
.config/quickshell/ii/services/Pomodoro.qml | 89 +++--
3 files changed, 300 insertions(+), 123 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml
index 3ee804fda..032fd441f 100644
--- a/.config/quickshell/ii/modules/common/Config.qml
+++ b/.config/quickshell/ii/modules/common/Config.qml
@@ -254,10 +254,11 @@ Singleton {
property string format: "hh:mm"
property string dateFormat: "ddd, dd/MM"
property JsonObject pomodoro: JsonObject {
- property int breaktime: 300
+ property int breakTime: 300
property int cycle: 4
property int focus: 1500
- property int longbreak: 1200
+ property int longBreak: 1200
+ property bool running: false
}
}
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index ff39f1b6b..b3c360665 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -2,9 +2,13 @@ import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
+import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
+import Quickshell
+
+
Item {
id: root
@@ -13,10 +17,8 @@ Item {
{"name": Translation.tr("Pomodoro"), "icon": "timer_play"},
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
]
- property bool showDialog: false
- property int dialogMargins: 20
- property int fabSize: 48
- property int fabMargins: 14
+ property int lapsListItemPadding: 8
+ property int lapsListItemSpacing: 5
// These are keybinds, make sure to change them.
@@ -29,7 +31,7 @@ Item {
}
event.accepted = true
} else if (event.key === Qt.Key_Space && !showDialog) {
- // Toggle start/pause with Space key
+ // Toggle start/stop with Space key
if (currentTab === 0) {
Pomodoro.togglePomodoro()
} else {
@@ -52,20 +54,18 @@ Item {
Timer {
id: pomodoroTimer
- interval: 1000
- running: Pomodoro.isPomodoroRunning
+ interval: 200
+ running: Config.options.time.pomodoro.running
repeat: true
onTriggered: Pomodoro.tickSecond()
}
Timer {
id: stopwatchTimer
- interval: 1000
+ interval: 10
running: Pomodoro.isStopwatchRunning
repeat: true
- onTriggered: {
- Pomodoro.stopwatchTime += 1
- }
+ onTriggered: Pomodoro.tick10ms()
}
@@ -174,17 +174,19 @@ Item {
CircularProgress {
Layout.alignment: Qt.AlignHCenter
lineWidth: 7
+ gapAngle: Math.PI / 14
value: {
let pomodoroTotalTime = Pomodoro.isPomodoroBreak ? Pomodoro.pomodoroBreakTime : Pomodoro.pomodoroFocusTime
return Pomodoro.getPomodoroSecondsLeft / pomodoroTotalTime
}
size: 125
- secondaryColor: Appearance.colors.colSecondaryContainer
primaryColor: Appearance.m3colors.m3onSecondaryContainer
+ secondaryColor: Appearance.colors.colSecondaryContainer
enableAnimation: true
ColumnLayout {
anchors.centerIn: parent
+ spacing: 0
StyledText {
Layout.alignment: Qt.AlignHCenter
@@ -208,20 +210,30 @@ Item {
// The Start/Stop and Reset buttons
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
- spacing: 20
+ spacing: 10
RippleButton {
- buttonText: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : Translation.tr("Start")
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Pomodoro.isPomodoroRunning ? Translation.tr("Stop") : Translation.tr("Start")
+ color: Appearance.colors.colSecondary
+ }
Layout.preferredHeight: 35
Layout.preferredWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger
onClicked: Pomodoro.togglePomodoro()
- colBackground: Appearance.m3colors.m3onSecondary
- colBackgroundHover: Appearance.m3colors.m3onSecondary
+ colBackground: Appearance.colors.colSecondaryContainer
+ colBackgroundHover: Appearance.colors.colSecondaryContainer
}
RippleButton {
- buttonText: Translation.tr("Reset")
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Translation.tr("Reset")
+ color: Appearance.colors.colSecondary
+ }
Layout.preferredHeight: 35
Layout.preferredWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger
@@ -232,36 +244,89 @@ Item {
}
}
- // The sliders for adjusting duration
+ // The SpinBoxes for adjusting duration
ColumnLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 10
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 20
- ConfigSpinBox {
- text: Translation.tr("Focus Duration: ")
- value: Pomodoro.pomodoroFocusTime / 60
- onValueChanged: {
- Pomodoro.pomodoroFocusTime = value * 60
- Config.options.time.pomodoro.focus = value * 60
+ StyledText {
+ id: focusTextBox
+ Layout.leftMargin: focusSpinBox.implicitWidth / 2 - 7
+ text: Translation.tr("Focus")
}
- Layout.alignment: Qt.AlignCenter
- }
-
- ConfigSpinBox {
- text: Translation.tr("Break Duration:")
- value: Pomodoro.pomodoroBreakTime / 60
- onValueChanged: {
- Config.options.time.pomodoro.breaktime = value * 60
- Pomodoro.pomodoroBreakTime = value * 60
+ StyledText {
+ Layout.leftMargin: breakSpinBox.implicitWidth / 2 + 10
+ text: Translation.tr("Break")
}
}
- ConfigSpinBox {
- text: Translation.tr("Long Break Duration:")
- value: Pomodoro.pomodoroLongBreakTime / 60
- onValueChanged:{
- Pomodoro.pomodoroLongBreakTime = value * 60
- Config.options.time.pomodoro.longbreak = value * 60
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 0
+
+ ConfigSpinBox {
+ id: focusSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.focus / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.focus = value * 60
+ }
+ }
+
+ ConfigSpinBox {
+ id: breakSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.breakTime / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.breakTime = value * 60
+ }
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 20
+
+ StyledText {
+ Layout.leftMargin: focusSpinBox.implicitWidth / 2 - 6
+ text: Translation.tr("Cycle")
+ }
+ StyledText {
+ Layout.leftMargin: breakSpinBox.implicitWidth / 2
+ text: Translation.tr("Long break")
+ }
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 0
+
+ ConfigSpinBox {
+ id: cycleSpinBox
+ spacing: 0
+ from: 1
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.cycle
+ onValueChanged: {
+ Config.options.time.pomodoro.cycle = value
+ }
+ }
+
+ ConfigSpinBox {
+ id: longBreakSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.longBreak / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.longBreak = value * 60
+ }
}
}
}
@@ -270,59 +335,155 @@ Item {
// Stopwatch Tab
Item {
+ Layout.fillWidth: true
+
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
- spacing: 18
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: {
- let totalSeconds = Math.floor(Pomodoro.stopwatchTime)
- let hours = Math.floor(totalSeconds / 3600).toString().padStart(2, '0')
- let minutes = Math.floor((totalSeconds % 3600) / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `${hours}:${minutes}:${seconds}`
- }
- font.pixelSize: 50
- color: Appearance.m3colors.m3onSurface
- }
+ spacing: 20
+ Layout.fillWidth: true
RowLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 20
+ spacing: 40
+ // The Stopwatch circle
+ CircularProgress {
+ Layout.alignment: Qt.AlignHCenter
+ lineWidth: 7
+ gapAngle: Math.PI / 18
+ value: {
+ return Pomodoro.stopwatchTime % 6000 / 6000 // The seconds in percent
+ }
+ size: 125
+ primaryColor: Math.floor(Pomodoro.stopwatchTime / 6000) % 2 ? Appearance.colors.colSecondaryContainer : Appearance.m3colors.m3onSecondaryContainer
+ secondaryColor: Math.floor(Pomodoro.stopwatchTime / 6000) % 2 ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colSecondaryContainer
+ enableAnimation: false // The animation seems weird after each cycle
- DialogButton {
- buttonText: Pomodoro.isStopwatchRunning ? Translation.tr("Pause") : Translation.tr("Start")
- Layout.preferredWidth: 90
- Layout.preferredHeight: 35
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.toggleStopwatch()
- background: Rectangle {
- color: Appearance.m3colors.m3onSecondary
- radius: Appearance.rounding.normal
- border.color: Appearance.m3colors.m3outline
- border.width: 1
+ ColumnLayout {
+ anchors.centerIn: parent
+ spacing: 0
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: {
+ let totalSeconds = Math.floor(Pomodoro.stopwatchTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}`
+ }
+ font.pixelSize: Appearance.font.pixelSize.hugeass + 4
+ color: Appearance.m3colors.m3onSurface
+ }
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: {
+ return (Math.floor(Pomodoro.stopwatchTime) % 100).toString().padStart(2, '0')
+ }
+ font.pixelSize: Appearance.font.pixelSize.normal
+ color: Appearance.m3colors.m3onSurface
+ }
}
}
- StyledText {
+ // The Start/Stop and Reset buttons
+ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Stopwatch")
- font.pixelSize: Appearance.font.pixelSize.large
- color: Appearance.m3colors.m3onSurface
- }
+ spacing: 10
- DialogButton {
- buttonText: Translation.tr("Reset")
- Layout.preferredWidth: 90
- Layout.preferredHeight: 35
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.stopwatchReset()
- background: Rectangle {
- color: Appearance.m3colors.m3onError
- radius: Appearance.rounding.normal
- border.color: Appearance.m3colors.m3outline
- border.width: 1
+ RippleButton {
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Pomodoro.isStopwatchRunning ? Translation.tr("Stop") : Translation.tr("Start")
+ color: Appearance.colors.colSecondary
+ }
+ Layout.preferredHeight: 35
+ Layout.preferredWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.toggleStopwatch()
+ colBackground: Appearance.colors.colSecondaryContainer
+ colBackgroundHover: Appearance.colors.colSecondaryContainer
+ }
+
+ RippleButton {
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Pomodoro.isStopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
+ color: Appearance.colors.colSecondary
+ }
+ Layout.preferredHeight: 35
+ Layout.preferredWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.stopwatchReset()
+ colBackground: Appearance.m3colors.m3onError
+ colBackgroundHover: Appearance.m3colors.m3onError
+ }
+ }
+ }
+
+ StyledListView {
+ id: lapsList
+ Layout.fillWidth: true
+ Layout.preferredHeight: contentHeight
+ spacing: lapsListItemSpacing
+ clip: true
+ model: Pomodoro.stopwatchLaps
+
+ delegate: Rectangle {
+ width: lapsList.width
+ implicitHeight: lapsContentText.implicitHeight + lapsListItemPadding
+ color: Appearance.colors.colLayer2
+ radius: Appearance.rounding.small
+
+ StyledText {
+ id: lapsContentText
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ leftPadding: lapsListItemPadding
+ rightPadding: lapsListItemPadding
+ topPadding: lapsListItemPadding / 2
+ bottomPadding: lapsListItemPadding / 2
+ font.pixelSize: Appearance.font.pixelSize.normal
+
+ text: {
+ let lapIndex = index + 1
+ let lapTime = modelData
+ // if (index > 0) {
+ // lapTime = modelData - Pomodoro.stopwatchLaps[index - 1]
+ // }
+ let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
+ let totalSeconds = Math.floor(lapTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}.${_10ms}`
+ }
+ }
+
+ StyledText {
+ id: lapsDiffText
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ leftPadding: lapsListItemPadding
+ rightPadding: lapsListItemPadding * 2
+ topPadding: lapsListItemPadding / 2
+ bottomPadding: lapsListItemPadding / 2
+ font.pixelSize: Appearance.font.pixelSize.normal
+ color: Appearance.colors.colPrimary
+
+ text: {
+ let lapTime = modelData
+ if (index != Pomodoro.stopwatchLaps.length - 1) { // except first lap
+ lapTime = modelData - Pomodoro.stopwatchLaps[index + 1]
+ let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
+ let totalSeconds = Math.floor(lapTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `+${minutes}:${seconds}.${_10ms}`
+ } else {
+ return `` // Nothing for first lap
+ }
+ }
}
}
}
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 5244e5382..3387081d5 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -4,9 +4,9 @@ pragma ComponentBehavior: Bound
import qs
import qs.modules.common
-import Quickshell;
-import Quickshell.Io;
-import QtQuick;
+import Quickshell
+import Quickshell.Io
+import QtQuick
/**
* Simple Pomodoro time manager.
@@ -14,29 +14,30 @@ import QtQuick;
Singleton {
id: root
- // TODO: read these values from a config file.
property int pomodoroFocusTime: Config.options.time.pomodoro.focus
- property int pomodoroBreakTime: Config.options.time.pomodoro.breaktime
- property int pomodoroLongBreakTime: Config.options.time.pomodoro.longbreak
+ property int pomodoroBreakTime: Config.options.time.pomodoro.breakTime
+ property int pomodoroLongBreakTime: Config.options.time.pomodoro.longBreak
property int pomodoroLongBreakCycle: Config.options.time.pomodoro.cycle
+ property bool isPomodoroRunning: Config.options.time.pomodoro.running
property int pomodoroTimeLeft: pomodoroFocusTime
property int getPomodoroSecondsLeft: pomodoroFocusTime
- property int pomodoroTimeStarted: getCurrentTime() // The time pomodoro was last Resumed
+ property int pomodoroTimeStarted: getCurrentTimeInSeconds() // The time pomodoro was last Resumed
property bool isPomodoroBreak: false
- property bool isPomodoroRunning: false
property int pomodoroCycle: 1
property int stopwatchTime: 0
property bool isStopwatchRunning: false
+ property int stopwatchStartTime: 0
+ property var stopwatchLaps: []
- // Pause and Resume button
+ // Start and Stop button
function togglePomodoro() {
- isPomodoroRunning = !isPomodoroRunning
+ Config.options.time.pomodoro.running = !isPomodoroRunning
if (isPomodoroRunning) { // Pressed Start button
- pomodoroTimeStarted = getCurrentTime()
- } else { // Pressed Pause button
- pomodoroTimeLeft -= (getCurrentTime() - pomodoroTimeStarted)
+ pomodoroTimeStarted = getCurrentTimeInSeconds()
+ } else { // Pressed Stop button
+ pomodoroTimeLeft -= (getCurrentTimeInSeconds() - pomodoroTimeStarted)
}
}
@@ -45,51 +46,65 @@ Singleton {
pomodoroTimeLeft = pomodoroFocusTime
getPomodoroSecondsLeft = pomodoroFocusTime
isPomodoroBreak = false
- isPomodoroRunning = false
+ Config.options.time.pomodoro.running = false
+ pomodoroCycle = 1
}
function tickSecond() {
- if (getCurrentTime() >= pomodoroTimeStarted + pomodoroTimeLeft) {
+ if (getCurrentTimeInSeconds() >= pomodoroTimeStarted + pomodoroTimeLeft) {
isPomodoroBreak = !isPomodoroBreak
pomodoroTimeStarted += pomodoroTimeLeft
- pomodoroTimeLeft = isPomodoroBreak ? pomodoroBreakTime : pomodoroFocusTime
+ pomodoroTimeLeft = isPomodoroBreak ? pomodoroBreakTime : pomodoroFocusTime
- if (isPomodoroBreak && pomodoroCycle % pomodoroLongBreakCycle == 0) { // isPomodoroLongBreak
- Quickshell.execDetached([
- "notify-send",
- Translation.tr("🌿 Long Break!"),
- Translation.tr(`Relax for %1 minutes.`).arg(Math.floor(pomodoroLongBreakTime / 60))
- ])
- } else if(isPomodoroBreak){
- Quickshell.execDetached([
- "notify-send",
- Translation.tr("☕ Short Break!"),
- Translation.tr(`Relax for %1 minutes.`).arg(Math.floor(pomodoroBreakTime / 60))
- ])
+ let notificationTitle, notificationMessage
+
+ if (isPomodoroBreak && pomodoroCycle % pomodoroLongBreakCycle === 0) { // isPomodoroLongBreak
+ notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(pomodoroLongBreakTime / 60))
+ } else if (isPomodoroBreak) {
+ notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(pomodoroBreakTime / 60))
} else {
- Quickshell.execDetached([
- "notify-send",
- Translation.tr("🔴 Pomodoro started!"),
- Translation.tr(`Focus for %1 minutes.`).arg(Math.floor(pomodoroFocusTime / 60))
- ])
+ notificationMessage = Translation.tr(`Focus for %1 minutes`).arg(Math.floor(pomodoroFocusTime / 60))
pomodoroCycle += 1
}
+
+ Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"])
}
- getPomodoroSecondsLeft = (pomodoroTimeStarted + pomodoroTimeLeft) - getCurrentTime()
+ // A nice abstraction for resume logic by updating the TimeStarted
+ getPomodoroSecondsLeft = (pomodoroTimeStarted + pomodoroTimeLeft) - getCurrentTimeInSeconds()
}
- function getCurrentTime() {
+ function getCurrentTimeInSeconds() { // Pomodoro uses Seconds
return Math.floor(Date.now() / 1000)
}
+ function getCurrentTimeIn10ms() { // Stopwatch uses 10ms
+ return Math.floor(Date.now() / 10)
+ }
+
+ function tick10ms() { // stopwatch stores time in 10ms
+ stopwatchTime = getCurrentTimeIn10ms() - stopwatchStartTime
+ }
+
// Stopwatch functions
function toggleStopwatch() {
isStopwatchRunning = !isStopwatchRunning
+ if (isStopwatchRunning) {
+ // Resume from paused time by adjusting start time
+ stopwatchStartTime = getCurrentTimeIn10ms() - stopwatchTime
+ }
}
function stopwatchReset() {
- stopwatchTime = 0
- isStopwatchRunning = false
+ if (isStopwatchRunning) { // Clicked on Lap
+ stopwatchLaps.unshift(stopwatchTime) // Last lap goes first on list
+ // Reassign to trigger onListChanged, idk copied from Todo.qml
+ root.stopwatchLaps = stopwatchLaps.slice(0)
+ } else { // Clicked on Reset
+ isStopwatchRunning = false
+ stopwatchTime = 0
+ stopwatchStartTime = 0
+ stopwatchLaps = []
+ }
}
}
From 91fee0d6e993b3bbe12396c9c75b50c04d7b4a1c Mon Sep 17 00:00:00 2001
From: Nyx <189459385+nyx-4@users.noreply.github.com>
Date: Fri, 8 Aug 2025 19:51:03 +0500
Subject: [PATCH 04/23] Minor tweaks and better variables names
Signed-off-by: Nyx <189459385+nyx-4@users.noreply.github.com>
---
.../quickshell/ii/modules/common/Config.qml | 3 +-
.../sidebarRight/pomodoro/PomodoroWidget.qml | 46 ++++-----
.config/quickshell/ii/services/Pomodoro.qml | 94 +++++++++++--------
3 files changed, 82 insertions(+), 61 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml
index 032fd441f..73930f938 100644
--- a/.config/quickshell/ii/modules/common/Config.qml
+++ b/.config/quickshell/ii/modules/common/Config.qml
@@ -254,11 +254,12 @@ Singleton {
property string format: "hh:mm"
property string dateFormat: "ddd, dd/MM"
property JsonObject pomodoro: JsonObject {
+ property string alertSound: ""
+ property bool autoRun: false
property int breakTime: 300
property int cycle: 4
property int focus: 1500
property int longBreak: 1200
- property bool running: false
}
}
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index b3c360665..6ab70cc7d 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -21,7 +21,7 @@ Item {
property int lapsListItemSpacing: 5
- // These are keybinds, make sure to change them.
+ // These are keybinds for stopwatch and pomodoro
Keys.onPressed: (event) => {
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
if (event.key === Qt.Key_PageDown) {
@@ -30,15 +30,15 @@ Item {
currentTab = Math.max(currentTab - 1, 0)
}
event.accepted = true
- } else if (event.key === Qt.Key_Space && !showDialog) {
- // Toggle start/stop with Space key
+ } else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) {
+ // Toggle start/stop with Space or S key
if (currentTab === 0) {
Pomodoro.togglePomodoro()
} else {
Pomodoro.toggleStopwatch()
}
event.accepted = true
- } else if (event.key === Qt.Key_R && !showDialog) {
+ } else if (event.key === Qt.Key_R) {
// Reset with R key
if (currentTab === 0) {
Pomodoro.pomodoroReset()
@@ -46,18 +46,18 @@ Item {
Pomodoro.stopwatchReset()
}
event.accepted = true
- } else if (event.key === Qt.Key_Escape && showDialog) {
- showDialog = false
- event.accepted = true
+ } else if (event.key === Qt.Key_L) {
+ // record Stopwatch lap with L key, regardless of current Tab
+ Pomodoro.recordLaps()
}
}
Timer {
id: pomodoroTimer
interval: 200
- running: Config.options.time.pomodoro.running
+ running: Pomodoro.isPomodoroRunning
repeat: true
- onTriggered: Pomodoro.tickSecond()
+ onTriggered: Pomodoro.refreshPomodoro()
}
Timer {
@@ -65,7 +65,7 @@ Item {
interval: 10
running: Pomodoro.isStopwatchRunning
repeat: true
- onTriggered: Pomodoro.tick10ms()
+ onTriggered: Pomodoro.refreshStopwatch()
}
@@ -116,7 +116,7 @@ Item {
Rectangle {
id: indicator
property int tabCount: root.tabButtonList.length
- property real fullTabSize: root.width / tabCount
+ property real fullTabSize: root.width / tabCount;
property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
implicitWidth: targetWidth
@@ -176,7 +176,7 @@ Item {
lineWidth: 7
gapAngle: Math.PI / 14
value: {
- let pomodoroTotalTime = Pomodoro.isPomodoroBreak ? Pomodoro.pomodoroBreakTime : Pomodoro.pomodoroFocusTime
+ let pomodoroTotalTime = Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime
return Pomodoro.getPomodoroSecondsLeft / pomodoroTotalTime
}
size: 125
@@ -200,7 +200,7 @@ Item {
}
StyledText {
Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.isPomodoroBreak ? Translation.tr("Break") : Translation.tr("Focus")
+ text: Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.m3colors.m3onSurface
}
@@ -273,6 +273,10 @@ Item {
value: Config.options.time.pomodoro.focus / 60
onValueChanged: {
Config.options.time.pomodoro.focus = value * 60
+ if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
+ Pomodoro.getPomodoroSecondsLeft = Pomodoro.focusTime
+ Pomodoro.timeLeft = Pomodoro.focusTime
+ }
}
}
@@ -335,7 +339,9 @@ Item {
// Stopwatch Tab
Item {
+ id: stopwatchTab
Layout.fillWidth: true
+ Layout.fillHeight: true
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
@@ -346,6 +352,7 @@ Item {
spacing: 40
// The Stopwatch circle
CircularProgress {
+ id: stopwatchClock
Layout.alignment: Qt.AlignHCenter
lineWidth: 7
gapAngle: Math.PI / 18
@@ -413,7 +420,7 @@ Item {
Layout.preferredHeight: 35
Layout.preferredWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.stopwatchReset()
+ onClicked: Pomodoro.stopwatchResetOrLaps()
colBackground: Appearance.m3colors.m3onError
colBackgroundHover: Appearance.m3colors.m3onError
}
@@ -423,9 +430,10 @@ Item {
StyledListView {
id: lapsList
Layout.fillWidth: true
- Layout.preferredHeight: contentHeight
+ Layout.preferredHeight: stopwatchTab.height - stopwatchClock.height - 20
spacing: lapsListItemSpacing
clip: true
+ popin: true
model: Pomodoro.stopwatchLaps
delegate: Rectangle {
@@ -446,11 +454,8 @@ Item {
font.pixelSize: Appearance.font.pixelSize.normal
text: {
- let lapIndex = index + 1
let lapTime = modelData
- // if (index > 0) {
- // lapTime = modelData - Pomodoro.stopwatchLaps[index - 1]
- // }
+
let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
let totalSeconds = Math.floor(lapTime) / 100
let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
@@ -472,9 +477,8 @@ Item {
color: Appearance.colors.colPrimary
text: {
- let lapTime = modelData
if (index != Pomodoro.stopwatchLaps.length - 1) { // except first lap
- lapTime = modelData - Pomodoro.stopwatchLaps[index + 1]
+ let lapTime = modelData - Pomodoro.stopwatchLaps[index + 1]
let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
let totalSeconds = Math.floor(lapTime) / 100
let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 3387081d5..9b7b68da4 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -14,64 +14,72 @@ import QtQuick
Singleton {
id: root
- property int pomodoroFocusTime: Config.options.time.pomodoro.focus
- property int pomodoroBreakTime: Config.options.time.pomodoro.breakTime
- property int pomodoroLongBreakTime: Config.options.time.pomodoro.longBreak
- property int pomodoroLongBreakCycle: Config.options.time.pomodoro.cycle
- property bool isPomodoroRunning: Config.options.time.pomodoro.running
+ property int focusTime: Config.options.time.pomodoro.focus
+ property int breakTime: Config.options.time.pomodoro.breakTime
+ property int longBreakTime: Config.options.time.pomodoro.longBreak
+ property int longBreakCycle: Config.options.time.pomodoro.cycle
+ property string alertSound: Config.options.time.pomodoro.alertSound
- property int pomodoroTimeLeft: pomodoroFocusTime
- property int getPomodoroSecondsLeft: pomodoroFocusTime
- property int pomodoroTimeStarted: getCurrentTimeInSeconds() // The time pomodoro was last Resumed
- property bool isPomodoroBreak: false
+ property bool isPomodoroRunning: Config.options.time.pomodoro.autoRun
+ property bool isBreak: false
+ property bool isPomodoroReset: !isPomodoroRunning
+ property int timeLeft: focusTime
+ property int getPomodoroSecondsLeft: focusTime
+ property int pomodoroStartTime: getCurrentTimeInSeconds() // The time pomodoro was last Resumed
property int pomodoroCycle: 1
- property int stopwatchTime: 0
property bool isStopwatchRunning: false
- property int stopwatchStartTime: 0
+ property int stopwatchTime: 0
+ property int stopwatchStart: 0
property var stopwatchLaps: []
// Start and Stop button
function togglePomodoro() {
- Config.options.time.pomodoro.running = !isPomodoroRunning
+ isPomodoroReset = false
+ isPomodoroRunning = !isPomodoroRunning
if (isPomodoroRunning) { // Pressed Start button
- pomodoroTimeStarted = getCurrentTimeInSeconds()
+ pomodoroStartTime = getCurrentTimeInSeconds()
} else { // Pressed Stop button
- pomodoroTimeLeft -= (getCurrentTimeInSeconds() - pomodoroTimeStarted)
+ timeLeft -= (getCurrentTimeInSeconds() - pomodoroStartTime)
}
}
// Reset button
function pomodoroReset() {
- pomodoroTimeLeft = pomodoroFocusTime
- getPomodoroSecondsLeft = pomodoroFocusTime
- isPomodoroBreak = false
- Config.options.time.pomodoro.running = false
+ isPomodoroRunning = false
+ isBreak = false
+ isPomodoroReset = true
+ timeLeft = focusTime
+ pomodoroStartTime = getCurrentTimeInSeconds()
pomodoroCycle = 1
+ refreshPomodoro()
}
- function tickSecond() {
- if (getCurrentTimeInSeconds() >= pomodoroTimeStarted + pomodoroTimeLeft) {
- isPomodoroBreak = !isPomodoroBreak
- pomodoroTimeStarted += pomodoroTimeLeft
- pomodoroTimeLeft = isPomodoroBreak ? pomodoroBreakTime : pomodoroFocusTime
+ function refreshPomodoro() {
+ if (getCurrentTimeInSeconds() >= pomodoroStartTime + timeLeft) {
+ isBreak = !isBreak
+ pomodoroStartTime += timeLeft
+ timeLeft = isBreak ? breakTime : focusTime
let notificationTitle, notificationMessage
- if (isPomodoroBreak && pomodoroCycle % pomodoroLongBreakCycle === 0) { // isPomodoroLongBreak
- notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(pomodoroLongBreakTime / 60))
- } else if (isPomodoroBreak) {
- notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(pomodoroBreakTime / 60))
+ if (isBreak && pomodoroCycle % longBreakCycle === 0) { // isPomodoroLongBreak
+ notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(longBreakTime / 60))
+ } else if (isBreak) {
+ notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(breakTime / 60))
} else {
- notificationMessage = Translation.tr(`Focus for %1 minutes`).arg(Math.floor(pomodoroFocusTime / 60))
+ notificationMessage = Translation.tr(`Focus for %1 minutes`).arg(Math.floor(focusTime / 60))
pomodoroCycle += 1
}
Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"])
+ if (alertSound) { // Play sound only if alertSound is explicitly specified
+ Quickshell.execDetached(["bash", "-c", `ffplay -nodisp -autoexit ${alertSound}`])
+ }
}
// A nice abstraction for resume logic by updating the TimeStarted
- getPomodoroSecondsLeft = (pomodoroTimeStarted + pomodoroTimeLeft) - getCurrentTimeInSeconds()
+ getPomodoroSecondsLeft = (pomodoroStartTime + timeLeft) - getCurrentTimeInSeconds()
}
function getCurrentTimeInSeconds() { // Pomodoro uses Seconds
@@ -82,8 +90,8 @@ Singleton {
return Math.floor(Date.now() / 10)
}
- function tick10ms() { // stopwatch stores time in 10ms
- stopwatchTime = getCurrentTimeIn10ms() - stopwatchStartTime
+ function refreshStopwatch() { // stopwatch stores time in 10ms
+ stopwatchTime = getCurrentTimeIn10ms() - stopwatchStart
}
// Stopwatch functions
@@ -91,20 +99,28 @@ Singleton {
isStopwatchRunning = !isStopwatchRunning
if (isStopwatchRunning) {
// Resume from paused time by adjusting start time
- stopwatchStartTime = getCurrentTimeIn10ms() - stopwatchTime
+ stopwatchStart = getCurrentTimeIn10ms() - stopwatchTime
+ }
+ }
+
+ function stopwatchResetOrLaps() {
+ if (isStopwatchRunning) { // Clicked on Lap
+ recordLaps()
+ } else { // Clicked on Reset
+ stopwatchReset()
}
}
function stopwatchReset() {
- if (isStopwatchRunning) { // Clicked on Lap
+ isStopwatchRunning = false
+ stopwatchTime = 0
+ stopwatchStart = 0
+ stopwatchLaps = []
+ }
+
+ function recordLaps() {
stopwatchLaps.unshift(stopwatchTime) // Last lap goes first on list
// Reassign to trigger onListChanged, idk copied from Todo.qml
root.stopwatchLaps = stopwatchLaps.slice(0)
- } else { // Clicked on Reset
- isStopwatchRunning = false
- stopwatchTime = 0
- stopwatchStartTime = 0
- stopwatchLaps = []
- }
}
}
From 91dae8ad85c816a0b3eebe14e01d2c2a67b56ef9 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 10:42:50 +0700
Subject: [PATCH 05/23] pomodoro widget: use grid instead of row/col spam
---
.../sidebarRight/pomodoro/PomodoroWidget.qml | 144 ++++++++----------
1 file changed, 65 insertions(+), 79 deletions(-)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index 6ab70cc7d..446a32977 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -8,8 +8,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
-
-
Item {
id: root
property int currentTab: 0
@@ -245,92 +243,80 @@ Item {
}
// The SpinBoxes for adjusting duration
- ColumnLayout {
- RowLayout {
- Layout.fillWidth: true
- spacing: 20
+ GridLayout {
+ Layout.alignment: Qt.AlignHCenter
+ columns: 2
+ uniformCellWidths: true
+ columnSpacing: 20
+ rowSpacing: 6
- StyledText {
- id: focusTextBox
- Layout.leftMargin: focusSpinBox.implicitWidth / 2 - 7
- text: Translation.tr("Focus")
- }
- StyledText {
- Layout.leftMargin: breakSpinBox.implicitWidth / 2 + 10
- text: Translation.tr("Break")
- }
- }
-
- RowLayout {
+ StyledText {
Layout.alignment: Qt.AlignHCenter
- spacing: 0
-
- ConfigSpinBox {
- id: focusSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.focus / 60
- onValueChanged: {
- Config.options.time.pomodoro.focus = value * 60
- if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
- Pomodoro.getPomodoroSecondsLeft = Pomodoro.focusTime
- Pomodoro.timeLeft = Pomodoro.focusTime
- }
- }
- }
-
- ConfigSpinBox {
- id: breakSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.breakTime / 60
- onValueChanged: {
- Config.options.time.pomodoro.breakTime = value * 60
- }
- }
+ text: Translation.tr("Focus")
}
- RowLayout {
- Layout.fillWidth: true
- spacing: 20
-
- StyledText {
- Layout.leftMargin: focusSpinBox.implicitWidth / 2 - 6
- text: Translation.tr("Cycle")
- }
- StyledText {
- Layout.leftMargin: breakSpinBox.implicitWidth / 2
- text: Translation.tr("Long break")
- }
- }
-
- RowLayout {
+ StyledText {
Layout.alignment: Qt.AlignHCenter
- spacing: 0
+ text: Translation.tr("Break")
+ }
- ConfigSpinBox {
- id: cycleSpinBox
- spacing: 0
- from: 1
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.cycle
- onValueChanged: {
- Config.options.time.pomodoro.cycle = value
+ ConfigSpinBox {
+ id: focusSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.focus / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.focus = value * 60
+ if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
+ Pomodoro.getPomodoroSecondsLeft = Pomodoro.focusTime
+ Pomodoro.timeLeft = Pomodoro.focusTime
}
}
+ }
- ConfigSpinBox {
- id: longBreakSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.longBreak / 60
- onValueChanged: {
- Config.options.time.pomodoro.longBreak = value * 60
- }
+ ConfigSpinBox {
+ id: breakSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.breakTime / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.breakTime = value * 60
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: 6
+ Layout.alignment: Qt.AlignHCenter
+ text: Translation.tr("Cycle")
+ }
+ StyledText {
+ Layout.topMargin: 6
+ Layout.alignment: Qt.AlignHCenter
+ text: Translation.tr("Long break")
+ }
+
+ ConfigSpinBox {
+ id: cycleSpinBox
+ spacing: 0
+ from: 1
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.cycle
+ onValueChanged: {
+ Config.options.time.pomodoro.cycle = value
+ }
+ }
+
+ ConfigSpinBox {
+ id: longBreakSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.longBreak / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.longBreak = value * 60
}
}
}
From 9bc4ff16a7b3aa502776658fc403deecdc531c9e Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 12:54:37 +0700
Subject: [PATCH 06/23] make pomodoro timer persistent
---
.config/quickshell/ii/modules/common/Config.qml | 1 -
.../quickshell/ii/modules/common/Persistent.qml | 7 +++++++
.config/quickshell/ii/services/Pomodoro.qml | 14 +++++++-------
3 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml
index 73930f938..9580c3bc0 100644
--- a/.config/quickshell/ii/modules/common/Config.qml
+++ b/.config/quickshell/ii/modules/common/Config.qml
@@ -255,7 +255,6 @@ Singleton {
property string dateFormat: "ddd, dd/MM"
property JsonObject pomodoro: JsonObject {
property string alertSound: ""
- property bool autoRun: false
property int breakTime: 300
property int cycle: 4
property int focus: 1500
diff --git a/.config/quickshell/ii/modules/common/Persistent.qml b/.config/quickshell/ii/modules/common/Persistent.qml
index abd062d73..a650dc0bb 100644
--- a/.config/quickshell/ii/modules/common/Persistent.qml
+++ b/.config/quickshell/ii/modules/common/Persistent.qml
@@ -44,6 +44,13 @@ Singleton {
property bool allowNsfw: false
property string provider: "yandere"
}
+
+ property JsonObject timer: JsonObject {
+ property JsonObject pomodoro: JsonObject {
+ property bool running: false
+ property int start: 0
+ }
+ }
}
}
}
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 9b7b68da4..073280d65 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -20,12 +20,12 @@ Singleton {
property int longBreakCycle: Config.options.time.pomodoro.cycle
property string alertSound: Config.options.time.pomodoro.alertSound
- property bool isPomodoroRunning: Config.options.time.pomodoro.autoRun
+ property bool isPomodoroRunning: Persistent.states.timer.pomodoro.running
property bool isBreak: false
property bool isPomodoroReset: !isPomodoroRunning
property int timeLeft: focusTime
property int getPomodoroSecondsLeft: focusTime
- property int pomodoroStartTime: getCurrentTimeInSeconds() // The time pomodoro was last Resumed
+ property int pomodoroStartTime: Persistent.states.timer.pomodoro.start
property int pomodoroCycle: 1
property bool isStopwatchRunning: false
@@ -36,9 +36,9 @@ Singleton {
// Start and Stop button
function togglePomodoro() {
isPomodoroReset = false
- isPomodoroRunning = !isPomodoroRunning
+ Persistent.states.timer.pomodoro.running = !isPomodoroRunning
if (isPomodoroRunning) { // Pressed Start button
- pomodoroStartTime = getCurrentTimeInSeconds()
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
} else { // Pressed Stop button
timeLeft -= (getCurrentTimeInSeconds() - pomodoroStartTime)
}
@@ -46,11 +46,11 @@ Singleton {
// Reset button
function pomodoroReset() {
- isPomodoroRunning = false
+ Persistent.states.timer.pomodoro.running = false
isBreak = false
isPomodoroReset = true
timeLeft = focusTime
- pomodoroStartTime = getCurrentTimeInSeconds()
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
pomodoroCycle = 1
refreshPomodoro()
}
@@ -58,7 +58,7 @@ Singleton {
function refreshPomodoro() {
if (getCurrentTimeInSeconds() >= pomodoroStartTime + timeLeft) {
isBreak = !isBreak
- pomodoroStartTime += timeLeft
+ Persistent.states.timer.pomodoro.start += timeLeft
timeLeft = isBreak ? breakTime : focusTime
let notificationTitle, notificationMessage
From de759b5120004806323955493e163ce97e604750 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 12:54:53 +0700
Subject: [PATCH 07/23] redesign stopwatch
---
.../sidebarRight/pomodoro/PomodoroWidget.qml | 168 +++++++++---------
1 file changed, 83 insertions(+), 85 deletions(-)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index 446a32977..8e0d65f74 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -15,9 +15,6 @@ Item {
{"name": Translation.tr("Pomodoro"), "icon": "timer_play"},
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
]
- property int lapsListItemPadding: 8
- property int lapsListItemSpacing: 5
-
// These are keybinds for stopwatch and pomodoro
Keys.onPressed: (event) => {
@@ -214,7 +211,7 @@ Item {
contentItem: StyledText {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isPomodoroRunning ? Translation.tr("Stop") : Translation.tr("Start")
+ text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : Translation.tr("Start")
color: Appearance.colors.colSecondary
}
Layout.preferredHeight: 35
@@ -330,62 +327,53 @@ Item {
Layout.fillHeight: true
ColumnLayout {
- anchors.horizontalCenter: parent.horizontalCenter
+ anchors {
+ fill: parent
+ leftMargin: 20
+ rightMargin: 20
+ }
spacing: 20
- Layout.fillWidth: true
- RowLayout {
- spacing: 40
- // The Stopwatch circle
- CircularProgress {
- id: stopwatchClock
+ ColumnLayout {
+ spacing: 8
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: false
+
+ RowLayout { // Elapsed
+ id: elapsedIndicator
Layout.alignment: Qt.AlignHCenter
- lineWidth: 7
- gapAngle: Math.PI / 18
- value: {
- return Pomodoro.stopwatchTime % 6000 / 6000 // The seconds in percent
- }
- size: 125
- primaryColor: Math.floor(Pomodoro.stopwatchTime / 6000) % 2 ? Appearance.colors.colSecondaryContainer : Appearance.m3colors.m3onSecondaryContainer
- secondaryColor: Math.floor(Pomodoro.stopwatchTime / 6000) % 2 ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colSecondaryContainer
- enableAnimation: false // The animation seems weird after each cycle
-
- ColumnLayout {
- anchors.centerIn: parent
- spacing: 0
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: {
- let totalSeconds = Math.floor(Pomodoro.stopwatchTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `${minutes}:${seconds}`
- }
- font.pixelSize: Appearance.font.pixelSize.hugeass + 4
- color: Appearance.m3colors.m3onSurface
+ spacing: 0
+ StyledText {
+ Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
+ font.pixelSize: 40
+ color: Appearance.m3colors.m3onSurface
+ text: {
+ let totalSeconds = Math.floor(Pomodoro.stopwatchTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}`
}
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: {
- return (Math.floor(Pomodoro.stopwatchTime) % 100).toString().padStart(2, '0')
- }
- font.pixelSize: Appearance.font.pixelSize.normal
- color: Appearance.m3colors.m3onSurface
+ }
+ StyledText {
+ Layout.fillWidth: true
+ font.pixelSize: 40
+ color: Appearance.colors.colSubtext
+ text: {
+ return `:${(Math.floor(Pomodoro.stopwatchTime) % 100).toString().padStart(2, '0')}`
}
}
}
// The Start/Stop and Reset buttons
- ColumnLayout {
+ RowLayout {
Layout.alignment: Qt.AlignHCenter
- spacing: 10
+ spacing: 4
RippleButton {
contentItem: StyledText {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isStopwatchRunning ? Translation.tr("Stop") : Translation.tr("Start")
+ text: Pomodoro.isStopwatchRunning ? Translation.tr("Pause") : Pomodoro.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
color: Appearance.colors.colSecondary
}
Layout.preferredHeight: 35
@@ -413,65 +401,75 @@ Item {
}
}
+ // Laps
StyledListView {
id: lapsList
Layout.fillWidth: true
- Layout.preferredHeight: stopwatchTab.height - stopwatchClock.height - 20
+ Layout.fillHeight: true
spacing: lapsListItemSpacing
clip: true
popin: true
- model: Pomodoro.stopwatchLaps
+
+ model: ScriptModel {
+ values: Pomodoro.stopwatchLaps
+ }
delegate: Rectangle {
+ id: lapItem
+ required property int index
+ required property var modelData
+ property var horizontalPadding: 10
+ property var verticalPadding: 6
width: lapsList.width
- implicitHeight: lapsContentText.implicitHeight + lapsListItemPadding
+ implicitHeight: lapRow.implicitHeight + verticalPadding * 2
+ implicitWidth: lapRow.implicitWidth + horizontalPadding * 2
color: Appearance.colors.colLayer2
radius: Appearance.rounding.small
- StyledText {
- id: lapsContentText
- anchors.left: parent.left
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- leftPadding: lapsListItemPadding
- rightPadding: lapsListItemPadding
- topPadding: lapsListItemPadding / 2
- bottomPadding: lapsListItemPadding / 2
- font.pixelSize: Appearance.font.pixelSize.normal
-
- text: {
- let lapTime = modelData
-
- let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
- let totalSeconds = Math.floor(lapTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `${minutes}:${seconds}.${_10ms}`
+ RowLayout {
+ id: lapRow
+ anchors {
+ fill: parent
+ leftMargin: lapItem.horizontalPadding
+ rightMargin: lapItem.horizontalPadding
+ topMargin: lapItem.verticalPadding
+ bottomMargin: lapItem.verticalPadding
}
- }
- StyledText {
- id: lapsDiffText
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- leftPadding: lapsListItemPadding
- rightPadding: lapsListItemPadding * 2
- topPadding: lapsListItemPadding / 2
- bottomPadding: lapsListItemPadding / 2
- font.pixelSize: Appearance.font.pixelSize.normal
- color: Appearance.colors.colPrimary
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.small
+ color: Appearance.colors.colSubtext
+ text: `${Pomodoro.stopwatchLaps.length - lapItem.index}.`
+ }
- text: {
- if (index != Pomodoro.stopwatchLaps.length - 1) { // except first lap
- let lapTime = modelData - Pomodoro.stopwatchLaps[index + 1]
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.small
+ text: {
+ let lapTime = lapItem.modelData
let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
let totalSeconds = Math.floor(lapTime) / 100
let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `+${minutes}:${seconds}.${_10ms}`
- } else {
- return `` // Nothing for first lap
+ return `${minutes}:${seconds}.${_10ms}`
+ }
+ }
+
+ Item { Layout.fillWidth: true }
+
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.smaller
+ color: Appearance.colors.colPrimary
+ text: {
+ if (lapItem.index != Pomodoro.stopwatchLaps.length - 1) { // except first lap
+ let lapTime = lapItem.modelData - Pomodoro.stopwatchLaps[lapItem.index + 1]
+ let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
+ let totalSeconds = Math.floor(lapTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `+${minutes == "00" ? "" : minutes + ":"}${seconds}.${_10ms}`
+ } else {
+ return `` // Nothing for first lap
+ }
}
}
}
From 0ee9afba4f3f3efa4d93bc57570ac6eba8108741 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 16:28:17 +0700
Subject: [PATCH 08/23] pomodoro: move tabs to their own files, adjust colors
---
.../ii/modules/common/Appearance.qml | 8 +
.../modules/common/widgets/RippleButton.qml | 1 +
.../sidebarRight/pomodoro/PomodoroTimer.qml | 176 ++++++++++
.../sidebarRight/pomodoro/PomodoroWidget.qml | 327 +-----------------
.../sidebarRight/pomodoro/Stopwatch.qml | 170 +++++++++
5 files changed, 359 insertions(+), 323 deletions(-)
create mode 100644 .config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
create mode 100644 .config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
diff --git a/.config/quickshell/ii/modules/common/Appearance.qml b/.config/quickshell/ii/modules/common/Appearance.qml
index 9c9374860..c16574e0f 100644
--- a/.config/quickshell/ii/modules/common/Appearance.qml
+++ b/.config/quickshell/ii/modules/common/Appearance.qml
@@ -146,6 +146,14 @@ Singleton {
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7)
property color colOutlineVariant: m3colors.m3outlineVariant
+ property color colError: m3colors.m3error
+ property color colErrorHover: ColorUtils.mix(m3colors.m3error, colLayer1Hover, 0.85)
+ property color colErrorActive: ColorUtils.mix(m3colors.m3error, colLayer1Active, 0.7)
+ property color colOnError: m3colors.m3onError
+ property color colErrorContainer: m3colors.m3errorContainer
+ property color colErrorContainerHover: ColorUtils.mix(m3colors.m3errorContainer, m3colors.m3onErrorContainer, 0.90)
+ property color colErrorContainerActive: ColorUtils.mix(m3colors.m3errorContainer, m3colors.m3onErrorContainer, 0.70)
+ property color colOnErrorContainer: m3colors.m3onErrorContainer
}
rounding: QtObject {
diff --git a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml
index 7487203ae..750e9a318 100644
--- a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml
+++ b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml
@@ -29,6 +29,7 @@ Button {
property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2"
property color colRippleToggled: Appearance?.colors.colPrimaryActive ?? "#D6CEE2"
+ opacity: root.enabled ? 1 : 0.4
property color buttonColor: root.enabled ? (root.toggled ?
(root.hovered ? colBackgroundToggledHover :
colBackgroundToggled) :
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
new file mode 100644
index 000000000..57bb4a9dc
--- /dev/null
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
@@ -0,0 +1,176 @@
+import qs
+import qs.services
+import qs.modules.common
+import qs.modules.common.widgets
+import Qt5Compat.GraphicalEffects
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+
+Item {
+ ColumnLayout {
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: 20
+
+ RowLayout {
+ spacing: 40
+ // The Pomodoro timer circle
+ CircularProgress {
+ Layout.alignment: Qt.AlignHCenter
+ lineWidth: 7
+ gapAngle: Math.PI / 14
+ value: {
+ let pomodoroTotalTime = Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime
+ return Pomodoro.getPomodoroSecondsLeft / pomodoroTotalTime
+ }
+ size: 125
+ primaryColor: Appearance.m3colors.m3onSecondaryContainer
+ secondaryColor: Appearance.colors.colSecondaryContainer
+ enableAnimation: true
+
+ ColumnLayout {
+ anchors.centerIn: parent
+ spacing: 0
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: {
+ let minutes = Math.floor(Pomodoro.getPomodoroSecondsLeft / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(Pomodoro.getPomodoroSecondsLeft % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}`
+ }
+ font.pixelSize: Appearance.font.pixelSize.hugeass + 4
+ color: Appearance.m3colors.m3onSurface
+ }
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
+ font.pixelSize: Appearance.font.pixelSize.normal
+ color: Appearance.m3colors.m3onSurface
+ }
+ }
+ }
+
+ // The Start/Stop and Reset buttons
+ ColumnLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 10
+
+ RippleButton {
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : (Pomodoro.getPomodoroSecondsLeft === Pomodoro.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
+ color: Pomodoro.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ }
+ implicitHeight: 35
+ implicitWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.togglePomodoro()
+ colBackground: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackgroundHover: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ }
+
+ RippleButton {
+ implicitHeight: 35
+ implicitWidth: 90
+
+ onClicked: Pomodoro.pomodoroReset()
+ enabled: (Pomodoro.getPomodoroSecondsLeft < Pomodoro.focusTime)
+
+ font.pixelSize: Appearance.font.pixelSize.larger
+ colBackground: Appearance.colors.colErrorContainer
+ colBackgroundHover: Appearance.colors.colErrorContainerHover
+ colRipple: Appearance.colors.colErrorContainerActive
+
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Translation.tr("Reset")
+ color: Appearance.colors.colOnErrorContainer
+ }
+ }
+ }
+ }
+
+ // The SpinBoxes for adjusting duration
+ GridLayout {
+ Layout.alignment: Qt.AlignHCenter
+ columns: 2
+ uniformCellWidths: true
+ columnSpacing: 20
+ rowSpacing: 4
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Translation.tr("Focus")
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Translation.tr("Break")
+ }
+
+ ConfigSpinBox {
+ id: focusSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.focus / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.focus = value * 60
+ if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
+ Pomodoro.getPomodoroSecondsLeft = Pomodoro.focusTime
+ Pomodoro.timeLeft = Pomodoro.focusTime
+ }
+ }
+ }
+
+ ConfigSpinBox {
+ id: breakSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.breakTime / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.breakTime = value * 60
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: 6
+ Layout.alignment: Qt.AlignHCenter
+ text: Translation.tr("Cycle")
+ }
+ StyledText {
+ Layout.topMargin: 6
+ Layout.alignment: Qt.AlignHCenter
+ text: Translation.tr("Long break")
+ }
+
+ ConfigSpinBox {
+ id: cycleSpinBox
+ spacing: 0
+ from: 1
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.cycle
+ onValueChanged: {
+ Config.options.time.pomodoro.cycle = value
+ }
+ }
+
+ ConfigSpinBox {
+ id: longBreakSpinBox
+ spacing: 0
+ Layout.leftMargin: 0
+ Layout.rightMargin: 0
+ value: Config.options.time.pomodoro.longBreak / 60
+ onValueChanged: {
+ Config.options.time.pomodoro.longBreak = value * 60
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index 8e0d65f74..594533f72 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -2,17 +2,15 @@ import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
-import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import Quickshell
Item {
id: root
property int currentTab: 0
property var tabButtonList: [
- {"name": Translation.tr("Pomodoro"), "icon": "timer_play"},
+ {"name": Translation.tr("Pomodoro"), "icon": "search_activity"},
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
]
@@ -157,326 +155,9 @@ Item {
currentTab = currentIndex
}
- // Pomodoro Timer Tab
- Item {
- ColumnLayout {
- anchors.horizontalCenter: parent.horizontalCenter
- spacing: 20
-
- RowLayout {
- spacing: 40
- // The Pomodoro timer circle
- CircularProgress {
- Layout.alignment: Qt.AlignHCenter
- lineWidth: 7
- gapAngle: Math.PI / 14
- value: {
- let pomodoroTotalTime = Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime
- return Pomodoro.getPomodoroSecondsLeft / pomodoroTotalTime
- }
- size: 125
- primaryColor: Appearance.m3colors.m3onSecondaryContainer
- secondaryColor: Appearance.colors.colSecondaryContainer
- enableAnimation: true
-
- ColumnLayout {
- anchors.centerIn: parent
- spacing: 0
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: {
- let minutes = Math.floor(Pomodoro.getPomodoroSecondsLeft / 60).toString().padStart(2, '0')
- let seconds = Math.floor(Pomodoro.getPomodoroSecondsLeft % 60).toString().padStart(2, '0')
- return `${minutes}:${seconds}`
- }
- font.pixelSize: Appearance.font.pixelSize.hugeass + 4
- color: Appearance.m3colors.m3onSurface
- }
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
- font.pixelSize: Appearance.font.pixelSize.normal
- color: Appearance.m3colors.m3onSurface
- }
- }
- }
-
- // The Start/Stop and Reset buttons
- ColumnLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 10
-
- RippleButton {
- contentItem: StyledText {
- anchors.centerIn: parent
- horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : Translation.tr("Start")
- color: Appearance.colors.colSecondary
- }
- Layout.preferredHeight: 35
- Layout.preferredWidth: 90
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.togglePomodoro()
- colBackground: Appearance.colors.colSecondaryContainer
- colBackgroundHover: Appearance.colors.colSecondaryContainer
- }
-
- RippleButton {
- contentItem: StyledText {
- anchors.centerIn: parent
- horizontalAlignment: Text.AlignHCenter
- text: Translation.tr("Reset")
- color: Appearance.colors.colSecondary
- }
- Layout.preferredHeight: 35
- Layout.preferredWidth: 90
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.pomodoroReset()
- colBackground: Appearance.m3colors.m3onError
- colBackgroundHover: Appearance.m3colors.m3onError
- }
- }
- }
-
- // The SpinBoxes for adjusting duration
- GridLayout {
- Layout.alignment: Qt.AlignHCenter
- columns: 2
- uniformCellWidths: true
- columnSpacing: 20
- rowSpacing: 6
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Focus")
- }
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Break")
- }
-
- ConfigSpinBox {
- id: focusSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.focus / 60
- onValueChanged: {
- Config.options.time.pomodoro.focus = value * 60
- if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
- Pomodoro.getPomodoroSecondsLeft = Pomodoro.focusTime
- Pomodoro.timeLeft = Pomodoro.focusTime
- }
- }
- }
-
- ConfigSpinBox {
- id: breakSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.breakTime / 60
- onValueChanged: {
- Config.options.time.pomodoro.breakTime = value * 60
- }
- }
-
- StyledText {
- Layout.topMargin: 6
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Cycle")
- }
- StyledText {
- Layout.topMargin: 6
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Long break")
- }
-
- ConfigSpinBox {
- id: cycleSpinBox
- spacing: 0
- from: 1
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.cycle
- onValueChanged: {
- Config.options.time.pomodoro.cycle = value
- }
- }
-
- ConfigSpinBox {
- id: longBreakSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.longBreak / 60
- onValueChanged: {
- Config.options.time.pomodoro.longBreak = value * 60
- }
- }
- }
- }
- }
-
- // Stopwatch Tab
- Item {
- id: stopwatchTab
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- ColumnLayout {
- anchors {
- fill: parent
- leftMargin: 20
- rightMargin: 20
- }
- spacing: 20
-
- ColumnLayout {
- spacing: 8
- Layout.alignment: Qt.AlignHCenter
- Layout.fillWidth: false
-
- RowLayout { // Elapsed
- id: elapsedIndicator
- Layout.alignment: Qt.AlignHCenter
- spacing: 0
- StyledText {
- Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
- font.pixelSize: 40
- color: Appearance.m3colors.m3onSurface
- text: {
- let totalSeconds = Math.floor(Pomodoro.stopwatchTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `${minutes}:${seconds}`
- }
- }
- StyledText {
- Layout.fillWidth: true
- font.pixelSize: 40
- color: Appearance.colors.colSubtext
- text: {
- return `:${(Math.floor(Pomodoro.stopwatchTime) % 100).toString().padStart(2, '0')}`
- }
- }
- }
-
- // The Start/Stop and Reset buttons
- RowLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 4
-
- RippleButton {
- contentItem: StyledText {
- anchors.centerIn: parent
- horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isStopwatchRunning ? Translation.tr("Pause") : Pomodoro.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
- color: Appearance.colors.colSecondary
- }
- Layout.preferredHeight: 35
- Layout.preferredWidth: 90
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.toggleStopwatch()
- colBackground: Appearance.colors.colSecondaryContainer
- colBackgroundHover: Appearance.colors.colSecondaryContainer
- }
-
- RippleButton {
- contentItem: StyledText {
- anchors.centerIn: parent
- horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isStopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
- color: Appearance.colors.colSecondary
- }
- Layout.preferredHeight: 35
- Layout.preferredWidth: 90
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.stopwatchResetOrLaps()
- colBackground: Appearance.m3colors.m3onError
- colBackgroundHover: Appearance.m3colors.m3onError
- }
- }
- }
-
- // Laps
- StyledListView {
- id: lapsList
- Layout.fillWidth: true
- Layout.fillHeight: true
- spacing: lapsListItemSpacing
- clip: true
- popin: true
-
- model: ScriptModel {
- values: Pomodoro.stopwatchLaps
- }
-
- delegate: Rectangle {
- id: lapItem
- required property int index
- required property var modelData
- property var horizontalPadding: 10
- property var verticalPadding: 6
- width: lapsList.width
- implicitHeight: lapRow.implicitHeight + verticalPadding * 2
- implicitWidth: lapRow.implicitWidth + horizontalPadding * 2
- color: Appearance.colors.colLayer2
- radius: Appearance.rounding.small
-
- RowLayout {
- id: lapRow
- anchors {
- fill: parent
- leftMargin: lapItem.horizontalPadding
- rightMargin: lapItem.horizontalPadding
- topMargin: lapItem.verticalPadding
- bottomMargin: lapItem.verticalPadding
- }
-
- StyledText {
- font.pixelSize: Appearance.font.pixelSize.small
- color: Appearance.colors.colSubtext
- text: `${Pomodoro.stopwatchLaps.length - lapItem.index}.`
- }
-
- StyledText {
- font.pixelSize: Appearance.font.pixelSize.small
- text: {
- let lapTime = lapItem.modelData
- let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
- let totalSeconds = Math.floor(lapTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `${minutes}:${seconds}.${_10ms}`
- }
- }
-
- Item { Layout.fillWidth: true }
-
- StyledText {
- font.pixelSize: Appearance.font.pixelSize.smaller
- color: Appearance.colors.colPrimary
- text: {
- if (lapItem.index != Pomodoro.stopwatchLaps.length - 1) { // except first lap
- let lapTime = lapItem.modelData - Pomodoro.stopwatchLaps[lapItem.index + 1]
- let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
- let totalSeconds = Math.floor(lapTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `+${minutes == "00" ? "" : minutes + ":"}${seconds}.${_10ms}`
- } else {
- return `` // Nothing for first lap
- }
- }
- }
- }
- }
- }
- }
- }
+ // Tabs
+ PomodoroTimer {}
+ Stopwatch {}
}
}
}
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
new file mode 100644
index 000000000..90ea1ef78
--- /dev/null
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
@@ -0,0 +1,170 @@
+import qs
+import qs.services
+import qs.modules.common
+import qs.modules.common.widgets
+import Qt5Compat.GraphicalEffects
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+
+Item {
+ id: stopwatchTab
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ ColumnLayout {
+ anchors {
+ fill: parent
+ leftMargin: 20
+ rightMargin: 20
+ }
+ spacing: 20
+
+ ColumnLayout {
+ spacing: 8
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: false
+
+ RowLayout { // Elapsed
+ id: elapsedIndicator
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 0
+ StyledText {
+ Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
+ font.pixelSize: 40
+ color: Appearance.m3colors.m3onSurface
+ text: {
+ let totalSeconds = Math.floor(Pomodoro.stopwatchTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}`
+ }
+ }
+ StyledText {
+ Layout.fillWidth: true
+ font.pixelSize: 40
+ color: Appearance.colors.colSubtext
+ text: {
+ return `:${(Math.floor(Pomodoro.stopwatchTime) % 100).toString().padStart(2, '0')}`
+ }
+ }
+ }
+
+ // The Start/Stop and Reset buttons
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 4
+
+ RippleButton {
+ Layout.preferredHeight: 35
+ Layout.preferredWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+
+ onClicked: Pomodoro.toggleStopwatch()
+
+ colBackground: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackgroundHover: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colPrimaryHover
+ colRipple: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colPrimaryActive
+
+ contentItem: StyledText {
+ horizontalAlignment: Text.AlignHCenter
+ color: Pomodoro.isStopwatchRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ text: Pomodoro.isStopwatchRunning ? Translation.tr("Pause") : Pomodoro.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
+ }
+ }
+
+ RippleButton {
+ implicitHeight: 35
+ implicitWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+
+ onClicked: Pomodoro.stopwatchResetOrLaps()
+ enabled: Pomodoro.stopwatchTime !== 0
+
+ colBackground: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
+ colBackgroundHover: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover
+ colRipple: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2Active : Appearance.colors.colErrorContainerActive
+
+ contentItem: StyledText {
+ horizontalAlignment: Text.AlignHCenter
+ text: Pomodoro.isStopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
+ color: Pomodoro.isStopwatchRunning ? Appearance.colors.colOnLayer2 : Appearance.colors.colOnErrorContainer
+ }
+ }
+ }
+ }
+
+ // Laps
+ StyledListView {
+ id: lapsList
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ spacing: lapsListItemSpacing
+ clip: true
+ popin: true
+
+ model: ScriptModel {
+ values: Pomodoro.stopwatchLaps
+ }
+
+ delegate: Rectangle {
+ id: lapItem
+ required property int index
+ required property var modelData
+ property var horizontalPadding: 10
+ property var verticalPadding: 6
+ width: lapsList.width
+ implicitHeight: lapRow.implicitHeight + verticalPadding * 2
+ implicitWidth: lapRow.implicitWidth + horizontalPadding * 2
+ color: Appearance.colors.colLayer2
+ radius: Appearance.rounding.small
+
+ RowLayout {
+ id: lapRow
+ anchors {
+ fill: parent
+ leftMargin: lapItem.horizontalPadding
+ rightMargin: lapItem.horizontalPadding
+ topMargin: lapItem.verticalPadding
+ bottomMargin: lapItem.verticalPadding
+ }
+
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.small
+ color: Appearance.colors.colSubtext
+ text: `${Pomodoro.stopwatchLaps.length - lapItem.index}.`
+ }
+
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.small
+ text: {
+ let lapTime = lapItem.modelData
+ let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
+ let totalSeconds = Math.floor(lapTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}.${_10ms}`
+ }
+ }
+
+ Item { Layout.fillWidth: true }
+
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.smaller
+ color: Appearance.colors.colPrimary
+ text: {
+ let lastTime = (lapItem.index != Pomodoro.stopwatchLaps.length - 1) ? Pomodoro.stopwatchLaps[lapItem.index + 1] : 0
+ let lapTime = lapItem.modelData - lastTime
+ let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
+ let totalSeconds = Math.floor(lapTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `+${minutes == "00" ? "" : minutes + ":"}${seconds}.${_10ms}`
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
From 903a9750339d3b7afcc022c9894b490cf2158f97 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 16:31:03 +0700
Subject: [PATCH 09/23] rename getPomodoroSecondsLeft -> pomodoroSecondsLeft
---
.../modules/sidebarRight/pomodoro/PomodoroTimer.qml | 12 ++++++------
.config/quickshell/ii/services/Pomodoro.qml | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
index 57bb4a9dc..60a0cefbd 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
@@ -22,7 +22,7 @@ Item {
gapAngle: Math.PI / 14
value: {
let pomodoroTotalTime = Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime
- return Pomodoro.getPomodoroSecondsLeft / pomodoroTotalTime
+ return Pomodoro.pomodoroSecondsLeft / pomodoroTotalTime
}
size: 125
primaryColor: Appearance.m3colors.m3onSecondaryContainer
@@ -36,8 +36,8 @@ Item {
StyledText {
Layout.alignment: Qt.AlignHCenter
text: {
- let minutes = Math.floor(Pomodoro.getPomodoroSecondsLeft / 60).toString().padStart(2, '0')
- let seconds = Math.floor(Pomodoro.getPomodoroSecondsLeft % 60).toString().padStart(2, '0')
+ let minutes = Math.floor(Pomodoro.pomodoroSecondsLeft / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(Pomodoro.pomodoroSecondsLeft % 60).toString().padStart(2, '0')
return `${minutes}:${seconds}`
}
font.pixelSize: Appearance.font.pixelSize.hugeass + 4
@@ -61,7 +61,7 @@ Item {
contentItem: StyledText {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : (Pomodoro.getPomodoroSecondsLeft === Pomodoro.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
+ text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : (Pomodoro.pomodoroSecondsLeft === Pomodoro.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
color: Pomodoro.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
}
implicitHeight: 35
@@ -77,7 +77,7 @@ Item {
implicitWidth: 90
onClicked: Pomodoro.pomodoroReset()
- enabled: (Pomodoro.getPomodoroSecondsLeft < Pomodoro.focusTime)
+ enabled: (Pomodoro.pomodoroSecondsLeft < Pomodoro.focusTime)
font.pixelSize: Appearance.font.pixelSize.larger
colBackground: Appearance.colors.colErrorContainer
@@ -121,7 +121,7 @@ Item {
onValueChanged: {
Config.options.time.pomodoro.focus = value * 60
if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
- Pomodoro.getPomodoroSecondsLeft = Pomodoro.focusTime
+ Pomodoro.pomodoroSecondsLeft = Pomodoro.focusTime
Pomodoro.timeLeft = Pomodoro.focusTime
}
}
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 073280d65..5a08709b3 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -24,7 +24,7 @@ Singleton {
property bool isBreak: false
property bool isPomodoroReset: !isPomodoroRunning
property int timeLeft: focusTime
- property int getPomodoroSecondsLeft: focusTime
+ property int pomodoroSecondsLeft: focusTime
property int pomodoroStartTime: Persistent.states.timer.pomodoro.start
property int pomodoroCycle: 1
@@ -79,7 +79,7 @@ Singleton {
}
// A nice abstraction for resume logic by updating the TimeStarted
- getPomodoroSecondsLeft = (pomodoroStartTime + timeLeft) - getCurrentTimeInSeconds()
+ pomodoroSecondsLeft = (pomodoroStartTime + timeLeft) - getCurrentTimeInSeconds()
}
function getCurrentTimeInSeconds() { // Pomodoro uses Seconds
From 1f4568d22f2e388c06b2b45756f6e1a60a7a45e3 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 18:20:14 +0700
Subject: [PATCH 10/23] pomodoro: rename more for consistency
---
.../ii/modules/common/Persistent.qml | 5 +
.../sidebarRight/pomodoro/PomodoroTimer.qml | 161 ++++++++++--------
.../sidebarRight/pomodoro/PomodoroWidget.qml | 2 +-
.../sidebarRight/pomodoro/Stopwatch.qml | 29 ++--
.config/quickshell/ii/services/Pomodoro.qml | 44 ++---
5 files changed, 135 insertions(+), 106 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Persistent.qml b/.config/quickshell/ii/modules/common/Persistent.qml
index a650dc0bb..f0cbcafab 100644
--- a/.config/quickshell/ii/modules/common/Persistent.qml
+++ b/.config/quickshell/ii/modules/common/Persistent.qml
@@ -50,6 +50,11 @@ Singleton {
property bool running: false
property int start: 0
}
+ property JsonObject stopwatch: JsonObject {
+ property bool running: false
+ property int start: 0
+ property list laps: []
+ }
}
}
}
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
index 60a0cefbd..1534adb1f 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
@@ -76,7 +76,7 @@ Item {
implicitHeight: 35
implicitWidth: 90
- onClicked: Pomodoro.pomodoroReset()
+ onClicked: Pomodoro.resetPomodoro()
enabled: (Pomodoro.pomodoroSecondsLeft < Pomodoro.focusTime)
font.pixelSize: Appearance.font.pixelSize.larger
@@ -95,82 +95,101 @@ Item {
}
// The SpinBoxes for adjusting duration
- GridLayout {
- Layout.alignment: Qt.AlignHCenter
- columns: 2
- uniformCellWidths: true
- columnSpacing: 20
- rowSpacing: 4
+ // GridLayout {
+ // Layout.alignment: Qt.AlignHCenter
+ // columns: 2
+ // uniformCellWidths: true
+ // columnSpacing: 20
+ // rowSpacing: 4
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Focus")
- }
+ // StyledText {
+ // Layout.alignment: Qt.AlignHCenter
+ // text: Translation.tr("Focus")
+ // }
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Break")
- }
+ // StyledText {
+ // Layout.alignment: Qt.AlignHCenter
+ // text: Translation.tr("Break")
+ // }
- ConfigSpinBox {
- id: focusSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.focus / 60
- onValueChanged: {
- Config.options.time.pomodoro.focus = value * 60
- if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
- Pomodoro.pomodoroSecondsLeft = Pomodoro.focusTime
- Pomodoro.timeLeft = Pomodoro.focusTime
- }
- }
- }
+ // ConfigSpinBox {
+ // id: focusSpinBox
+ // spacing: 0
+ // Layout.leftMargin: 0
+ // Layout.rightMargin: 0
+ // from: 0
+ // to: 120
+ // Connections {
+ // target: Config
+ // function onReadyChanged() {
+ // focusSpinBox.valueChanged()
+ // }
+ // }
+ // value: {
+ // console.log("New focus time: " + (Config.options.time.pomodoro.focus / 60))
+ // return Config.options.time.pomodoro.focus / 60
+ // }
+ // onValueChanged: {
+ // console.log("New focus time is " + value + " minutes, Config is ready:", Config.ready)
+ // if (!Config.ready) return;
+ // console.log("Setting focus time to " + value + " minutes")
+ // Config.options.time.pomodoro.focus = value * 60
+ // if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
+ // Pomodoro.pomodoroSecondsLeft = Pomodoro.focusTime
+ // Pomodoro.timeLeft = Pomodoro.focusTime
+ // }
+ // }
+ // }
- ConfigSpinBox {
- id: breakSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.breakTime / 60
- onValueChanged: {
- Config.options.time.pomodoro.breakTime = value * 60
- }
- }
+ // ConfigSpinBox {
+ // id: breakSpinBox
+ // value: Config.options.time.pomodoro.breakTime / 60
+ // spacing: 0
+ // from: 0
+ // to: 120
+ // Layout.leftMargin: 0
+ // Layout.rightMargin: 0
+ // onValueChanged: {
+ // Config.options.time.pomodoro.breakTime = value * 60
+ // }
+ // }
- StyledText {
- Layout.topMargin: 6
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Cycle")
- }
- StyledText {
- Layout.topMargin: 6
- Layout.alignment: Qt.AlignHCenter
- text: Translation.tr("Long break")
- }
+ // StyledText {
+ // Layout.topMargin: 6
+ // Layout.alignment: Qt.AlignHCenter
+ // text: Translation.tr("Cycle")
+ // }
+ // StyledText {
+ // Layout.topMargin: 6
+ // Layout.alignment: Qt.AlignHCenter
+ // text: Translation.tr("Long break")
+ // }
- ConfigSpinBox {
- id: cycleSpinBox
- spacing: 0
- from: 1
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.cycle
- onValueChanged: {
- Config.options.time.pomodoro.cycle = value
- }
- }
+ // ConfigSpinBox {
+ // id: cycleSpinBox
+ // value: Config.options.time.pomodoro.cycle
+ // spacing: 0
+ // from: 1
+ // to: 20
+ // Layout.leftMargin: 0
+ // Layout.rightMargin: 0
+ // onValueChanged: {
+ // Config.options.time.pomodoro.cycle = value
+ // }
+ // }
- ConfigSpinBox {
- id: longBreakSpinBox
- spacing: 0
- Layout.leftMargin: 0
- Layout.rightMargin: 0
- value: Config.options.time.pomodoro.longBreak / 60
- onValueChanged: {
- Config.options.time.pomodoro.longBreak = value * 60
- }
- }
- }
+ // ConfigSpinBox {
+ // id: longBreakSpinBox
+ // spacing: 0
+ // Layout.leftMargin: 0
+ // Layout.rightMargin: 0
+ // value: Config.options.time.pomodoro.longBreak / 60
+ // from: 0
+ // to: 120
+ // onValueChanged: {
+ // Config.options.time.pomodoro.longBreak = value * 60
+ // }
+ // }
+ // }
}
}
\ No newline at end of file
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index 594533f72..242b1ceb8 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -34,7 +34,7 @@ Item {
} else if (event.key === Qt.Key_R) {
// Reset with R key
if (currentTab === 0) {
- Pomodoro.pomodoroReset()
+ Pomodoro.resetPomodoro()
} else {
Pomodoro.stopwatchReset()
}
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
index 90ea1ef78..8f5d65295 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
@@ -31,7 +31,7 @@ Item {
Layout.alignment: Qt.AlignHCenter
spacing: 0
StyledText {
- Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
+ // Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
font.pixelSize: 40
color: Appearance.m3colors.m3onSurface
text: {
@@ -100,12 +100,12 @@ Item {
id: lapsList
Layout.fillWidth: true
Layout.fillHeight: true
- spacing: lapsListItemSpacing
+ spacing: 4
clip: true
popin: true
model: ScriptModel {
- values: Pomodoro.stopwatchLaps
+ values: Pomodoro.stopwatchLaps.map((v, i, arr) => arr[arr.length - 1 - i])
}
delegate: Rectangle {
@@ -139,11 +139,11 @@ Item {
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: {
- let lapTime = lapItem.modelData
- let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
- let totalSeconds = Math.floor(lapTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ const lapTime = lapItem.modelData
+ const _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
+ const totalSeconds = Math.floor(lapTime) / 100
+ const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
return `${minutes}:${seconds}.${_10ms}`
}
}
@@ -154,12 +154,13 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colPrimary
text: {
- let lastTime = (lapItem.index != Pomodoro.stopwatchLaps.length - 1) ? Pomodoro.stopwatchLaps[lapItem.index + 1] : 0
- let lapTime = lapItem.modelData - lastTime
- let _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
- let totalSeconds = Math.floor(lapTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ const originalIndex = Pomodoro.stopwatchLaps.length - lapItem.index - 1
+ const lastTime = originalIndex > 0 ? Pomodoro.stopwatchLaps[originalIndex - 1] : 0
+ const lapTime = lapItem.modelData - lastTime
+ const _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
+ const totalSeconds = Math.floor(lapTime) / 100
+ const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
return `+${minutes == "00" ? "" : minutes + ":"}${seconds}.${_10ms}`
}
}
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 5a08709b3..87fe046be 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -25,13 +25,17 @@ Singleton {
property bool isPomodoroReset: !isPomodoroRunning
property int timeLeft: focusTime
property int pomodoroSecondsLeft: focusTime
- property int pomodoroStartTime: Persistent.states.timer.pomodoro.start
+ property int pomodoroStart: Persistent.states.timer.pomodoro.start
property int pomodoroCycle: 1
- property bool isStopwatchRunning: false
+ property bool isStopwatchRunning: Persistent.states.timer.stopwatch.running
property int stopwatchTime: 0
- property int stopwatchStart: 0
- property var stopwatchLaps: []
+ property int stopwatchStart: Persistent.states.timer.stopwatch.start
+ property var stopwatchLaps: Persistent.states.timer.stopwatch.laps
+
+ Component.onCompleted: {
+ if (!isStopwatchRunning) stopwatchReset()
+ }
// Start and Stop button
function togglePomodoro() {
@@ -40,12 +44,12 @@ Singleton {
if (isPomodoroRunning) { // Pressed Start button
Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
} else { // Pressed Stop button
- timeLeft -= (getCurrentTimeInSeconds() - pomodoroStartTime)
+ timeLeft -= (getCurrentTimeInSeconds() - pomodoroStart)
}
}
// Reset button
- function pomodoroReset() {
+ function resetPomodoro() {
Persistent.states.timer.pomodoro.running = false
isBreak = false
isPomodoroReset = true
@@ -56,7 +60,7 @@ Singleton {
}
function refreshPomodoro() {
- if (getCurrentTimeInSeconds() >= pomodoroStartTime + timeLeft) {
+ if (getCurrentTimeInSeconds() >= pomodoroStart + timeLeft) {
isBreak = !isBreak
Persistent.states.timer.pomodoro.start += timeLeft
timeLeft = isBreak ? breakTime : focusTime
@@ -74,12 +78,12 @@ Singleton {
Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"])
if (alertSound) { // Play sound only if alertSound is explicitly specified
- Quickshell.execDetached(["bash", "-c", `ffplay -nodisp -autoexit ${alertSound}`])
+ Quickshell.execDetached(["ffplay", "-nodisp", "-autoexit", alertSound])
}
}
// A nice abstraction for resume logic by updating the TimeStarted
- pomodoroSecondsLeft = (pomodoroStartTime + timeLeft) - getCurrentTimeInSeconds()
+ pomodoroSecondsLeft = (pomodoroStart + timeLeft) - getCurrentTimeInSeconds()
}
function getCurrentTimeInSeconds() { // Pomodoro uses Seconds
@@ -96,31 +100,31 @@ Singleton {
// Stopwatch functions
function toggleStopwatch() {
- isStopwatchRunning = !isStopwatchRunning
+ Persistent.states.timer.stopwatch.running = !isStopwatchRunning
if (isStopwatchRunning) {
// Resume from paused time by adjusting start time
- stopwatchStart = getCurrentTimeIn10ms() - stopwatchTime
+ Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms() - stopwatchTime
}
}
function stopwatchResetOrLaps() {
- if (isStopwatchRunning) { // Clicked on Lap
+ if (isStopwatchRunning) {
recordLaps()
- } else { // Clicked on Reset
+ } else {
stopwatchReset()
}
}
function stopwatchReset() {
- isStopwatchRunning = false
- stopwatchTime = 0
- stopwatchStart = 0
- stopwatchLaps = []
+ Persistent.states.timer.stopwatch.running = false
+ stopwatchTime = 0
+ stopwatchStart = getCurrentTimeIn10ms()
+ Persistent.states.timer.stopwatch.laps = []
}
function recordLaps() {
- stopwatchLaps.unshift(stopwatchTime) // Last lap goes first on list
- // Reassign to trigger onListChanged, idk copied from Todo.qml
- root.stopwatchLaps = stopwatchLaps.slice(0)
+ Persistent.states.timer.stopwatch.laps.push(stopwatchTime)
+ // Reassign to trigger change
+ // Persistent.states.timer.stopwatch.laps = Persistent.states.timer.stopwatch.laps.slice(0)
}
}
From 5bf80dae4ed5844f54d8ecfc175e18c7e9c323c8 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 19:45:26 +0700
Subject: [PATCH 11/23] pomodoro: move timers to service, specific button logic
to widget
---
.../ii/modules/common/Persistent.qml | 1 +
.../sidebarRight/pomodoro/PomodoroWidget.qml | 17 ----
.../sidebarRight/pomodoro/Stopwatch.qml | 11 ++-
.config/quickshell/ii/services/Pomodoro.qml | 99 +++++++++++--------
4 files changed, 67 insertions(+), 61 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Persistent.qml b/.config/quickshell/ii/modules/common/Persistent.qml
index f0cbcafab..876b2ef6a 100644
--- a/.config/quickshell/ii/modules/common/Persistent.qml
+++ b/.config/quickshell/ii/modules/common/Persistent.qml
@@ -49,6 +49,7 @@ Singleton {
property JsonObject pomodoro: JsonObject {
property bool running: false
property int start: 0
+ property bool isBreak: false
}
property JsonObject stopwatch: JsonObject {
property bool running: false
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index 242b1ceb8..64b16a9df 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -45,23 +45,6 @@ Item {
}
}
- Timer {
- id: pomodoroTimer
- interval: 200
- running: Pomodoro.isPomodoroRunning
- repeat: true
- onTriggered: Pomodoro.refreshPomodoro()
- }
-
- Timer {
- id: stopwatchTimer
- interval: 10
- running: Pomodoro.isStopwatchRunning
- repeat: true
- onTriggered: Pomodoro.refreshStopwatch()
- }
-
-
ColumnLayout {
anchors.fill: parent
spacing: 0
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
index 8f5d65295..f1c428ddf 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
@@ -61,7 +61,9 @@ Item {
Layout.preferredWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.toggleStopwatch()
+ onClicked: {
+ Pomodoro.toggleStopwatch()
+ }
colBackground: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
colBackgroundHover: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colPrimaryHover
@@ -79,7 +81,12 @@ Item {
implicitWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.stopwatchResetOrLaps()
+ onClicked: {
+ if (Pomodoro.isStopwatchRunning)
+ Pomodoro.stopwatchRecordLap()
+ else
+ Pomodoro.stopwatchReset()
+ }
enabled: Pomodoro.stopwatchTime !== 0
colBackground: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 87fe046be..dd60a9f4a 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -21,7 +21,7 @@ Singleton {
property string alertSound: Config.options.time.pomodoro.alertSound
property bool isPomodoroRunning: Persistent.states.timer.pomodoro.running
- property bool isBreak: false
+ property bool isBreak: Persistent.states.timer.pomodoro.isBreak
property bool isPomodoroReset: !isPomodoroRunning
property int timeLeft: focusTime
property int pomodoroSecondsLeft: focusTime
@@ -33,35 +33,24 @@ Singleton {
property int stopwatchStart: Persistent.states.timer.stopwatch.start
property var stopwatchLaps: Persistent.states.timer.stopwatch.laps
+ // General
Component.onCompleted: {
if (!isStopwatchRunning) stopwatchReset()
}
- // Start and Stop button
- function togglePomodoro() {
- isPomodoroReset = false
- Persistent.states.timer.pomodoro.running = !isPomodoroRunning
- if (isPomodoroRunning) { // Pressed Start button
- Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
- } else { // Pressed Stop button
- timeLeft -= (getCurrentTimeInSeconds() - pomodoroStart)
- }
+ function getCurrentTimeInSeconds() { // Pomodoro uses Seconds
+ return Math.floor(Date.now() / 1000)
}
- // Reset button
- function resetPomodoro() {
- Persistent.states.timer.pomodoro.running = false
- isBreak = false
- isPomodoroReset = true
- timeLeft = focusTime
- Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
- pomodoroCycle = 1
- refreshPomodoro()
+ function getCurrentTimeIn10ms() { // Stopwatch uses 10ms
+ return Math.floor(Date.now() / 10)
}
+ // Pomodoro
function refreshPomodoro() {
+ // Work <-> break ?
if (getCurrentTimeInSeconds() >= pomodoroStart + timeLeft) {
- isBreak = !isBreak
+ Persistent.states.timer.pomodoro.isBreak = !isBreak
Persistent.states.timer.pomodoro.start += timeLeft
timeLeft = isBreak ? breakTime : focusTime
@@ -86,45 +75,71 @@ Singleton {
pomodoroSecondsLeft = (pomodoroStart + timeLeft) - getCurrentTimeInSeconds()
}
- function getCurrentTimeInSeconds() { // Pomodoro uses Seconds
- return Math.floor(Date.now() / 1000)
+ Timer {
+ id: pomodoroTimer
+ interval: 200
+ running: root.isPomodoroRunning
+ repeat: true
+ onTriggered: Pomodoro.refreshPomodoro()
}
- function getCurrentTimeIn10ms() { // Stopwatch uses 10ms
- return Math.floor(Date.now() / 10)
+ function togglePomodoro() {
+ isPomodoroReset = false
+ Persistent.states.timer.pomodoro.running = !isPomodoroRunning
+ if (isPomodoroRunning) { // Pressed Start button
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
+ } else { // Pressed Stop button
+ timeLeft -= (getCurrentTimeInSeconds() - pomodoroStart)
+ }
}
- function refreshStopwatch() { // stopwatch stores time in 10ms
+ function resetPomodoro() {
+ Persistent.states.timer.pomodoro.running = false
+ Persistent.states.timer.pomodoro.isBreak = false
+ isPomodoroReset = true
+ timeLeft = focusTime
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
+ pomodoroCycle = 1
+ refreshPomodoro()
+ }
+
+ // Stopwatch
+ function refreshStopwatch() { // Stopwatch stores time in 10ms
stopwatchTime = getCurrentTimeIn10ms() - stopwatchStart
}
- // Stopwatch functions
- function toggleStopwatch() {
- Persistent.states.timer.stopwatch.running = !isStopwatchRunning
- if (isStopwatchRunning) {
- // Resume from paused time by adjusting start time
- Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms() - stopwatchTime
- }
+ Timer {
+ id: stopwatchTimer
+ interval: 10
+ running: root.isStopwatchRunning
+ repeat: true
+ onTriggered: root.refreshStopwatch()
}
- function stopwatchResetOrLaps() {
- if (isStopwatchRunning) {
- recordLaps()
- } else {
- stopwatchReset()
- }
+ function toggleStopwatch() {
+ if (root.isStopwatchRunning)
+ root.stopwatchPause()
+ else
+ root.stopwatchResume()
+ }
+
+ function stopwatchPause() {
+ Persistent.states.timer.stopwatch.running = false
+ }
+
+ function stopwatchResume() {
+ Persistent.states.timer.stopwatch.running = true
+ Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms() - stopwatchTime
}
function stopwatchReset() {
Persistent.states.timer.stopwatch.running = false
stopwatchTime = 0
- stopwatchStart = getCurrentTimeIn10ms()
+ Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms()
Persistent.states.timer.stopwatch.laps = []
}
- function recordLaps() {
+ function stopwatchRecordLap() {
Persistent.states.timer.stopwatch.laps.push(stopwatchTime)
- // Reassign to trigger change
- // Persistent.states.timer.stopwatch.laps = Persistent.states.timer.stopwatch.laps.slice(0)
}
}
From 4ca15a1fc361c9f1b10ed6b22431f16ab0f66a4d Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 21:36:28 +0700
Subject: [PATCH 12/23] pomodoro: make pause/resume less weird
---
.../quickshell/ii/modules/common/Config.qml | 4 +-
.../ii/modules/common/Persistent.qml | 1 +
.../sidebarRight/pomodoro/PomodoroTimer.qml | 237 ++++++------------
.config/quickshell/ii/services/Pomodoro.qml | 13 +-
4 files changed, 80 insertions(+), 175 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml
index 9580c3bc0..32cc214dd 100644
--- a/.config/quickshell/ii/modules/common/Config.qml
+++ b/.config/quickshell/ii/modules/common/Config.qml
@@ -256,9 +256,9 @@ Singleton {
property JsonObject pomodoro: JsonObject {
property string alertSound: ""
property int breakTime: 300
- property int cycle: 4
+ property int cyclesBeforeLongBreak: 4
property int focus: 1500
- property int longBreak: 1200
+ property int longBreak: 900
}
}
diff --git a/.config/quickshell/ii/modules/common/Persistent.qml b/.config/quickshell/ii/modules/common/Persistent.qml
index 876b2ef6a..a2c8f4391 100644
--- a/.config/quickshell/ii/modules/common/Persistent.qml
+++ b/.config/quickshell/ii/modules/common/Persistent.qml
@@ -50,6 +50,7 @@ Singleton {
property bool running: false
property int start: 0
property bool isBreak: false
+ property int cycle: 0
}
property JsonObject stopwatch: JsonObject {
property bool running: false
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
index 1534adb1f..fd971b512 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
@@ -9,187 +9,92 @@ import QtQuick.Layouts
import Quickshell
Item {
+ id: root
+
+ implicitHeight: contentColumn.implicitHeight
+ implicitWidth: contentColumn.implicitWidth
+
ColumnLayout {
- anchors.horizontalCenter: parent.horizontalCenter
- spacing: 20
+ id: contentColumn
+ anchors.fill: parent
+ spacing: 0
- RowLayout {
- spacing: 40
- // The Pomodoro timer circle
- CircularProgress {
- Layout.alignment: Qt.AlignHCenter
- lineWidth: 7
- gapAngle: Math.PI / 14
- value: {
- let pomodoroTotalTime = Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime
- return Pomodoro.pomodoroSecondsLeft / pomodoroTotalTime
- }
- size: 125
- primaryColor: Appearance.m3colors.m3onSecondaryContainer
- secondaryColor: Appearance.colors.colSecondaryContainer
- enableAnimation: true
-
- ColumnLayout {
- anchors.centerIn: parent
- spacing: 0
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: {
- let minutes = Math.floor(Pomodoro.pomodoroSecondsLeft / 60).toString().padStart(2, '0')
- let seconds = Math.floor(Pomodoro.pomodoroSecondsLeft % 60).toString().padStart(2, '0')
- return `${minutes}:${seconds}`
- }
- font.pixelSize: Appearance.font.pixelSize.hugeass + 4
- color: Appearance.m3colors.m3onSurface
- }
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
- font.pixelSize: Appearance.font.pixelSize.normal
- color: Appearance.m3colors.m3onSurface
- }
- }
+ // The Pomodoro timer circle
+ CircularProgress {
+ Layout.alignment: Qt.AlignHCenter
+ lineWidth: 8
+ gapAngle: Math.PI / 14
+ value: {
+ let pomodoroTotalTime = Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime;
+ return Pomodoro.pomodoroSecondsLeft / pomodoroTotalTime;
}
+ size: 200
+ primaryColor: Appearance.m3colors.m3onSecondaryContainer
+ secondaryColor: Appearance.colors.colSecondaryContainer
+ enableAnimation: true
- // The Start/Stop and Reset buttons
ColumnLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 10
+ anchors.centerIn: parent
+ spacing: 0
- RippleButton {
- contentItem: StyledText {
- anchors.centerIn: parent
- horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : (Pomodoro.pomodoroSecondsLeft === Pomodoro.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
- color: Pomodoro.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: {
+ let minutes = Math.floor(Pomodoro.pomodoroSecondsLeft / 60).toString().padStart(2, '0');
+ let seconds = Math.floor(Pomodoro.pomodoroSecondsLeft % 60).toString().padStart(2, '0');
+ return `${minutes}:${seconds}`;
}
- implicitHeight: 35
- implicitWidth: 90
- font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.togglePomodoro()
- colBackground: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
- colBackgroundHover: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ font.pixelSize: 40
+ color: Appearance.m3colors.m3onSurface
}
-
- RippleButton {
- implicitHeight: 35
- implicitWidth: 90
-
- onClicked: Pomodoro.resetPomodoro()
- enabled: (Pomodoro.pomodoroSecondsLeft < Pomodoro.focusTime)
-
- font.pixelSize: Appearance.font.pixelSize.larger
- colBackground: Appearance.colors.colErrorContainer
- colBackgroundHover: Appearance.colors.colErrorContainerHover
- colRipple: Appearance.colors.colErrorContainerActive
-
- contentItem: StyledText {
- anchors.centerIn: parent
- horizontalAlignment: Text.AlignHCenter
- text: Translation.tr("Reset")
- color: Appearance.colors.colOnErrorContainer
- }
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
+ font.pixelSize: Appearance.font.pixelSize.normal
+ color: Appearance.colors.colSubtext
}
}
}
- // The SpinBoxes for adjusting duration
- // GridLayout {
- // Layout.alignment: Qt.AlignHCenter
- // columns: 2
- // uniformCellWidths: true
- // columnSpacing: 20
- // rowSpacing: 4
+ // The Start/Stop and Reset buttons
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 10
- // StyledText {
- // Layout.alignment: Qt.AlignHCenter
- // text: Translation.tr("Focus")
- // }
+ RippleButton {
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : (Pomodoro.pomodoroSecondsLeft === Pomodoro.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
+ color: Pomodoro.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ }
+ implicitHeight: 35
+ implicitWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+ onClicked: Pomodoro.togglePomodoro()
+ colBackground: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackgroundHover: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ }
- // StyledText {
- // Layout.alignment: Qt.AlignHCenter
- // text: Translation.tr("Break")
- // }
+ RippleButton {
+ implicitHeight: 35
+ implicitWidth: 90
- // ConfigSpinBox {
- // id: focusSpinBox
- // spacing: 0
- // Layout.leftMargin: 0
- // Layout.rightMargin: 0
- // from: 0
- // to: 120
- // Connections {
- // target: Config
- // function onReadyChanged() {
- // focusSpinBox.valueChanged()
- // }
- // }
- // value: {
- // console.log("New focus time: " + (Config.options.time.pomodoro.focus / 60))
- // return Config.options.time.pomodoro.focus / 60
- // }
- // onValueChanged: {
- // console.log("New focus time is " + value + " minutes, Config is ready:", Config.ready)
- // if (!Config.ready) return;
- // console.log("Setting focus time to " + value + " minutes")
- // Config.options.time.pomodoro.focus = value * 60
- // if (Pomodoro.isPomodoroReset) { // Special case for Pomodoro in Reset state
- // Pomodoro.pomodoroSecondsLeft = Pomodoro.focusTime
- // Pomodoro.timeLeft = Pomodoro.focusTime
- // }
- // }
- // }
+ onClicked: Pomodoro.resetPomodoro()
+ enabled: (Pomodoro.pomodoroSecondsLeft < (Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime))
- // ConfigSpinBox {
- // id: breakSpinBox
- // value: Config.options.time.pomodoro.breakTime / 60
- // spacing: 0
- // from: 0
- // to: 120
- // Layout.leftMargin: 0
- // Layout.rightMargin: 0
- // onValueChanged: {
- // Config.options.time.pomodoro.breakTime = value * 60
- // }
- // }
+ font.pixelSize: Appearance.font.pixelSize.larger
+ colBackground: Appearance.colors.colErrorContainer
+ colBackgroundHover: Appearance.colors.colErrorContainerHover
+ colRipple: Appearance.colors.colErrorContainerActive
- // StyledText {
- // Layout.topMargin: 6
- // Layout.alignment: Qt.AlignHCenter
- // text: Translation.tr("Cycle")
- // }
- // StyledText {
- // Layout.topMargin: 6
- // Layout.alignment: Qt.AlignHCenter
- // text: Translation.tr("Long break")
- // }
-
- // ConfigSpinBox {
- // id: cycleSpinBox
- // value: Config.options.time.pomodoro.cycle
- // spacing: 0
- // from: 1
- // to: 20
- // Layout.leftMargin: 0
- // Layout.rightMargin: 0
- // onValueChanged: {
- // Config.options.time.pomodoro.cycle = value
- // }
- // }
-
- // ConfigSpinBox {
- // id: longBreakSpinBox
- // spacing: 0
- // Layout.leftMargin: 0
- // Layout.rightMargin: 0
- // value: Config.options.time.pomodoro.longBreak / 60
- // from: 0
- // to: 120
- // onValueChanged: {
- // Config.options.time.pomodoro.longBreak = value * 60
- // }
- // }
- // }
+ contentItem: StyledText {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: Translation.tr("Reset")
+ color: Appearance.colors.colOnErrorContainer
+ }
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index dd60a9f4a..a537fd957 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -17,7 +17,7 @@ Singleton {
property int focusTime: Config.options.time.pomodoro.focus
property int breakTime: Config.options.time.pomodoro.breakTime
property int longBreakTime: Config.options.time.pomodoro.longBreak
- property int longBreakCycle: Config.options.time.pomodoro.cycle
+ property int cyclesBeforeLongBreak: Config.options.time.pomodoro.cyclesBeforeLongBreak
property string alertSound: Config.options.time.pomodoro.alertSound
property bool isPomodoroRunning: Persistent.states.timer.pomodoro.running
@@ -56,7 +56,7 @@ Singleton {
let notificationTitle, notificationMessage
- if (isBreak && pomodoroCycle % longBreakCycle === 0) { // isPomodoroLongBreak
+ if (isBreak && pomodoroCycle % cyclesBeforeLongBreak === 0) { // isPomodoroLongBreak
notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(longBreakTime / 60))
} else if (isBreak) {
notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(breakTime / 60))
@@ -86,10 +86,8 @@ Singleton {
function togglePomodoro() {
isPomodoroReset = false
Persistent.states.timer.pomodoro.running = !isPomodoroRunning
- if (isPomodoroRunning) { // Pressed Start button
- Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
- } else { // Pressed Stop button
- timeLeft -= (getCurrentTimeInSeconds() - pomodoroStart)
+ if (Persistent.states.timer.pomodoro.running) { // Start/Resume
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds() + pomodoroSecondsLeft - (isBreak ? breakTime : focusTime)
}
}
@@ -99,7 +97,8 @@ Singleton {
isPomodoroReset = true
timeLeft = focusTime
Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
- pomodoroCycle = 1
+ pomodoroSecondsLeft = 0
+ Persistent.states.timer.pomodoro.cycle = 1
refreshPomodoro()
}
From 9e1b55a749126c45dbdf07e1228b41ffd0a2f54b Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:06:05 +0700
Subject: [PATCH 13/23] pomodoro: add cycle indicator, make long break work,
fix reset button
---
.../ii/modules/common/Persistent.qml | 28 ++++++++++---
.../sidebarRight/pomodoro/PomodoroTimer.qml | 29 ++++++++++----
.config/quickshell/ii/services/Pomodoro.qml | 40 +++++++++----------
3 files changed, 65 insertions(+), 32 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Persistent.qml b/.config/quickshell/ii/modules/common/Persistent.qml
index a2c8f4391..e639f54f1 100644
--- a/.config/quickshell/ii/modules/common/Persistent.qml
+++ b/.config/quickshell/ii/modules/common/Persistent.qml
@@ -11,18 +11,35 @@ Singleton {
property string fileName: "states.json"
property string filePath: `${root.fileDir}/${root.fileName}`
+ Timer {
+ id: fileReloadTimer
+ interval: 100
+ repeat: false
+ onTriggered: {
+ persistentStatesFileView.reload()
+ }
+ }
+
+ Timer {
+ id: fileWriteTimer
+ interval: 100
+ repeat: false
+ onTriggered: {
+ persistentStatesFileView.writeAdapter()
+ }
+ }
+
FileView {
+ id: persistentStatesFileView
path: root.filePath
watchChanges: true
- onFileChanged: reload()
- onAdapterUpdated: {
- writeAdapter()
- }
+ onFileChanged: fileReloadTimer.restart()
+ onAdapterUpdated: fileWriteTimer.restart()
onLoadFailed: error => {
console.log("Failed to load persistent states file:", error);
if (error == FileViewError.FileNotFound) {
- writeAdapter();
+ fileWriteTimer.restart();
}
}
@@ -50,6 +67,7 @@ Singleton {
property bool running: false
property int start: 0
property bool isBreak: false
+ property bool isLongBreak: false
property int cycle: 0
}
property JsonObject stopwatch: JsonObject {
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
index fd971b512..b086a68ab 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
@@ -23,14 +23,10 @@ Item {
CircularProgress {
Layout.alignment: Qt.AlignHCenter
lineWidth: 8
- gapAngle: Math.PI / 14
value: {
- let pomodoroTotalTime = Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime;
- return Pomodoro.pomodoroSecondsLeft / pomodoroTotalTime;
+ return Pomodoro.pomodoroSecondsLeft / Pomodoro.pomodoroLapDuration;
}
size: 200
- primaryColor: Appearance.m3colors.m3onSecondaryContainer
- secondaryColor: Appearance.colors.colSecondaryContainer
enableAnimation: true
ColumnLayout {
@@ -49,11 +45,30 @@ Item {
}
StyledText {
Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
+ text: Pomodoro.isLongBreak ? Translation.tr("Long break") : Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.colors.colSubtext
}
}
+
+ Rectangle {
+ radius: Appearance.rounding.full
+ color: Appearance.colors.colLayer2
+
+ anchors {
+ right: parent.right
+ bottom: parent.bottom
+ }
+ implicitWidth: 36
+ implicitHeight: implicitWidth
+
+ StyledText {
+ id: cycleText
+ anchors.centerIn: parent
+ color: Appearance.colors.colOnLayer2
+ text: Pomodoro.pomodoroCycle + 1
+ }
+ }
}
// The Start/Stop and Reset buttons
@@ -81,7 +96,7 @@ Item {
implicitWidth: 90
onClicked: Pomodoro.resetPomodoro()
- enabled: (Pomodoro.pomodoroSecondsLeft < (Pomodoro.isBreak ? Pomodoro.breakTime : Pomodoro.focusTime))
+ enabled: (Pomodoro.pomodoroSecondsLeft < Pomodoro.pomodoroLapDuration) || Pomodoro.pomodoroCycle > 0 || Pomodoro.isBreak
font.pixelSize: Appearance.font.pixelSize.larger
colBackground: Appearance.colors.colErrorContainer
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index a537fd957..8bb198076 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -22,11 +22,11 @@ Singleton {
property bool isPomodoroRunning: Persistent.states.timer.pomodoro.running
property bool isBreak: Persistent.states.timer.pomodoro.isBreak
- property bool isPomodoroReset: !isPomodoroRunning
- property int timeLeft: focusTime
+ property bool isLongBreak: Persistent.states.timer.pomodoro.isLongBreak
+ property bool isPomodoroLongBreak: Persistent.states.timer.pomodoro.isLongBreak
+ property int pomodoroLapDuration: isBreak ? (isLongBreak ? longBreakTime : breakTime) : focusTime
property int pomodoroSecondsLeft: focusTime
- property int pomodoroStart: Persistent.states.timer.pomodoro.start
- property int pomodoroCycle: 1
+ property int pomodoroCycle: Persistent.states.timer.pomodoro.cycle
property bool isStopwatchRunning: Persistent.states.timer.stopwatch.running
property int stopwatchTime: 0
@@ -49,30 +49,34 @@ Singleton {
// Pomodoro
function refreshPomodoro() {
// Work <-> break ?
- if (getCurrentTimeInSeconds() >= pomodoroStart + timeLeft) {
- Persistent.states.timer.pomodoro.isBreak = !isBreak
- Persistent.states.timer.pomodoro.start += timeLeft
- timeLeft = isBreak ? breakTime : focusTime
+ if (getCurrentTimeInSeconds() >= Persistent.states.timer.pomodoro.start + pomodoroLapDuration) {
+ // Reset counts
+ const currentTimeInSeconds = getCurrentTimeInSeconds()
+ Persistent.states.timer.pomodoro.isBreak = !Persistent.states.timer.pomodoro.isBreak
+ Persistent.states.timer.pomodoro.isLongBreak = Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak)
+ Persistent.states.timer.pomodoro.start = currentTimeInSeconds
+ // Send notification
let notificationTitle, notificationMessage
-
- if (isBreak && pomodoroCycle % cyclesBeforeLongBreak === 0) { // isPomodoroLongBreak
+ if (Persistent.states.timer.pomodoro.isBreak && pomodoroCycle % cyclesBeforeLongBreak === 0) { // isPomodoroLongBreak
notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(longBreakTime / 60))
- } else if (isBreak) {
+ } else if (Persistent.states.timer.pomodoro.isBreak) {
notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(breakTime / 60))
} else {
notificationMessage = Translation.tr(`Focus for %1 minutes`).arg(Math.floor(focusTime / 60))
- pomodoroCycle += 1
}
Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"])
if (alertSound) { // Play sound only if alertSound is explicitly specified
Quickshell.execDetached(["ffplay", "-nodisp", "-autoexit", alertSound])
}
+
+ if (!isBreak) {
+ Persistent.states.timer.pomodoro.cycle = (Persistent.states.timer.pomodoro.cycle + 1) % root.cyclesBeforeLongBreak;
+ }
}
- // A nice abstraction for resume logic by updating the TimeStarted
- pomodoroSecondsLeft = (pomodoroStart + timeLeft) - getCurrentTimeInSeconds()
+ pomodoroSecondsLeft = pomodoroLapDuration - (getCurrentTimeInSeconds() - Persistent.states.timer.pomodoro.start)
}
Timer {
@@ -84,21 +88,17 @@ Singleton {
}
function togglePomodoro() {
- isPomodoroReset = false
Persistent.states.timer.pomodoro.running = !isPomodoroRunning
if (Persistent.states.timer.pomodoro.running) { // Start/Resume
- Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds() + pomodoroSecondsLeft - (isBreak ? breakTime : focusTime)
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds() + pomodoroSecondsLeft - pomodoroLapDuration
}
}
function resetPomodoro() {
Persistent.states.timer.pomodoro.running = false
Persistent.states.timer.pomodoro.isBreak = false
- isPomodoroReset = true
- timeLeft = focusTime
Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
- pomodoroSecondsLeft = 0
- Persistent.states.timer.pomodoro.cycle = 1
+ Persistent.states.timer.pomodoro.cycle = 0
refreshPomodoro()
}
From 90fafa219a54f5ee2e81d2fbb8d7827cb93235c6 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:09:07 +0700
Subject: [PATCH 14/23] remove unnecessary qualified access
---
.config/quickshell/ii/services/Pomodoro.qml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 8bb198076..8376499d9 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -84,7 +84,7 @@ Singleton {
interval: 200
running: root.isPomodoroRunning
repeat: true
- onTriggered: Pomodoro.refreshPomodoro()
+ onTriggered: refreshPomodoro()
}
function togglePomodoro() {
@@ -112,14 +112,14 @@ Singleton {
interval: 10
running: root.isStopwatchRunning
repeat: true
- onTriggered: root.refreshStopwatch()
+ onTriggered: refreshStopwatch()
}
function toggleStopwatch() {
if (root.isStopwatchRunning)
- root.stopwatchPause()
+ stopwatchPause()
else
- root.stopwatchResume()
+ stopwatchResume()
}
function stopwatchPause() {
From df9a5e398e9ad838a71509e0ca299d9a7e3202cc Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:11:06 +0700
Subject: [PATCH 15/23] pomodoro: remove unused notificationTitle var, format
---
.config/quickshell/ii/services/Pomodoro.qml | 73 +++++++++++----------
1 file changed, 37 insertions(+), 36 deletions(-)
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/Pomodoro.qml
index 8376499d9..297a1c204 100644
--- a/.config/quickshell/ii/services/Pomodoro.qml
+++ b/.config/quickshell/ii/services/Pomodoro.qml
@@ -35,15 +35,16 @@ Singleton {
// General
Component.onCompleted: {
- if (!isStopwatchRunning) stopwatchReset()
+ if (!isStopwatchRunning)
+ stopwatchReset();
}
function getCurrentTimeInSeconds() { // Pomodoro uses Seconds
- return Math.floor(Date.now() / 1000)
+ return Math.floor(Date.now() / 1000);
}
function getCurrentTimeIn10ms() { // Stopwatch uses 10ms
- return Math.floor(Date.now() / 10)
+ return Math.floor(Date.now() / 10);
}
// Pomodoro
@@ -51,32 +52,31 @@ Singleton {
// Work <-> break ?
if (getCurrentTimeInSeconds() >= Persistent.states.timer.pomodoro.start + pomodoroLapDuration) {
// Reset counts
- const currentTimeInSeconds = getCurrentTimeInSeconds()
- Persistent.states.timer.pomodoro.isBreak = !Persistent.states.timer.pomodoro.isBreak
- Persistent.states.timer.pomodoro.isLongBreak = Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak)
- Persistent.states.timer.pomodoro.start = currentTimeInSeconds
+ const currentTimeInSeconds = getCurrentTimeInSeconds();
+ Persistent.states.timer.pomodoro.isBreak = !Persistent.states.timer.pomodoro.isBreak;
+ Persistent.states.timer.pomodoro.isLongBreak = Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak);
+ Persistent.states.timer.pomodoro.start = currentTimeInSeconds;
// Send notification
- let notificationTitle, notificationMessage
- if (Persistent.states.timer.pomodoro.isBreak && pomodoroCycle % cyclesBeforeLongBreak === 0) { // isPomodoroLongBreak
- notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(longBreakTime / 60))
+ let notificationMessage;
+ if (Persistent.states.timer.pomodoro.isLongBreak) {
+ notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(longBreakTime / 60));
} else if (Persistent.states.timer.pomodoro.isBreak) {
- notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(breakTime / 60))
+ notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(breakTime / 60));
} else {
- notificationMessage = Translation.tr(`Focus for %1 minutes`).arg(Math.floor(focusTime / 60))
+ notificationMessage = Translation.tr(`Focus for %1 minutes`).arg(Math.floor(focusTime / 60));
}
- Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"])
- if (alertSound) { // Play sound only if alertSound is explicitly specified
- Quickshell.execDetached(["ffplay", "-nodisp", "-autoexit", alertSound])
- }
+ Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"]);
+ if (alertSound)
+ Quickshell.execDetached(["ffplay", "-nodisp", "-autoexit", alertSound]);
if (!isBreak) {
Persistent.states.timer.pomodoro.cycle = (Persistent.states.timer.pomodoro.cycle + 1) % root.cyclesBeforeLongBreak;
}
}
- pomodoroSecondsLeft = pomodoroLapDuration - (getCurrentTimeInSeconds() - Persistent.states.timer.pomodoro.start)
+ pomodoroSecondsLeft = pomodoroLapDuration - (getCurrentTimeInSeconds() - Persistent.states.timer.pomodoro.start);
}
Timer {
@@ -88,23 +88,24 @@ Singleton {
}
function togglePomodoro() {
- Persistent.states.timer.pomodoro.running = !isPomodoroRunning
- if (Persistent.states.timer.pomodoro.running) { // Start/Resume
- Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds() + pomodoroSecondsLeft - pomodoroLapDuration
+ Persistent.states.timer.pomodoro.running = !isPomodoroRunning;
+ if (Persistent.states.timer.pomodoro.running) {
+ // Start/Resume
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds() + pomodoroSecondsLeft - pomodoroLapDuration;
}
}
function resetPomodoro() {
- Persistent.states.timer.pomodoro.running = false
- Persistent.states.timer.pomodoro.isBreak = false
- Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds()
- Persistent.states.timer.pomodoro.cycle = 0
- refreshPomodoro()
+ Persistent.states.timer.pomodoro.running = false;
+ Persistent.states.timer.pomodoro.isBreak = false;
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds();
+ Persistent.states.timer.pomodoro.cycle = 0;
+ refreshPomodoro();
}
// Stopwatch
function refreshStopwatch() { // Stopwatch stores time in 10ms
- stopwatchTime = getCurrentTimeIn10ms() - stopwatchStart
+ stopwatchTime = getCurrentTimeIn10ms() - stopwatchStart;
}
Timer {
@@ -117,28 +118,28 @@ Singleton {
function toggleStopwatch() {
if (root.isStopwatchRunning)
- stopwatchPause()
+ stopwatchPause();
else
- stopwatchResume()
+ stopwatchResume();
}
function stopwatchPause() {
- Persistent.states.timer.stopwatch.running = false
+ Persistent.states.timer.stopwatch.running = false;
}
function stopwatchResume() {
- Persistent.states.timer.stopwatch.running = true
- Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms() - stopwatchTime
+ Persistent.states.timer.stopwatch.running = true;
+ Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms() - stopwatchTime;
}
function stopwatchReset() {
- Persistent.states.timer.stopwatch.running = false
- stopwatchTime = 0
- Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms()
- Persistent.states.timer.stopwatch.laps = []
+ Persistent.states.timer.stopwatch.running = false;
+ stopwatchTime = 0;
+ Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms();
+ Persistent.states.timer.stopwatch.laps = [];
}
function stopwatchRecordLap() {
- Persistent.states.timer.stopwatch.laps.push(stopwatchTime)
+ Persistent.states.timer.stopwatch.laps.push(stopwatchTime);
}
}
From b102e5c1a5c840dc4dc860b9ea297dcbf4f7e469 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:15:07 +0700
Subject: [PATCH 16/23] pomodoro: remove unnecessary event propagation
prevention
---
.../sidebarRight/pomodoro/PomodoroWidget.qml | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index 64b16a9df..b5ee7a597 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -16,32 +16,26 @@ Item {
// These are keybinds for stopwatch and pomodoro
Keys.onPressed: (event) => {
- if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
+ if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { // Switch tabs
if (event.key === Qt.Key_PageDown) {
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
} else if (event.key === Qt.Key_PageUp) {
currentTab = Math.max(currentTab - 1, 0)
}
- event.accepted = true
- } else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) {
- // Toggle start/stop with Space or S key
+ } else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
if (currentTab === 0) {
Pomodoro.togglePomodoro()
} else {
Pomodoro.toggleStopwatch()
}
- event.accepted = true
- } else if (event.key === Qt.Key_R) {
- // Reset with R key
+ } else if (event.key === Qt.Key_R) { // Reset with R
if (currentTab === 0) {
Pomodoro.resetPomodoro()
} else {
Pomodoro.stopwatchReset()
}
- event.accepted = true
- } else if (event.key === Qt.Key_L) {
- // record Stopwatch lap with L key, regardless of current Tab
- Pomodoro.recordLaps()
+ } else if (event.key === Qt.Key_L) { // Record lap with L
+ Pomodoro.stopwatchRecordLap()
}
}
From fbe17dc3e3119d5ff15dd8418e1f9fb7c9461d15 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:19:45 +0700
Subject: [PATCH 17/23] pomodoro: accept event on keybind cuz recommended by qt
docs
---
.../ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index b5ee7a597..3aaa868a4 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -22,20 +22,24 @@ Item {
} else if (event.key === Qt.Key_PageUp) {
currentTab = Math.max(currentTab - 1, 0)
}
+ event.accepted = true
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
if (currentTab === 0) {
Pomodoro.togglePomodoro()
} else {
Pomodoro.toggleStopwatch()
}
+ event.accepted = true
} else if (event.key === Qt.Key_R) { // Reset with R
if (currentTab === 0) {
Pomodoro.resetPomodoro()
} else {
Pomodoro.stopwatchReset()
}
+ event.accepted = true
} else if (event.key === Qt.Key_L) { // Record lap with L
Pomodoro.stopwatchRecordLap()
+ event.accepted = true
}
}
From bcd1167d3989bbbdbf4af1eeec2c4e8b1cb70f17 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:48:40 +0700
Subject: [PATCH 18/23] pomodoro: rename service to TimerService, better
stopwatch layout
---
.../sidebarRight/pomodoro/PomodoroTimer.qml | 24 +--
.../sidebarRight/pomodoro/PomodoroWidget.qml | 10 +-
.../sidebarRight/pomodoro/Stopwatch.qml | 192 ++++++++++--------
.../{Pomodoro.qml => TimerService.qml} | 0
4 files changed, 128 insertions(+), 98 deletions(-)
rename .config/quickshell/ii/services/{Pomodoro.qml => TimerService.qml} (100%)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
index b086a68ab..e50f92d83 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
@@ -24,7 +24,7 @@ Item {
Layout.alignment: Qt.AlignHCenter
lineWidth: 8
value: {
- return Pomodoro.pomodoroSecondsLeft / Pomodoro.pomodoroLapDuration;
+ return TimerService.pomodoroSecondsLeft / TimerService.pomodoroLapDuration;
}
size: 200
enableAnimation: true
@@ -36,8 +36,8 @@ Item {
StyledText {
Layout.alignment: Qt.AlignHCenter
text: {
- let minutes = Math.floor(Pomodoro.pomodoroSecondsLeft / 60).toString().padStart(2, '0');
- let seconds = Math.floor(Pomodoro.pomodoroSecondsLeft % 60).toString().padStart(2, '0');
+ let minutes = Math.floor(TimerService.pomodoroSecondsLeft / 60).toString().padStart(2, '0');
+ let seconds = Math.floor(TimerService.pomodoroSecondsLeft % 60).toString().padStart(2, '0');
return `${minutes}:${seconds}`;
}
font.pixelSize: 40
@@ -45,7 +45,7 @@ Item {
}
StyledText {
Layout.alignment: Qt.AlignHCenter
- text: Pomodoro.isLongBreak ? Translation.tr("Long break") : Pomodoro.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
+ text: TimerService.isLongBreak ? Translation.tr("Long break") : TimerService.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.colors.colSubtext
}
@@ -66,7 +66,7 @@ Item {
id: cycleText
anchors.centerIn: parent
color: Appearance.colors.colOnLayer2
- text: Pomodoro.pomodoroCycle + 1
+ text: TimerService.pomodoroCycle + 1
}
}
}
@@ -80,23 +80,23 @@ Item {
contentItem: StyledText {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : (Pomodoro.pomodoroSecondsLeft === Pomodoro.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
- color: Pomodoro.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ text: TimerService.isPomodoroRunning ? Translation.tr("Pause") : (TimerService.pomodoroSecondsLeft === TimerService.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
+ color: TimerService.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
}
implicitHeight: 35
implicitWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger
- onClicked: Pomodoro.togglePomodoro()
- colBackground: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
- colBackgroundHover: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ onClicked: TimerService.togglePomodoro()
+ colBackground: TimerService.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackgroundHover: TimerService.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
}
RippleButton {
implicitHeight: 35
implicitWidth: 90
- onClicked: Pomodoro.resetPomodoro()
- enabled: (Pomodoro.pomodoroSecondsLeft < Pomodoro.pomodoroLapDuration) || Pomodoro.pomodoroCycle > 0 || Pomodoro.isBreak
+ onClicked: TimerService.resetPomodoro()
+ enabled: (TimerService.pomodoroSecondsLeft < TimerService.pomodoroLapDuration) || TimerService.pomodoroCycle > 0 || TimerService.isBreak
font.pixelSize: Appearance.font.pixelSize.larger
colBackground: Appearance.colors.colErrorContainer
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
index 3aaa868a4..729d45809 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroWidget.qml
@@ -25,20 +25,20 @@ Item {
event.accepted = true
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
if (currentTab === 0) {
- Pomodoro.togglePomodoro()
+ TimerService.togglePomodoro()
} else {
- Pomodoro.toggleStopwatch()
+ TimerService.toggleStopwatch()
}
event.accepted = true
} else if (event.key === Qt.Key_R) { // Reset with R
if (currentTab === 0) {
- Pomodoro.resetPomodoro()
+ TimerService.resetPomodoro()
} else {
- Pomodoro.stopwatchReset()
+ TimerService.stopwatchReset()
}
event.accepted = true
} else if (event.key === Qt.Key_L) { // Record lap with L
- Pomodoro.stopwatchRecordLap()
+ TimerService.stopwatchRecordLap()
event.accepted = true
}
}
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
index f1c428ddf..cbf588194 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
@@ -13,91 +13,61 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
- ColumnLayout {
+ Item {
anchors {
fill: parent
- leftMargin: 20
- rightMargin: 20
+ topMargin: 8
+ leftMargin: 16
+ rightMargin: 16
}
- spacing: 20
- ColumnLayout {
- spacing: 8
- Layout.alignment: Qt.AlignHCenter
- Layout.fillWidth: false
+ RowLayout { // Elapsed
+ id: elapsedIndicator
+
+ anchors {
+ top: undefined
+ verticalCenter: parent.verticalCenter
+ left: controlButtons.left
+ leftMargin: 6
+ }
- RowLayout { // Elapsed
- id: elapsedIndicator
- Layout.alignment: Qt.AlignHCenter
- spacing: 0
- StyledText {
- // Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
- font.pixelSize: 40
- color: Appearance.m3colors.m3onSurface
- text: {
- let totalSeconds = Math.floor(Pomodoro.stopwatchTime) / 100
- let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
- let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
- return `${minutes}:${seconds}`
- }
- }
- StyledText {
- Layout.fillWidth: true
- font.pixelSize: 40
- color: Appearance.colors.colSubtext
- text: {
- return `:${(Math.floor(Pomodoro.stopwatchTime) % 100).toString().padStart(2, '0')}`
- }
+ states: State {
+ name: "hasLaps"
+ when: TimerService.stopwatchLaps.length > 0
+ AnchorChanges {
+ target: elapsedIndicator
+ anchors.top: parent.top
+ anchors.verticalCenter: undefined
+ anchors.left: controlButtons.left
}
}
- // The Start/Stop and Reset buttons
- RowLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: 4
-
- RippleButton {
- Layout.preferredHeight: 35
- Layout.preferredWidth: 90
- font.pixelSize: Appearance.font.pixelSize.larger
-
- onClicked: {
- Pomodoro.toggleStopwatch()
- }
-
- colBackground: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
- colBackgroundHover: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colPrimaryHover
- colRipple: Pomodoro.isStopwatchRunning ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colPrimaryActive
-
- contentItem: StyledText {
- horizontalAlignment: Text.AlignHCenter
- color: Pomodoro.isStopwatchRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
- text: Pomodoro.isStopwatchRunning ? Translation.tr("Pause") : Pomodoro.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
- }
+ transitions: Transition {
+ AnchorAnimation {
+ duration: Appearance.animation.elementMoveFast.duration
+ easing.type: Appearance.animation.elementMoveFast.type
+ easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
}
+ }
- RippleButton {
- implicitHeight: 35
- implicitWidth: 90
- font.pixelSize: Appearance.font.pixelSize.larger
-
- onClicked: {
- if (Pomodoro.isStopwatchRunning)
- Pomodoro.stopwatchRecordLap()
- else
- Pomodoro.stopwatchReset()
- }
- enabled: Pomodoro.stopwatchTime !== 0
-
- colBackground: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
- colBackgroundHover: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover
- colRipple: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2Active : Appearance.colors.colErrorContainerActive
-
- contentItem: StyledText {
- horizontalAlignment: Text.AlignHCenter
- text: Pomodoro.isStopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
- color: Pomodoro.isStopwatchRunning ? Appearance.colors.colOnLayer2 : Appearance.colors.colOnErrorContainer
- }
+ spacing: 0
+ StyledText {
+ // Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
+ font.pixelSize: 40
+ color: Appearance.m3colors.m3onSurface
+ text: {
+ let totalSeconds = Math.floor(TimerService.stopwatchTime) / 100
+ let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
+ let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
+ return `${minutes}:${seconds}`
+ }
+ }
+ StyledText {
+ Layout.fillWidth: true
+ font.pixelSize: 40
+ color: Appearance.colors.colSubtext
+ text: {
+ return `:${(Math.floor(TimerService.stopwatchTime) % 100).toString().padStart(2, '0')}`
}
}
}
@@ -105,14 +75,20 @@ Item {
// Laps
StyledListView {
id: lapsList
- Layout.fillWidth: true
- Layout.fillHeight: true
+ anchors {
+ top: elapsedIndicator.bottom
+ bottom: controlButtons.top
+ left: parent.left
+ right: parent.right
+ topMargin: 16
+ bottomMargin: 16
+ }
spacing: 4
clip: true
popin: true
model: ScriptModel {
- values: Pomodoro.stopwatchLaps.map((v, i, arr) => arr[arr.length - 1 - i])
+ values: TimerService.stopwatchLaps.map((v, i, arr) => arr[arr.length - 1 - i])
}
delegate: Rectangle {
@@ -140,7 +116,7 @@ Item {
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colSubtext
- text: `${Pomodoro.stopwatchLaps.length - lapItem.index}.`
+ text: `${TimerService.stopwatchLaps.length - lapItem.index}.`
}
StyledText {
@@ -161,8 +137,8 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colPrimary
text: {
- const originalIndex = Pomodoro.stopwatchLaps.length - lapItem.index - 1
- const lastTime = originalIndex > 0 ? Pomodoro.stopwatchLaps[originalIndex - 1] : 0
+ const originalIndex = TimerService.stopwatchLaps.length - lapItem.index - 1
+ const lastTime = originalIndex > 0 ? TimerService.stopwatchLaps[originalIndex - 1] : 0
const lapTime = lapItem.modelData - lastTime
const _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
const totalSeconds = Math.floor(lapTime) / 100
@@ -174,5 +150,59 @@ Item {
}
}
}
+
+ RowLayout {
+ id: controlButtons
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom
+ bottomMargin: 6
+ }
+ spacing: 4
+
+ RippleButton {
+ Layout.preferredHeight: 35
+ Layout.preferredWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+
+ onClicked: {
+ TimerService.toggleStopwatch()
+ }
+
+ colBackground: TimerService.isStopwatchRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackgroundHover: TimerService.isStopwatchRunning ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colPrimaryHover
+ colRipple: TimerService.isStopwatchRunning ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colPrimaryActive
+
+ contentItem: StyledText {
+ horizontalAlignment: Text.AlignHCenter
+ color: TimerService.isStopwatchRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ text: TimerService.isStopwatchRunning ? Translation.tr("Pause") : TimerService.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
+ }
+ }
+
+ RippleButton {
+ implicitHeight: 35
+ implicitWidth: 90
+ font.pixelSize: Appearance.font.pixelSize.larger
+
+ onClicked: {
+ if (TimerService.isStopwatchRunning)
+ TimerService.stopwatchRecordLap()
+ else
+ TimerService.stopwatchReset()
+ }
+ enabled: TimerService.stopwatchTime !== 0
+
+ colBackground: TimerService.isStopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
+ colBackgroundHover: TimerService.isStopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover
+ colRipple: TimerService.isStopwatchRunning ? Appearance.colors.colLayer2Active : Appearance.colors.colErrorContainerActive
+
+ contentItem: StyledText {
+ horizontalAlignment: Text.AlignHCenter
+ text: TimerService.isStopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
+ color: TimerService.isStopwatchRunning ? Appearance.colors.colOnLayer2 : Appearance.colors.colOnErrorContainer
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/.config/quickshell/ii/services/Pomodoro.qml b/.config/quickshell/ii/services/TimerService.qml
similarity index 100%
rename from .config/quickshell/ii/services/Pomodoro.qml
rename to .config/quickshell/ii/services/TimerService.qml
From 0e17b7a981d2e6450434db5d0aaebd43c379f58a Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:55:40 +0700
Subject: [PATCH 19/23] TimerService: add back emojis, make reset also reset
isLongBreak
---
.config/quickshell/ii/services/TimerService.qml | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/.config/quickshell/ii/services/TimerService.qml b/.config/quickshell/ii/services/TimerService.qml
index 297a1c204..651af1b05 100644
--- a/.config/quickshell/ii/services/TimerService.qml
+++ b/.config/quickshell/ii/services/TimerService.qml
@@ -60,11 +60,11 @@ Singleton {
// Send notification
let notificationMessage;
if (Persistent.states.timer.pomodoro.isLongBreak) {
- notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(longBreakTime / 60));
+ notificationMessage = Translation.tr(`🌿 Long break: %1 minutes`).arg(Math.floor(longBreakTime / 60));
} else if (Persistent.states.timer.pomodoro.isBreak) {
- notificationMessage = Translation.tr(`Relax for %1 minutes`).arg(Math.floor(breakTime / 60));
+ notificationMessage = Translation.tr(`☕ Break: %1 minutes`).arg(Math.floor(breakTime / 60));
} else {
- notificationMessage = Translation.tr(`Focus for %1 minutes`).arg(Math.floor(focusTime / 60));
+ notificationMessage = Translation.tr(`🔴 Focus: %1 minutes`).arg(Math.floor(focusTime / 60));
}
Quickshell.execDetached(["notify-send", "Pomodoro", notificationMessage, "-a", "Shell"]);
@@ -98,6 +98,7 @@ Singleton {
function resetPomodoro() {
Persistent.states.timer.pomodoro.running = false;
Persistent.states.timer.pomodoro.isBreak = false;
+ Persistent.states.timer.pomodoro.isLongBreak = false;
Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds();
Persistent.states.timer.pomodoro.cycle = 0;
refreshPomodoro();
From ca6463cae8f828a032a4136c489f8347196f256f Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:58:55 +0700
Subject: [PATCH 20/23] TimerService: not have stupid extra declaration
---
.config/quickshell/ii/services/TimerService.qml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.config/quickshell/ii/services/TimerService.qml b/.config/quickshell/ii/services/TimerService.qml
index 651af1b05..f4f9bde95 100644
--- a/.config/quickshell/ii/services/TimerService.qml
+++ b/.config/quickshell/ii/services/TimerService.qml
@@ -52,10 +52,9 @@ Singleton {
// Work <-> break ?
if (getCurrentTimeInSeconds() >= Persistent.states.timer.pomodoro.start + pomodoroLapDuration) {
// Reset counts
- const currentTimeInSeconds = getCurrentTimeInSeconds();
Persistent.states.timer.pomodoro.isBreak = !Persistent.states.timer.pomodoro.isBreak;
Persistent.states.timer.pomodoro.isLongBreak = Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak);
- Persistent.states.timer.pomodoro.start = currentTimeInSeconds;
+ Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds();
// Send notification
let notificationMessage;
From c2d5d2b61ae4d4905ecc7f61e7dbf90e46557071 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sun, 10 Aug 2025 15:40:42 +0700
Subject: [PATCH 21/23] pomodoro: remove unnecessary isLongBreak persistent
state
---
.config/quickshell/ii/modules/common/Persistent.qml | 1 -
.config/quickshell/ii/services/TimerService.qml | 10 +++-------
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/.config/quickshell/ii/modules/common/Persistent.qml b/.config/quickshell/ii/modules/common/Persistent.qml
index e639f54f1..29d41e18d 100644
--- a/.config/quickshell/ii/modules/common/Persistent.qml
+++ b/.config/quickshell/ii/modules/common/Persistent.qml
@@ -67,7 +67,6 @@ Singleton {
property bool running: false
property int start: 0
property bool isBreak: false
- property bool isLongBreak: false
property int cycle: 0
}
property JsonObject stopwatch: JsonObject {
diff --git a/.config/quickshell/ii/services/TimerService.qml b/.config/quickshell/ii/services/TimerService.qml
index f4f9bde95..baf13c796 100644
--- a/.config/quickshell/ii/services/TimerService.qml
+++ b/.config/quickshell/ii/services/TimerService.qml
@@ -22,10 +22,9 @@ Singleton {
property bool isPomodoroRunning: Persistent.states.timer.pomodoro.running
property bool isBreak: Persistent.states.timer.pomodoro.isBreak
- property bool isLongBreak: Persistent.states.timer.pomodoro.isLongBreak
- property bool isPomodoroLongBreak: Persistent.states.timer.pomodoro.isLongBreak
+ property bool isLongBreak: Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak);
property int pomodoroLapDuration: isBreak ? (isLongBreak ? longBreakTime : breakTime) : focusTime
- property int pomodoroSecondsLeft: focusTime
+ property int pomodoroSecondsLeft: isLongBreak ? longBreakTime : (isBreak ? breakTime : focusTime)
property int pomodoroCycle: Persistent.states.timer.pomodoro.cycle
property bool isStopwatchRunning: Persistent.states.timer.stopwatch.running
@@ -53,7 +52,6 @@ Singleton {
if (getCurrentTimeInSeconds() >= Persistent.states.timer.pomodoro.start + pomodoroLapDuration) {
// Reset counts
Persistent.states.timer.pomodoro.isBreak = !Persistent.states.timer.pomodoro.isBreak;
- Persistent.states.timer.pomodoro.isLongBreak = Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak);
Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds();
// Send notification
@@ -97,7 +95,6 @@ Singleton {
function resetPomodoro() {
Persistent.states.timer.pomodoro.running = false;
Persistent.states.timer.pomodoro.isBreak = false;
- Persistent.states.timer.pomodoro.isLongBreak = false;
Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds();
Persistent.states.timer.pomodoro.cycle = 0;
refreshPomodoro();
@@ -133,10 +130,9 @@ Singleton {
}
function stopwatchReset() {
- Persistent.states.timer.stopwatch.running = false;
stopwatchTime = 0;
- Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms();
Persistent.states.timer.stopwatch.laps = [];
+ Persistent.states.timer.stopwatch.running = false;
}
function stopwatchRecordLap() {
From 709415a6b4e54edc07d55d87511013e1cf8c90f8 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sun, 10 Aug 2025 16:02:00 +0700
Subject: [PATCH 22/23] rename stuff
---
.../sidebarRight/pomodoro/PomodoroTimer.qml | 12 ++++-----
.../sidebarRight/pomodoro/Stopwatch.qml | 22 ++++++++--------
.../quickshell/ii/services/TimerService.qml | 26 +++++++++----------
3 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
index e50f92d83..439c06ee4 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/PomodoroTimer.qml
@@ -45,7 +45,7 @@ Item {
}
StyledText {
Layout.alignment: Qt.AlignHCenter
- text: TimerService.isLongBreak ? Translation.tr("Long break") : TimerService.isBreak ? Translation.tr("Break") : Translation.tr("Focus")
+ text: TimerService.pomodoroLongBreak ? Translation.tr("Long break") : TimerService.pomodoroBreak ? Translation.tr("Break") : Translation.tr("Focus")
font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.colors.colSubtext
}
@@ -80,15 +80,15 @@ Item {
contentItem: StyledText {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
- text: TimerService.isPomodoroRunning ? Translation.tr("Pause") : (TimerService.pomodoroSecondsLeft === TimerService.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
- color: TimerService.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ text: TimerService.pomodoroRunning ? Translation.tr("Pause") : (TimerService.pomodoroSecondsLeft === TimerService.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
+ color: TimerService.pomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
}
implicitHeight: 35
implicitWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger
onClicked: TimerService.togglePomodoro()
- colBackground: TimerService.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
- colBackgroundHover: TimerService.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackground: TimerService.pomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackgroundHover: TimerService.pomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
}
RippleButton {
@@ -96,7 +96,7 @@ Item {
implicitWidth: 90
onClicked: TimerService.resetPomodoro()
- enabled: (TimerService.pomodoroSecondsLeft < TimerService.pomodoroLapDuration) || TimerService.pomodoroCycle > 0 || TimerService.isBreak
+ enabled: (TimerService.pomodoroSecondsLeft < TimerService.pomodoroLapDuration) || TimerService.pomodoroCycle > 0 || TimerService.pomodoroBreak
font.pixelSize: Appearance.font.pixelSize.larger
colBackground: Appearance.colors.colErrorContainer
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
index cbf588194..2f1483932 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
@@ -169,14 +169,14 @@ Item {
TimerService.toggleStopwatch()
}
- colBackground: TimerService.isStopwatchRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
- colBackgroundHover: TimerService.isStopwatchRunning ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colPrimaryHover
- colRipple: TimerService.isStopwatchRunning ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colPrimaryActive
+ colBackground: TimerService.stopwatchRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
+ colBackgroundHover: TimerService.stopwatchRunning ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colPrimaryHover
+ colRipple: TimerService.stopwatchRunning ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colPrimaryActive
contentItem: StyledText {
horizontalAlignment: Text.AlignHCenter
- color: TimerService.isStopwatchRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
- text: TimerService.isStopwatchRunning ? Translation.tr("Pause") : TimerService.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
+ color: TimerService.stopwatchRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
+ text: TimerService.stopwatchRunning ? Translation.tr("Pause") : TimerService.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
}
}
@@ -186,21 +186,21 @@ Item {
font.pixelSize: Appearance.font.pixelSize.larger
onClicked: {
- if (TimerService.isStopwatchRunning)
+ if (TimerService.stopwatchRunning)
TimerService.stopwatchRecordLap()
else
TimerService.stopwatchReset()
}
enabled: TimerService.stopwatchTime !== 0
- colBackground: TimerService.isStopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
- colBackgroundHover: TimerService.isStopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover
- colRipple: TimerService.isStopwatchRunning ? Appearance.colors.colLayer2Active : Appearance.colors.colErrorContainerActive
+ colBackground: TimerService.stopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
+ colBackgroundHover: TimerService.stopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover
+ colRipple: TimerService.stopwatchRunning ? Appearance.colors.colLayer2Active : Appearance.colors.colErrorContainerActive
contentItem: StyledText {
horizontalAlignment: Text.AlignHCenter
- text: TimerService.isStopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
- color: TimerService.isStopwatchRunning ? Appearance.colors.colOnLayer2 : Appearance.colors.colOnErrorContainer
+ text: TimerService.stopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
+ color: TimerService.stopwatchRunning ? Appearance.colors.colOnLayer2 : Appearance.colors.colOnErrorContainer
}
}
}
diff --git a/.config/quickshell/ii/services/TimerService.qml b/.config/quickshell/ii/services/TimerService.qml
index baf13c796..5824c01ab 100644
--- a/.config/quickshell/ii/services/TimerService.qml
+++ b/.config/quickshell/ii/services/TimerService.qml
@@ -20,21 +20,21 @@ Singleton {
property int cyclesBeforeLongBreak: Config.options.time.pomodoro.cyclesBeforeLongBreak
property string alertSound: Config.options.time.pomodoro.alertSound
- property bool isPomodoroRunning: Persistent.states.timer.pomodoro.running
- property bool isBreak: Persistent.states.timer.pomodoro.isBreak
- property bool isLongBreak: Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak);
- property int pomodoroLapDuration: isBreak ? (isLongBreak ? longBreakTime : breakTime) : focusTime
- property int pomodoroSecondsLeft: isLongBreak ? longBreakTime : (isBreak ? breakTime : focusTime)
+ property bool pomodoroRunning: Persistent.states.timer.pomodoro.running
+ property bool pomodoroBreak: Persistent.states.timer.pomodoro.isBreak
+ property bool pomodoroLongBreak: Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak);
+ property int pomodoroLapDuration: pomodoroLongBreak ? longBreakTime : pomodoroBreak ? breakTime : focusTime // This is a binding that's to be kept
+ property int pomodoroSecondsLeft: pomodoroLapDuration // Reasonable init value, to be changed
property int pomodoroCycle: Persistent.states.timer.pomodoro.cycle
- property bool isStopwatchRunning: Persistent.states.timer.stopwatch.running
+ property bool stopwatchRunning: Persistent.states.timer.stopwatch.running
property int stopwatchTime: 0
property int stopwatchStart: Persistent.states.timer.stopwatch.start
property var stopwatchLaps: Persistent.states.timer.stopwatch.laps
// General
Component.onCompleted: {
- if (!isStopwatchRunning)
+ if (!stopwatchRunning)
stopwatchReset();
}
@@ -56,7 +56,7 @@ Singleton {
// Send notification
let notificationMessage;
- if (Persistent.states.timer.pomodoro.isLongBreak) {
+ if (Persistent.states.timer.pomodoro.isBreak && (pomodoroCycle + 1 == cyclesBeforeLongBreak)) {
notificationMessage = Translation.tr(`🌿 Long break: %1 minutes`).arg(Math.floor(longBreakTime / 60));
} else if (Persistent.states.timer.pomodoro.isBreak) {
notificationMessage = Translation.tr(`☕ Break: %1 minutes`).arg(Math.floor(breakTime / 60));
@@ -68,7 +68,7 @@ Singleton {
if (alertSound)
Quickshell.execDetached(["ffplay", "-nodisp", "-autoexit", alertSound]);
- if (!isBreak) {
+ if (!pomodoroBreak) {
Persistent.states.timer.pomodoro.cycle = (Persistent.states.timer.pomodoro.cycle + 1) % root.cyclesBeforeLongBreak;
}
}
@@ -79,13 +79,13 @@ Singleton {
Timer {
id: pomodoroTimer
interval: 200
- running: root.isPomodoroRunning
+ running: root.pomodoroRunning
repeat: true
onTriggered: refreshPomodoro()
}
function togglePomodoro() {
- Persistent.states.timer.pomodoro.running = !isPomodoroRunning;
+ Persistent.states.timer.pomodoro.running = !pomodoroRunning;
if (Persistent.states.timer.pomodoro.running) {
// Start/Resume
Persistent.states.timer.pomodoro.start = getCurrentTimeInSeconds() + pomodoroSecondsLeft - pomodoroLapDuration;
@@ -108,13 +108,13 @@ Singleton {
Timer {
id: stopwatchTimer
interval: 10
- running: root.isStopwatchRunning
+ running: root.stopwatchRunning
repeat: true
onTriggered: refreshStopwatch()
}
function toggleStopwatch() {
- if (root.isStopwatchRunning)
+ if (root.stopwatchRunning)
stopwatchPause();
else
stopwatchResume();
From 86f1e63ed2e56013b77a5d254326a69e454f692d Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sun, 10 Aug 2025 16:03:37 +0700
Subject: [PATCH 23/23] stopwatch: clear laps on Start
---
.../quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml | 2 +-
.config/quickshell/ii/services/TimerService.qml | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
index 2f1483932..ffc706568 100644
--- a/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
+++ b/.config/quickshell/ii/modules/sidebarRight/pomodoro/Stopwatch.qml
@@ -191,7 +191,7 @@ Item {
else
TimerService.stopwatchReset()
}
- enabled: TimerService.stopwatchTime !== 0
+ enabled: TimerService.stopwatchTime > 0 || Persistent.states.timer.stopwatch.laps.length > 0
colBackground: TimerService.stopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
colBackgroundHover: TimerService.stopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover
diff --git a/.config/quickshell/ii/services/TimerService.qml b/.config/quickshell/ii/services/TimerService.qml
index 5824c01ab..e6c8906bc 100644
--- a/.config/quickshell/ii/services/TimerService.qml
+++ b/.config/quickshell/ii/services/TimerService.qml
@@ -125,6 +125,7 @@ Singleton {
}
function stopwatchResume() {
+ if (stopwatchTime === 0) Persistent.states.timer.stopwatch.laps = [];
Persistent.states.timer.stopwatch.running = true;
Persistent.states.timer.stopwatch.start = getCurrentTimeIn10ms() - stopwatchTime;
}