Improve stopwatch (#432)

* Improve stopwatch more

* Make sure sleep gets reenabled

* Cleanup and clang-format
This commit is contained in:
Riku Isokoski 2021-07-04 21:23:03 +03:00 committed by GitHub
parent ab59b9b830
commit 61a4642221
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 119 deletions

View file

@ -337,7 +337,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None);
break; break;
case Apps::StopWatch: case Apps::StopWatch:
currentScreen = std::make_unique<Screens::StopWatch>(this); currentScreen = std::make_unique<Screens::StopWatch>(this, *systemTask);
break; break;
case Apps::Twos: case Apps::Twos:
currentScreen = std::make_unique<Screens::Twos>(this); currentScreen = std::make_unique<Screens::Twos>(this);

View file

@ -45,17 +45,16 @@ static void stop_lap_event_handler(lv_obj_t* obj, lv_event_t event) {
stopWatch->stopLapBtnEventHandler(event); stopWatch->stopLapBtnEventHandler(event);
} }
StopWatch::StopWatch(DisplayApp* app) StopWatch::StopWatch(DisplayApp* app, System::SystemTask& systemTask)
: Screen(app), : Screen(app),
systemTask {systemTask},
running {true}, running {true},
currentState {States::Init}, currentState {States::Init},
currentEvent {Events::Stop},
startTime {}, startTime {},
oldTimeElapsed {}, oldTimeElapsed {},
currentTimeSeparated {}, currentTimeSeparated {},
lapBuffer {}, lapBuffer {},
lapNr {}, lapNr {} {
lapPressed {false} {
time = lv_label_create(lv_scr_act(), nullptr); time = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
@ -105,28 +104,16 @@ StopWatch::StopWatch(DisplayApp* app)
} }
StopWatch::~StopWatch() { StopWatch::~StopWatch() {
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
} }
bool StopWatch::Refresh() { void StopWatch::reset() {
// @startuml CHIP8_state currentState = States::Init;
// State "Init" as init oldTimeElapsed = 0;
// State "Running" as run lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
// State "Halted" as halt lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
// [*] --> init
// init -> run : press play
// run -> run : press lap
// run --> halt : press pause
// halt --> run : press play
// halt --> init : press stop
// @enduml
// Copy paste the above plantuml text to visualize the state diagram
switch (currentState) {
// Init state when an user first opens the app
// and when a stop/reset button is pressed
case States::Init: {
// The initial default value
lv_label_set_text(time, "00:00"); lv_label_set_text(time, "00:00");
lv_label_set_text(msecTime, "00"); lv_label_set_text(msecTime, "00");
@ -134,99 +121,83 @@ bool StopWatch::Refresh() {
lv_label_set_text(lapTwoText, ""); lv_label_set_text(lapTwoText, "");
lapBuffer.clearBuffer(); lapBuffer.clearBuffer();
lapNr = 0; lapNr = 0;
if (currentEvent == Events::Play) {
lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT);
lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT);
startTime = xTaskGetTickCount();
currentState = States::Running;
} else {
lv_obj_set_state(btnStopLap, LV_STATE_DISABLED); lv_obj_set_state(btnStopLap, LV_STATE_DISABLED);
lv_obj_set_state(txtStopLap, LV_STATE_DISABLED); lv_obj_set_state(txtStopLap, LV_STATE_DISABLED);
} }
break;
} void StopWatch::start() {
case States::Running: { lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT);
lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT);
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_label_set_text(txtPlayPause, Symbols::pause); lv_label_set_text(txtPlayPause, Symbols::pause);
lv_label_set_text(txtStopLap, Symbols::lapsFlag); lv_label_set_text(txtStopLap, Symbols::lapsFlag);
startTime = xTaskGetTickCount();
currentState = States::Running;
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
}
const auto timeElapsed = calculateDelta(startTime, xTaskGetTickCount()); void StopWatch::pause() {
currentTimeSeparated = convertTicksToTimeSegments((oldTimeElapsed + timeElapsed));
lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths);
if (lapPressed == true) {
if (lapBuffer[1]) {
lv_label_set_text_fmt(
lapOneText, "#%2d %2d:%02d.%02d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->hundredths);
}
if (lapBuffer[0]) {
lv_label_set_text_fmt(
lapTwoText, "#%2d %2d:%02d.%02d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->hundredths);
}
// Reset the bool to avoid setting the text in each cycle until there is a change
lapPressed = false;
}
if (currentEvent == Events::Pause) {
// Reset the start time
startTime = 0; startTime = 0;
// Store the current time elapsed in cache // Store the current time elapsed in cache
oldTimeElapsed += timeElapsed; oldTimeElapsed += timeElapsed;
currentState = States::Halted; currentState = States::Halted;
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
} else {
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
}
break;
}
case States::Halted: {
lv_label_set_text(txtPlayPause, Symbols::play); lv_label_set_text(txtPlayPause, Symbols::play);
lv_label_set_text(txtStopLap, Symbols::stop); lv_label_set_text(txtStopLap, Symbols::stop);
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
}
if (currentEvent == Events::Play) { bool StopWatch::Refresh() {
startTime = xTaskGetTickCount(); if (currentState == States::Running) {
currentState = States::Running; timeElapsed = calculateDelta(startTime, xTaskGetTickCount());
} currentTimeSeparated = convertTicksToTimeSegments((oldTimeElapsed + timeElapsed));
if (currentEvent == Events::Stop) {
currentState = States::Init; lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
oldTimeElapsed = 0; lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths);
lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
}
break;
}
} }
return running; return running;
} }
void StopWatch::playPauseBtnEventHandler(lv_event_t event) { void StopWatch::playPauseBtnEventHandler(lv_event_t event) {
if (event == LV_EVENT_CLICKED) { if (event != LV_EVENT_PRESSED) {
if (currentState == States::Init) { return;
currentEvent = Events::Play;
} else {
// Simple Toggle for play/pause
currentEvent = (currentEvent == Events::Play ? Events::Pause : Events::Play);
} }
if (currentState == States::Init) {
start();
} else if (currentState == States::Running) {
pause();
} else if (currentState == States::Halted) {
start();
} }
} }
void StopWatch::stopLapBtnEventHandler(lv_event_t event) { void StopWatch::stopLapBtnEventHandler(lv_event_t event) {
if (event == LV_EVENT_CLICKED) { if (event != LV_EVENT_PRESSED) {
return;
}
// If running, then this button is used to save laps // If running, then this button is used to save laps
if (currentState == States::Running) { if (currentState == States::Running) {
lapBuffer.addLaps(currentTimeSeparated); lapBuffer.addLaps(currentTimeSeparated);
lapNr++; lapNr++;
lapPressed = true; if (lapBuffer[1]) {
lv_label_set_text_fmt(
} else if (currentState == States::Halted) { lapOneText, "#%2d %2d:%02d.%02d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->hundredths);
currentEvent = Events::Stop;
} else {
// Not possible to reach here. Do nothing.
} }
if (lapBuffer[0]) {
lv_label_set_text_fmt(lapTwoText, "#%2d %2d:%02d.%02d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->hundredths);
}
} else if (currentState == States::Halted) {
reset();
} }
} }
bool StopWatch::OnButtonPushed() {
if (currentState == States::Running) {
pause();
} else {
running = false;
}
return true;
}

View file

@ -8,13 +8,12 @@
#include "portmacro_cmsis.h" #include "portmacro_cmsis.h"
#include <array> #include <array>
#include "systemtask/SystemTask.h"
namespace Pinetime::Applications::Screens { namespace Pinetime::Applications::Screens {
enum class States { Init, Running, Halted }; enum class States { Init, Running, Halted };
enum class Events { Play, Pause, Stop };
struct TimeSeparated_t { struct TimeSeparated_t {
int mins; int mins;
int secs; int secs;
@ -63,23 +62,28 @@ namespace Pinetime::Applications::Screens {
class StopWatch : public Screen { class StopWatch : public Screen {
public: public:
StopWatch(DisplayApp* app); StopWatch(DisplayApp* app, System::SystemTask& systemTask);
~StopWatch() override; ~StopWatch() override;
bool Refresh() override; bool Refresh() override;
void playPauseBtnEventHandler(lv_event_t event); void playPauseBtnEventHandler(lv_event_t event);
void stopLapBtnEventHandler(lv_event_t event); void stopLapBtnEventHandler(lv_event_t event);
bool OnButtonPushed() override;
void reset();
void start();
void pause();
private: private:
Pinetime::System::SystemTask& systemTask;
TickType_t timeElapsed;
bool running; bool running;
States currentState; States currentState;
Events currentEvent;
TickType_t startTime; TickType_t startTime;
TickType_t oldTimeElapsed; TickType_t oldTimeElapsed;
TimeSeparated_t currentTimeSeparated; // Holds Mins, Secs, millisecs TimeSeparated_t currentTimeSeparated; // Holds Mins, Secs, millisecs
LapTextBuffer_t<2> lapBuffer; LapTextBuffer_t<2> lapBuffer;
int lapNr; int lapNr = 0;
bool lapPressed;
lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
lv_obj_t *lapOneText, *lapTwoText; lv_obj_t *lapOneText, *lapTwoText;
}; };