Files

220 lines
6.9 KiB
QML

import QtQuick
import qs.Common
import qs.DCommon.Widgets
import qs.Modules.Plugins
import qs.Modules.DDash
import qs.Modules.DDash.Overview
DashTabComponent {
id: root
property bool running: false
property real startedAt: 0
property real banked: 0
property var laps: []
property real now: 0
readonly property real elapsed: banked + (running ? Math.max(0, now - startedAt) : 0)
readonly property bool showHundredths: options.hundredths !== false
readonly property var visibleLaps: laps.slice(0, options.laps ?? 5)
focusTarget: startButton
implicitHeight: Math.max(DashMetrics.tabMinHeight, content.implicitHeight + Theme.spacingL * 2)
menuActions: [
{
label: I18n.trFor("dashTabExample", "Reset"),
iconName: "restart_alt",
enabled: root.elapsed > 0,
action: () => root.reset()
}
]
onPluginIdChanged: restore()
onPluginServiceChanged: restore()
function restore() {
if (!pluginService || !pluginId)
return;
const saved = pluginService.loadPluginState(pluginId, "stopwatch", null);
if (!saved)
return;
running = saved.running === true;
startedAt = Number(saved.startedAt) || 0;
banked = Number(saved.banked) || 0;
laps = Array.isArray(saved.laps) ? saved.laps : [];
now = Date.now();
}
function save() {
pluginService?.savePluginState(pluginId, "stopwatch", {
running: running,
startedAt: startedAt,
banked: banked,
laps: laps
});
}
function toggle() {
now = Date.now();
if (running) {
banked += now - startedAt;
running = false;
} else {
startedAt = now;
running = true;
}
save();
}
function lap() {
if (!running)
return;
now = Date.now();
laps = [elapsed].concat(laps).slice(0, 99);
save();
}
function reset() {
running = false;
startedAt = 0;
banked = 0;
laps = [];
save();
}
function pad2(value) {
return String(value).padStart(2, "0");
}
function format(ms) {
const centis = Math.floor(ms / 10);
const seconds = Math.floor(centis / 100);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const clock = (hours > 0 ? hours + ":" : "") + pad2(minutes % 60) + ":" + pad2(seconds % 60);
return showHundredths ? clock + "." + pad2(centis % 100) : clock;
}
function restoreFocus() {
startButton.forceActiveFocus(Qt.OtherFocusReason);
}
function handleKeyEvent(event) {
if (event.modifiers & (Qt.ControlModifier | Qt.AltModifier))
return false;
switch (event.key) {
case Qt.Key_Space:
toggle();
return true;
case Qt.Key_L:
lap();
return true;
case Qt.Key_R:
reset();
return true;
}
return false;
}
Timer {
interval: root.showHundredths ? 40 : 200
repeat: true
triggeredOnStart: true
running: root.live && root.running
onTriggered: root.now = Date.now()
}
Column {
id: content
anchors.centerIn: parent
width: Math.min(parent.width, DashMetrics.gridRowUnit * 5)
spacing: Theme.spacingL
NumericText {
anchors.horizontalCenter: parent.horizontalCenter
isMonospace: false
text: root.format(root.elapsed)
font.pixelSize: Theme.fontSizeDisplayLarge
font.weight: Theme.fontWeightMedium
color: root.running ? Theme.primary : Theme.surfaceText
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingS
DButton {
text: root.running ? I18n.trFor("dashTabExample", "Lap") : I18n.trFor("dashTabExample", "Reset")
reserveText: I18n.trFor("dashTabExample", "Reset")
iconName: root.running ? "flag" : "restart_alt"
enabled: root.running || root.elapsed > 0
backgroundColor: Theme.secondaryContainer
textColor: Theme.onSecondaryContainer
onClicked: root.running ? root.lap() : root.reset()
}
DButton {
id: startButton
text: root.running ? I18n.trFor("dashTabExample", "Pause") : root.elapsed > 0 ? I18n.trFor("dashTabExample", "Resume") : I18n.trFor("dashTabExample", "Start")
reserveText: I18n.trFor("dashTabExample", "Resume")
iconName: root.running ? "pause" : "play_arrow"
onClicked: root.toggle()
}
}
Card {
id: lapCard
width: parent.width
height: lapList.implicitHeight + pad * 2
visible: root.visibleLaps.length > 0
Column {
id: lapList
width: parent.width
Repeater {
model: root.visibleLaps
Item {
id: lapRow
required property int index
required property real modelData
width: lapList.width
height: Theme.buttonHeightXS
StyledText {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: I18n.trFor("dashTabExample", "Lap %1").arg(root.laps.length - lapRow.index)
font.pixelSize: Theme.fontSizeMedium
color: lapCard.mutedColor
}
NumericText {
anchors.right: total.left
anchors.rightMargin: Theme.spacingXL
anchors.verticalCenter: parent.verticalCenter
isMonospace: false
text: root.format(lapRow.modelData - (root.laps[lapRow.index + 1] ?? 0))
font.pixelSize: Theme.fontSizeMedium
color: lapCard.contentColor
}
NumericText {
id: total
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
isMonospace: false
text: root.format(lapRow.modelData)
font.pixelSize: Theme.fontSizeMedium
color: lapCard.mutedColor
}
}
}
}
}
}
}