pomodoro: rename service to TimerService, better stopwatch layout

This commit is contained in:
end-4
2025-08-09 22:48:40 +07:00
parent fbe17dc3e3
commit bcd1167d39
4 changed files with 128 additions and 98 deletions
@@ -24,7 +24,7 @@ Item {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
lineWidth: 8 lineWidth: 8
value: { value: {
return Pomodoro.pomodoroSecondsLeft / Pomodoro.pomodoroLapDuration; return TimerService.pomodoroSecondsLeft / TimerService.pomodoroLapDuration;
} }
size: 200 size: 200
enableAnimation: true enableAnimation: true
@@ -36,8 +36,8 @@ Item {
StyledText { StyledText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
text: { text: {
let minutes = Math.floor(Pomodoro.pomodoroSecondsLeft / 60).toString().padStart(2, '0'); let minutes = Math.floor(TimerService.pomodoroSecondsLeft / 60).toString().padStart(2, '0');
let seconds = Math.floor(Pomodoro.pomodoroSecondsLeft % 60).toString().padStart(2, '0'); let seconds = Math.floor(TimerService.pomodoroSecondsLeft % 60).toString().padStart(2, '0');
return `${minutes}:${seconds}`; return `${minutes}:${seconds}`;
} }
font.pixelSize: 40 font.pixelSize: 40
@@ -45,7 +45,7 @@ Item {
} }
StyledText { StyledText {
Layout.alignment: Qt.AlignHCenter 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 font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.colors.colSubtext color: Appearance.colors.colSubtext
} }
@@ -66,7 +66,7 @@ Item {
id: cycleText id: cycleText
anchors.centerIn: parent anchors.centerIn: parent
color: Appearance.colors.colOnLayer2 color: Appearance.colors.colOnLayer2
text: Pomodoro.pomodoroCycle + 1 text: TimerService.pomodoroCycle + 1
} }
} }
} }
@@ -80,23 +80,23 @@ Item {
contentItem: StyledText { contentItem: StyledText {
anchors.centerIn: parent anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: Pomodoro.isPomodoroRunning ? Translation.tr("Pause") : (Pomodoro.pomodoroSecondsLeft === Pomodoro.focusTime) ? Translation.tr("Start") : Translation.tr("Resume") text: TimerService.isPomodoroRunning ? Translation.tr("Pause") : (TimerService.pomodoroSecondsLeft === TimerService.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
color: Pomodoro.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary color: TimerService.isPomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
} }
implicitHeight: 35 implicitHeight: 35
implicitWidth: 90 implicitWidth: 90
font.pixelSize: Appearance.font.pixelSize.larger font.pixelSize: Appearance.font.pixelSize.larger
onClicked: Pomodoro.togglePomodoro() onClicked: TimerService.togglePomodoro()
colBackground: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary colBackground: TimerService.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
colBackgroundHover: Pomodoro.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary colBackgroundHover: TimerService.isPomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
} }
RippleButton { RippleButton {
implicitHeight: 35 implicitHeight: 35
implicitWidth: 90 implicitWidth: 90
onClicked: Pomodoro.resetPomodoro() onClicked: TimerService.resetPomodoro()
enabled: (Pomodoro.pomodoroSecondsLeft < Pomodoro.pomodoroLapDuration) || Pomodoro.pomodoroCycle > 0 || Pomodoro.isBreak enabled: (TimerService.pomodoroSecondsLeft < TimerService.pomodoroLapDuration) || TimerService.pomodoroCycle > 0 || TimerService.isBreak
font.pixelSize: Appearance.font.pixelSize.larger font.pixelSize: Appearance.font.pixelSize.larger
colBackground: Appearance.colors.colErrorContainer colBackground: Appearance.colors.colErrorContainer
@@ -25,20 +25,20 @@ Item {
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S } else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
if (currentTab === 0) { if (currentTab === 0) {
Pomodoro.togglePomodoro() TimerService.togglePomodoro()
} else { } else {
Pomodoro.toggleStopwatch() TimerService.toggleStopwatch()
} }
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_R) { // Reset with R } else if (event.key === Qt.Key_R) { // Reset with R
if (currentTab === 0) { if (currentTab === 0) {
Pomodoro.resetPomodoro() TimerService.resetPomodoro()
} else { } else {
Pomodoro.stopwatchReset() TimerService.stopwatchReset()
} }
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_L) { // Record lap with L } else if (event.key === Qt.Key_L) { // Record lap with L
Pomodoro.stopwatchRecordLap() TimerService.stopwatchRecordLap()
event.accepted = true event.accepted = true
} }
} }
@@ -13,91 +13,61 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
ColumnLayout { Item {
anchors { anchors {
fill: parent fill: parent
leftMargin: 20 topMargin: 8
rightMargin: 20 leftMargin: 16
rightMargin: 16
} }
spacing: 20
ColumnLayout { RowLayout { // Elapsed
spacing: 8 id: elapsedIndicator
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: false anchors {
top: undefined
verticalCenter: parent.verticalCenter
left: controlButtons.left
leftMargin: 6
}
RowLayout { // Elapsed states: State {
id: elapsedIndicator name: "hasLaps"
Layout.alignment: Qt.AlignHCenter when: TimerService.stopwatchLaps.length > 0
spacing: 0 AnchorChanges {
StyledText { target: elapsedIndicator
// Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness anchors.top: parent.top
font.pixelSize: 40 anchors.verticalCenter: undefined
color: Appearance.m3colors.m3onSurface anchors.left: controlButtons.left
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 `:<sub>${(Math.floor(Pomodoro.stopwatchTime) % 100).toString().padStart(2, '0')}</sub>`
}
} }
} }
// The Start/Stop and Reset buttons transitions: Transition {
RowLayout { AnchorAnimation {
Layout.alignment: Qt.AlignHCenter duration: Appearance.animation.elementMoveFast.duration
spacing: 4 easing.type: Appearance.animation.elementMoveFast.type
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
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 { spacing: 0
implicitHeight: 35 StyledText {
implicitWidth: 90 // Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
font.pixelSize: Appearance.font.pixelSize.larger font.pixelSize: 40
color: Appearance.m3colors.m3onSurface
onClicked: { text: {
if (Pomodoro.isStopwatchRunning) let totalSeconds = Math.floor(TimerService.stopwatchTime) / 100
Pomodoro.stopwatchRecordLap() let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
else let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
Pomodoro.stopwatchReset() return `${minutes}:${seconds}`
} }
enabled: Pomodoro.stopwatchTime !== 0 }
StyledText {
colBackground: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer Layout.fillWidth: true
colBackgroundHover: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover font.pixelSize: 40
colRipple: Pomodoro.isStopwatchRunning ? Appearance.colors.colLayer2Active : Appearance.colors.colErrorContainerActive color: Appearance.colors.colSubtext
text: {
contentItem: StyledText { return `:<sub>${(Math.floor(TimerService.stopwatchTime) % 100).toString().padStart(2, '0')}</sub>`
horizontalAlignment: Text.AlignHCenter
text: Pomodoro.isStopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
color: Pomodoro.isStopwatchRunning ? Appearance.colors.colOnLayer2 : Appearance.colors.colOnErrorContainer
}
} }
} }
} }
@@ -105,14 +75,20 @@ Item {
// Laps // Laps
StyledListView { StyledListView {
id: lapsList id: lapsList
Layout.fillWidth: true anchors {
Layout.fillHeight: true top: elapsedIndicator.bottom
bottom: controlButtons.top
left: parent.left
right: parent.right
topMargin: 16
bottomMargin: 16
}
spacing: 4 spacing: 4
clip: true clip: true
popin: true popin: true
model: ScriptModel { 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 { delegate: Rectangle {
@@ -140,7 +116,7 @@ Item {
StyledText { StyledText {
font.pixelSize: Appearance.font.pixelSize.small font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colSubtext color: Appearance.colors.colSubtext
text: `${Pomodoro.stopwatchLaps.length - lapItem.index}.` text: `${TimerService.stopwatchLaps.length - lapItem.index}.`
} }
StyledText { StyledText {
@@ -161,8 +137,8 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colPrimary color: Appearance.colors.colPrimary
text: { text: {
const originalIndex = Pomodoro.stopwatchLaps.length - lapItem.index - 1 const originalIndex = TimerService.stopwatchLaps.length - lapItem.index - 1
const lastTime = originalIndex > 0 ? Pomodoro.stopwatchLaps[originalIndex - 1] : 0 const lastTime = originalIndex > 0 ? TimerService.stopwatchLaps[originalIndex - 1] : 0
const lapTime = lapItem.modelData - lastTime const lapTime = lapItem.modelData - lastTime
const _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0') const _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
const totalSeconds = Math.floor(lapTime) / 100 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
}
}
}
} }
} }