pomodoro: make pause/resume less weird

This commit is contained in:
end-4 2025-08-09 21:36:28 +07:00
parent 5bf80dae4e
commit 4ca15a1fc3
4 changed files with 80 additions and 175 deletions

View file

@ -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
}
}

View file

@ -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

View file

@ -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
}
}
}
}
}
}

View file

@ -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()
}