- attempt to make app easier to understand using state management

This commit is contained in:
Dmytro Bogovych 2022-11-13 20:16:09 +03:00
parent fff67bbedb
commit 266d6ddf09
2 changed files with 142 additions and 99 deletions

View File

@ -83,16 +83,16 @@ void MainWindow::init()
mLastIdleMilliseconds = 0;
// Timer to start break
mTimer = new QTimer(this);
mTimer->setTimerType(Qt::TimerType::CoarseTimer);
mTimer->setSingleShot(true);
connect(mTimer, SIGNAL(timeout()), this, SLOT(onLongBreakStart()));
mBreakStartTimer = new QTimer(this);
mBreakStartTimer->setTimerType(Qt::TimerType::CoarseTimer);
mBreakStartTimer->setSingleShot(true);
connect(mBreakStartTimer, SIGNAL(timeout()), this, SLOT(onLongBreakStart()));
// Timer to run notification about upcoming break
mNotifyTimer = new QTimer(this);
mNotifyTimer->setTimerType(Qt::TimerType::CoarseTimer);
mNotifyTimer->setSingleShot(true);
connect(mNotifyTimer, SIGNAL(timeout()), this, SLOT(onLongBreakNotify()));
mBreakNotifyTimer = new QTimer(this);
mBreakNotifyTimer->setTimerType(Qt::TimerType::CoarseTimer);
mBreakNotifyTimer->setSingleShot(true);
connect(mBreakNotifyTimer, SIGNAL(timeout()), this, SLOT(onLongBreakNotify()));
// Just update UI once per minute
mUpdateUITimer = new QTimer(this);
@ -114,8 +114,7 @@ void MainWindow::init()
// Use the latest config
applyConfig();
// Refresh UI
onUpdateUI();
shiftTo(AppState::Counting);
}
void MainWindow::loadConfig()
@ -126,17 +125,17 @@ void MainWindow::loadConfig()
void MainWindow::applyConfig()
{
if (mTimer)
if (mBreakStartTimer)
{
if (mTimer->interval() != mAppConfig.longbreak_interval)
if (mBreakStartTimer->interval() != mAppConfig.longbreak_interval)
{
mTimer->stop();
mTimer->setInterval(std::chrono::seconds(mAppConfig.longbreak_interval));
mTimer->start();
mBreakStartTimer->stop();
mBreakStartTimer->setInterval(std::chrono::seconds(mAppConfig.longbreak_interval));
mBreakStartTimer->start();
mNotifyTimer->stop();
mNotifyTimer->setInterval(std::chrono::seconds(mAppConfig.longbreak_interval - 30));
mNotifyTimer->start();
mBreakNotifyTimer->stop();
mBreakNotifyTimer->setInterval(std::chrono::seconds(mAppConfig.longbreak_interval - 30));
mBreakNotifyTimer->start();
}
}
@ -210,7 +209,10 @@ void MainWindow::showMe()
// qDebug() << "Window moved to screen " << screen->name() + " / " + screen->model() + " " + screen->manufacturer();
}
}
#if !defined(DEBUG)
showFullScreen();
#endif
}
void MainWindow::hideMe()
@ -262,79 +264,90 @@ static int msec2min(int msec)
return (int)(min_f + 0.5f);
}
void MainWindow::shiftTo(AppState newState)
{
if (newState == mState)
return;
switch (newState)
{
case AppState::None:
// Do nothing, app is not started
break;
case AppState::Idle:
onIdleStart();
break;
case AppState::Break:
// Break is active
onLongBreakStart();
break;
case AppState::Counting:
// Working, break is closing
if (mState == AppState::Break)
onLongBreakEnd();
else
if (mState == AppState::Idle)
onIdleEnd();
break;
}
mState = newState;
onUpdateUI();
}
void MainWindow::onUpdateUI()
{
if (mAppConfig.idle_timeout != 0 && (mTimer->isActive() || mIdleStart))
int idle_milliseconds = 0;
switch (mState)
{
int idle_milliseconds = get_idle_time_dynamically();
case AppState::None:
// Do nothing, app is not started
break;
case AppState::Idle:
// Detected idle, don't count this time as working
// But check - maybe idle is over
idle_milliseconds = get_idle_time_dynamically();
if (idle_milliseconds < mAppConfig.idle_timeout * 60 * 1000)
{
shiftTo(AppState::Counting);
return;
}
break;
case AppState::Break:
// Break is active
if (mTrayIcon)
mTrayIcon->setToolTip(QString());
break;
case AppState::Counting:
// Working, break is closing
// Check maybe it is idle ?
if (!mIdleStart && mAppConfig.idle_timeout)
{
idle_milliseconds = get_idle_time_dynamically();
if (idle_milliseconds >= mAppConfig.idle_timeout * 60 * 1000)
{
if (!mIdleStart)
{
// Idle could start before timer start
// Check and shrink the found idle interval if needed
auto current_time = std::chrono::steady_clock::now();
auto proposed_idle_start = current_time - std::chrono::milliseconds(idle_milliseconds);
auto timer_start = std::chrono::steady_clock::now() - (std::chrono::milliseconds(mTimer->interval() - mTimer->remainingTime()));
mIdleStart = std::max(timer_start, proposed_idle_start);
// Start idle mode. Save idle start time
// mIdleStart = std::chrono::steady_clock::now() - std::chrono::milliseconds(idle_milliseconds);
if (mTimer->isActive())
{
// Correct duration of idle
idle_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - mIdleStart.value()).count();
// Save how much time was remaininig when idle was detected + add idle length
// Later timer will restart with this interval time
mIdleRemaining = mTimer->remainingTime() + idle_milliseconds;
// Stop counting
mTimer->stop();
mNotifyTimer->stop();
// Update "Remaining ..." label
mTrayIcon->setToolTip(tr("There are %1 minutes left until the next break.").arg(msec2min(mIdleRemaining)));
}
}
else
{
// Do nothing here - main timer is stopped, idle time & timer remaining duration are recorded already
// How much time remains ?
}
}
else
{
if (mIdleStart)
{
// Idle interval ended
mIdleStart.reset();
mTimer->start(mIdleRemaining);
mNotifyTimer->start(std::max(1, mIdleRemaining - 30 * 1000));
}
else
{
// Do nothing here - timer is running already
}
shiftTo(AppState::Idle);
return;
}
}
// Update tray icon
if (mTrayIcon)
{
if (mProgressTimer->isActive())
{
// Break is in effect now
mTrayIcon->setToolTip(QString());
}
else
if (mTimer->isActive())
{
auto remaining_milliseconds = mTimer->remainingTime();
auto remaining_milliseconds = mBreakStartTimer->remainingTime();
if (remaining_milliseconds < 60000)
mTrayIcon->setToolTip(tr("Less than a minute left until the next break."));
else
mTrayIcon->setToolTip(tr("There are %1 minutes left until the next break.").arg(msec2min(remaining_milliseconds)));
}
break;
}
ui->mSkipButton->setVisible(mPostponeCount > 0);
@ -350,18 +363,19 @@ void MainWindow::onLongBreakNotify()
void MainWindow::onLongBreakStart()
{
// qDebug() << "Long break starts for " << secondsToText(mAppConfig.longbreak_postpone_interval);
mTimer->stop();
mNotifyTimer->stop();
mBreakStartTimer->stop();
mBreakNotifyTimer->stop();
// Reset idle counter
mIdleStart.reset();
mLastIdleMilliseconds = 0;
// Show the button "Postpone"
ui->mPostponeButton->setText(tr("Postpone for ") + secondsToText(mAppConfig.longbreak_postpone_interval));
// Show the screen
showMe();
// Save start time
mBreakStartTime = std::chrono::steady_clock::now();
// Start progress bar
@ -387,10 +401,10 @@ void MainWindow::onLongBreakEnd()
mProgressTimer->stop();
// Start new timer
mTimer->stop();
mTimer->start(std::chrono::seconds(mAppConfig.longbreak_interval));
mNotifyTimer->stop();
mNotifyTimer->start(std::chrono::seconds(mAppConfig.longbreak_interval - 30));
mBreakStartTimer->stop();
mBreakStartTimer->start(std::chrono::seconds(mAppConfig.longbreak_interval));
mBreakNotifyTimer->stop();
mBreakNotifyTimer->start(std::chrono::seconds(mAppConfig.longbreak_interval - 30));
// Refresh UI
onUpdateUI();
@ -418,10 +432,10 @@ void MainWindow::onLongBreakPostpone()
ui->mProgressBar->setValue(0);
// Start timer again
mTimer->stop();
mTimer->start(std::chrono::seconds(mAppConfig.longbreak_postpone_interval));
mNotifyTimer->stop();
mNotifyTimer->start(std::chrono::seconds(mAppConfig.longbreak_postpone_interval - 30));
mBreakStartTimer->stop();
mBreakStartTimer->start(std::chrono::seconds(mAppConfig.longbreak_postpone_interval));
mBreakNotifyTimer->stop();
mBreakNotifyTimer->start(std::chrono::seconds(mAppConfig.longbreak_postpone_interval - 30));
// Refresh UI
onUpdateUI();
@ -443,7 +457,7 @@ void MainWindow::onProgress()
if (percents > 100)
{
mProgressTimer->stop();
onLongBreakEnd();
shiftTo(AppState::Counting);
}
else
showMe();
@ -451,13 +465,40 @@ void MainWindow::onProgress()
void MainWindow::onNextBreak()
{
mIdleRemaining = 0;
shiftTo(AppState::Break);
}
void MainWindow::onIdleStart()
{
if (mState != AppState::Counting)
return;
// Detected idle
// Timestamp when idle started
mIdleStart = std::chrono::steady_clock::now();
// How much working time remains
mWorkInterval = mBreakStartTimer->remainingTime();
// Stop main & notify timers
mBreakStartTimer->stop();
mBreakNotifyTimer->stop();
}
void MainWindow::onIdleEnd()
{
if (mState != AppState::Idle)
return;
mIdleStart.reset();
mTimer->stop();
mNotifyTimer->stop();
// Update timer(s) duration
mBreakStartTimer->setInterval(mWorkInterval);
if (mWorkInterval > INTERVAL_NOTIFICATION)
mBreakNotifyTimer->setInterval(mWorkInterval - INTERVAL_NOTIFICATION);
onLongBreakStart();
mWorkInterval = mAppConfig.longbreak_interval * 1000;
shiftTo(AppState::Counting);
}
void MainWindow::onSettings()

View File

@ -40,16 +40,16 @@ public:
private:
Ui::MainWindow *ui;
QTimer* mMainTimer;
QTimer* mNotifyTimer;
QTimer* mUpdateUITimer;
QTimer* mProgressTimer;
QTimer* mBreakStartTimer; // Main timer - triggers when break occurs
QTimer* mBreakNotifyTimer; // Timer to show notification from system tray
QTimer* mUpdateUITimer; // Update UI timer - triggers every minute to update UI and checks for idle
QTimer* mProgressTimer; // Break progress timer
QSystemTrayIcon* mTrayIcon;
SettingsDialog* mSettingsDialog;
std::chrono::steady_clock::time_point mBreakStartTime;
// How much seconds remains for main break
// How much milliseconds remains for main break
int mWorkInterval = -1;
app_settings::config mAppConfig;
@ -84,6 +84,8 @@ public slots:
void onLongBreakStart();
void onLongBreakPostpone();
void onLongBreakEnd();
void onIdleStart();
void onIdleEnd();
void onProgress();
void onNextBreak();