| 1 | #include "PomodoroButton.h" | 
|---|
| 2 |  | 
|---|
| 3 | #include <GitQlientSettings.h> | 
|---|
| 4 | #include <GitBase.h> | 
|---|
| 5 | #include <PomodoroConfigDlg.h> | 
|---|
| 6 |  | 
|---|
| 7 | #include <QTime> | 
|---|
| 8 | #include <QToolButton> | 
|---|
| 9 | #include <QLabel> | 
|---|
| 10 | #include <QMenu> | 
|---|
| 11 | #include <QVBoxLayout> | 
|---|
| 12 | #include <QMouseEvent> | 
|---|
| 13 | #include <QTimer> | 
|---|
| 14 | #include <QMessageBox> | 
|---|
| 15 | #include <QStyle> | 
|---|
| 16 |  | 
|---|
| 17 | PomodoroButton::PomodoroButton(const QSharedPointer<GitBase> &git, QWidget *parent) | 
|---|
| 18 | : QFrame(parent) | 
|---|
| 19 | , mGit(git) | 
|---|
| 20 | , mButton(new QToolButton()) | 
|---|
| 21 | , mArrow(new QToolButton()) | 
|---|
| 22 | , mCounter(new QLabel()) | 
|---|
| 23 | , mTimer(new QTimer(this)) | 
|---|
| 24 | { | 
|---|
| 25 | setContentsMargins(0, 0, 0, 0); | 
|---|
| 26 | setToolTip(tr( "Pomodoro")); | 
|---|
| 27 |  | 
|---|
| 28 | const auto  = new QMenu(mButton); | 
|---|
| 29 | menu->installEventFilter(this); | 
|---|
| 30 | mConfigAction = menu->addAction(tr( "Options...")); | 
|---|
| 31 | connect(mConfigAction, &QAction::triggered, this, &PomodoroButton::showConfig); | 
|---|
| 32 |  | 
|---|
| 33 | mButton->setIcon(QIcon( ":/icons/pomodoro")); | 
|---|
| 34 | mButton->setIconSize(QSize(22, 22)); | 
|---|
| 35 | mButton->setToolButtonStyle(Qt::ToolButtonIconOnly); | 
|---|
| 36 | mButton->setObjectName( "ToolButtonAboveMenu"); | 
|---|
| 37 | connect(mButton, &QToolButton::clicked, this, &PomodoroButton::onClick); | 
|---|
| 38 |  | 
|---|
| 39 | mArrow->setObjectName( "Arrow"); | 
|---|
| 40 | mArrow->setIcon(QIcon( ":/icons/arrow_down")); | 
|---|
| 41 | mArrow->setIconSize(QSize(10, 10)); | 
|---|
| 42 | mArrow->setToolButtonStyle(Qt::ToolButtonIconOnly); | 
|---|
| 43 | mArrow->setToolTip(tr( "Options")); | 
|---|
| 44 | mArrow->setPopupMode(QToolButton::InstantPopup); | 
|---|
| 45 | mArrow->setMenu(menu); | 
|---|
| 46 | mArrow->setFixedWidth(10); | 
|---|
| 47 | mArrow->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); | 
|---|
| 48 |  | 
|---|
| 49 | GitQlientSettings settings(mGit->getGitDir()); | 
|---|
| 50 | const auto durationMins = settings.localValue( "Pomodoro/Duration", 25).toInt(); | 
|---|
| 51 | mDurationTime = QTime(0, durationMins, 0); | 
|---|
| 52 | mCounter->setText(mDurationTime.toString( "mm:ss")); | 
|---|
| 53 |  | 
|---|
| 54 | const auto breakMins = settings.localValue( "Pomodoro/Break", 5).toInt(); | 
|---|
| 55 | mBreakTime = QTime(0, breakMins, 0); | 
|---|
| 56 |  | 
|---|
| 57 | const auto longBreakMins = settings.localValue( "Pomodoro/LongBreak", 15).toInt(); | 
|---|
| 58 | mLongBreakTime = QTime(0, longBreakMins, 0); | 
|---|
| 59 |  | 
|---|
| 60 | mBigBreakCount = settings.localValue( "Pomodoro/LongBreakTrigger", 4).toInt(); | 
|---|
| 61 |  | 
|---|
| 62 | mStopResets = settings.localValue( "Pomodoro/StopResets", true).toBool(); | 
|---|
| 63 |  | 
|---|
| 64 | mTimer->setInterval(1000); | 
|---|
| 65 | connect(mTimer, &QTimer::timeout, this, &PomodoroButton::onTimeout); | 
|---|
| 66 |  | 
|---|
| 67 | const auto layout = new QGridLayout(this); | 
|---|
| 68 | layout->setContentsMargins(QMargins()); | 
|---|
| 69 | layout->setSpacing(0); | 
|---|
| 70 | layout->addWidget(mButton, 0, 0); | 
|---|
| 71 | layout->addWidget(mCounter, 1, 0); | 
|---|
| 72 | layout->addWidget(mArrow, 0, 1, 2, 1); | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 | void PomodoroButton::setText(const QString &text) | 
|---|
| 76 | { | 
|---|
| 77 | mCounter->setText(text); | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | void PomodoroButton::updateCounters() | 
|---|
| 81 | { | 
|---|
| 82 | mState = State::OnHold; | 
|---|
| 83 | mTimer->stop(); | 
|---|
| 84 | mButton->setIcon(QIcon( ":/icons/pomodoro")); | 
|---|
| 85 |  | 
|---|
| 86 | GitQlientSettings settings(mGit->getGitDir()); | 
|---|
| 87 |  | 
|---|
| 88 | const auto durationMins = settings.localValue( "Pomodoro/Duration", 25).toInt(); | 
|---|
| 89 | mDurationTime = QTime(0, durationMins, 0); | 
|---|
| 90 | mCounter->setText(mDurationTime.toString( "mm:ss")); | 
|---|
| 91 |  | 
|---|
| 92 | const auto breakMins = settings.localValue( "Pomodoro/Break", 5).toInt(); | 
|---|
| 93 | mBreakTime = QTime(0, breakMins, 0); | 
|---|
| 94 |  | 
|---|
| 95 | const auto longBreakMins = settings.localValue( "Pomodoro/LongBreak", 15).toInt(); | 
|---|
| 96 | mLongBreakTime = QTime(0, longBreakMins, 0); | 
|---|
| 97 |  | 
|---|
| 98 | const auto longBreakTriggerCount = settings.localValue( "Pomodoro/LongBreakTrigger", 4).toInt(); | 
|---|
| 99 |  | 
|---|
| 100 | mStopResets = settings.localValue( "Pomodoro/StopResets", true).toBool(); | 
|---|
| 101 |  | 
|---|
| 102 | if (longBreakTriggerCount < mBigBreakOriginalValue) | 
|---|
| 103 | { | 
|---|
| 104 | mBigBreakOriginalValue = longBreakTriggerCount; | 
|---|
| 105 | mBigBreakCount = longBreakTriggerCount; | 
|---|
| 106 | } | 
|---|
| 107 | else | 
|---|
| 108 | { | 
|---|
| 109 | mBigBreakCount = longBreakTriggerCount - (mBigBreakOriginalValue - mBigBreakCount); | 
|---|
| 110 | mBigBreakOriginalValue = longBreakTriggerCount; | 
|---|
| 111 | } | 
|---|
| 112 | } | 
|---|
| 113 |  | 
|---|
| 114 | void PomodoroButton::setRunningMode() | 
|---|
| 115 | { | 
|---|
| 116 | style()->unpolish(this); | 
|---|
| 117 | setProperty( "checked", true); | 
|---|
| 118 | style()->polish(this); | 
|---|
| 119 | mState = State::Running; | 
|---|
| 120 | mTimer->start(); | 
|---|
| 121 | mButton->setIcon(QIcon( ":/icons/pomodoro_running")); | 
|---|
| 122 | --mBigBreakCount; | 
|---|
| 123 | } | 
|---|
| 124 |  | 
|---|
| 125 | void PomodoroButton::mousePressEvent(QMouseEvent *e) | 
|---|
| 126 | { | 
|---|
| 127 | if (isEnabled()) | 
|---|
| 128 | mPressed = true; | 
|---|
| 129 |  | 
|---|
| 130 | QFrame::mousePressEvent(e); | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | void PomodoroButton::mouseReleaseEvent(QMouseEvent *e) | 
|---|
| 134 | { | 
|---|
| 135 | if (isEnabled() && mPressed) | 
|---|
| 136 | { | 
|---|
| 137 | onClick(); | 
|---|
| 138 |  | 
|---|
| 139 | emit clicked(); | 
|---|
| 140 | } | 
|---|
| 141 |  | 
|---|
| 142 | QFrame::mouseReleaseEvent(e); | 
|---|
| 143 | } | 
|---|
| 144 |  | 
|---|
| 145 | bool PomodoroButton::eventFilter(QObject *obj, QEvent *event) | 
|---|
| 146 | { | 
|---|
| 147 | if (const auto  = qobject_cast<QMenu *>(obj); menu && event->type() == QEvent::Show) | 
|---|
| 148 | { | 
|---|
| 149 | auto localPos = mButton->pos(); | 
|---|
| 150 | localPos.setX(localPos.x()); | 
|---|
| 151 | auto pos = mapToGlobal(localPos); | 
|---|
| 152 | menu->show(); | 
|---|
| 153 | pos.setY(pos.y() + height()); | 
|---|
| 154 | menu->move(pos); | 
|---|
| 155 |  | 
|---|
| 156 | return true; | 
|---|
| 157 | } | 
|---|
| 158 |  | 
|---|
| 159 | return false; | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|
| 162 | void PomodoroButton::onTimeout() | 
|---|
| 163 | { | 
|---|
| 164 | if (mState == State::Running) | 
|---|
| 165 | onRunningMode(); | 
|---|
| 166 | else if (mState == State::InBreakRunning) | 
|---|
| 167 | onBreakingMode(); | 
|---|
| 168 | else if (mState == State::InLongBreakRunning) | 
|---|
| 169 | onLongBreakingMode(); | 
|---|
| 170 | } | 
|---|
| 171 |  | 
|---|
| 172 | void PomodoroButton::onClick() | 
|---|
| 173 | { | 
|---|
| 174 | switch (mState) | 
|---|
| 175 | { | 
|---|
| 176 | case State::OnHold: | 
|---|
| 177 | case State::Finished: | 
|---|
| 178 | setRunningMode(); | 
|---|
| 179 | break; | 
|---|
| 180 | case State::InBreak: | 
|---|
| 181 | style()->unpolish(this); | 
|---|
| 182 | setProperty( "checked", true); | 
|---|
| 183 | style()->polish(this); | 
|---|
| 184 | mState = State::InBreakRunning; | 
|---|
| 185 | mTimer->start(); | 
|---|
| 186 | break; | 
|---|
| 187 | case State::InLongBreak: | 
|---|
| 188 | style()->unpolish(this); | 
|---|
| 189 | setProperty( "checked", true); | 
|---|
| 190 | style()->polish(this); | 
|---|
| 191 | mState = State::InLongBreakRunning; | 
|---|
| 192 | mTimer->start(); | 
|---|
| 193 | break; | 
|---|
| 194 | case State::InBreakRunning: | 
|---|
| 195 | case State::InLongBreakRunning: | 
|---|
| 196 | setRunningMode(); | 
|---|
| 197 | break; | 
|---|
| 198 | case State::Running: | 
|---|
| 199 | style()->unpolish(this); | 
|---|
| 200 | setProperty( "checked", false); | 
|---|
| 201 | style()->polish(this); | 
|---|
| 202 | mState = State::OnHold; | 
|---|
| 203 | mTimer->stop(); | 
|---|
| 204 | mButton->setIcon(QIcon( ":/icons/pomodoro")); | 
|---|
| 205 |  | 
|---|
| 206 | if (mStopResets) | 
|---|
| 207 | { | 
|---|
| 208 | GitQlientSettings settings(mGit->getGitDir()); | 
|---|
| 209 | const auto durationMins = settings.localValue( "Pomodoro/Duration", 25).toInt(); | 
|---|
| 210 | mDurationTime = QTime(0, durationMins, 0); | 
|---|
| 211 | mCounter->setText(mDurationTime.toString( "mm:ss")); | 
|---|
| 212 | } | 
|---|
| 213 |  | 
|---|
| 214 | ++mBigBreakCount; | 
|---|
| 215 | break; | 
|---|
| 216 | } | 
|---|
| 217 | } | 
|---|
| 218 |  | 
|---|
| 219 | void PomodoroButton::onRunningMode() | 
|---|
| 220 | { | 
|---|
| 221 | mDurationTime = mDurationTime.addSecs(-1); | 
|---|
| 222 | mCounter->setText(mDurationTime.toString( "mm:ss")); | 
|---|
| 223 |  | 
|---|
| 224 | if (mDurationTime == QTime(0, 0, 0)) | 
|---|
| 225 | { | 
|---|
| 226 | mTimer->stop(); | 
|---|
| 227 |  | 
|---|
| 228 | GitQlientSettings settings(mGit->getGitDir()); | 
|---|
| 229 | const auto durationMins = settings.localValue( "Pomodoro/Duration", 25).toInt(); | 
|---|
| 230 | mDurationTime = QTime(0, durationMins, 0); | 
|---|
| 231 |  | 
|---|
| 232 | mButton->setIcon(QIcon( ":/icons/pomodoro_timeout")); | 
|---|
| 233 |  | 
|---|
| 234 | const auto breakMins = settings.localValue( "Pomodoro/Break", 5).toInt(); | 
|---|
| 235 | mBreakTime = QTime(0, breakMins, 0); | 
|---|
| 236 |  | 
|---|
| 237 | const auto longBreakMins = settings.localValue( "Pomodoro/LongBreak", 15).toInt(); | 
|---|
| 238 | mLongBreakTime = QTime(0, longBreakMins, 0); | 
|---|
| 239 |  | 
|---|
| 240 | if (mBigBreakCount <= 0) | 
|---|
| 241 | { | 
|---|
| 242 | mBigBreakCount = settings.localValue( "Pomodoro/LongBreakTrigger", 4).toInt(); | 
|---|
| 243 |  | 
|---|
| 244 | mCounter->setText(mLongBreakTime.toString( "mm:ss")); | 
|---|
| 245 | } | 
|---|
| 246 | else | 
|---|
| 247 | mCounter->setText(mBreakTime.toString( "mm:ss")); | 
|---|
| 248 |  | 
|---|
| 249 | if (QMessageBox::question(this, tr( "Time for a break!"), tr( "It's time to do a break. Are you ready?")) | 
|---|
| 250 | == QMessageBox::Yes) | 
|---|
| 251 | { | 
|---|
| 252 | if (mBigBreakCount == 0) | 
|---|
| 253 | mState = State::InLongBreakRunning; | 
|---|
| 254 | else | 
|---|
| 255 | mState = State::InBreakRunning; | 
|---|
| 256 |  | 
|---|
| 257 | mTimer->start(); | 
|---|
| 258 | } | 
|---|
| 259 | else | 
|---|
| 260 | { | 
|---|
| 261 | style()->unpolish(this); | 
|---|
| 262 | setProperty( "checked", false); | 
|---|
| 263 | style()->polish(this); | 
|---|
| 264 |  | 
|---|
| 265 | if (mBigBreakCount == 0) | 
|---|
| 266 | mState = State::InLongBreak; | 
|---|
| 267 | else | 
|---|
| 268 | mState = State::InBreak; | 
|---|
| 269 | } | 
|---|
| 270 | } | 
|---|
| 271 | } | 
|---|
| 272 |  | 
|---|
| 273 | void PomodoroButton::onBreakingMode() | 
|---|
| 274 | { | 
|---|
| 275 | mBreakTime = mBreakTime.addSecs(-1); | 
|---|
| 276 | mCounter->setText(mBreakTime.toString( "mm:ss")); | 
|---|
| 277 |  | 
|---|
| 278 | if (mBreakTime == QTime(0, 0, 0)) | 
|---|
| 279 | { | 
|---|
| 280 | mTimer->stop(); | 
|---|
| 281 |  | 
|---|
| 282 | GitQlientSettings settings(mGit->getGitDir()); | 
|---|
| 283 | const auto breakMins = settings.localValue( "Pomodoro/Break", 5).toInt(); | 
|---|
| 284 | mBreakTime = QTime(0, breakMins, 0); | 
|---|
| 285 |  | 
|---|
| 286 | mState = State::Finished; | 
|---|
| 287 |  | 
|---|
| 288 | const auto answer | 
|---|
| 289 | = QMessageBox::question(this, tr( "Time to work!"), tr( "It's time to go back to work. Are you ready?")); | 
|---|
| 290 |  | 
|---|
| 291 | if (answer == QMessageBox::Yes) | 
|---|
| 292 | { | 
|---|
| 293 | mCounter->setText(mDurationTime.toString( "mm:ss")); | 
|---|
| 294 | setRunningMode(); | 
|---|
| 295 | } | 
|---|
| 296 | else | 
|---|
| 297 | { | 
|---|
| 298 | style()->unpolish(this); | 
|---|
| 299 | setProperty( "checked", false); | 
|---|
| 300 | style()->polish(this); | 
|---|
| 301 | } | 
|---|
| 302 | } | 
|---|
| 303 | } | 
|---|
| 304 |  | 
|---|
| 305 | void PomodoroButton::onLongBreakingMode() | 
|---|
| 306 | { | 
|---|
| 307 | mLongBreakTime = mLongBreakTime.addSecs(-1); | 
|---|
| 308 | mCounter->setText(mLongBreakTime.toString( "mm:ss")); | 
|---|
| 309 |  | 
|---|
| 310 | if (mLongBreakTime == QTime(0, 0, 0)) | 
|---|
| 311 | { | 
|---|
| 312 | mTimer->stop(); | 
|---|
| 313 |  | 
|---|
| 314 | GitQlientSettings settings(mGit->getGitDir()); | 
|---|
| 315 | const auto breakMins = settings.localValue( "Pomodoro/LongBreak", 15).toInt(); | 
|---|
| 316 | mLongBreakTime = QTime(0, breakMins, 0); | 
|---|
| 317 |  | 
|---|
| 318 | mState = State::Finished; | 
|---|
| 319 |  | 
|---|
| 320 | const auto answer | 
|---|
| 321 | = QMessageBox::question(this, tr( "Time to work!"), tr( "It's time to go back to work. Are you ready?")); | 
|---|
| 322 |  | 
|---|
| 323 | if (answer == QMessageBox::Yes) | 
|---|
| 324 | { | 
|---|
| 325 | mCounter->setText(mDurationTime.toString( "mm:ss")); | 
|---|
| 326 | setRunningMode(); | 
|---|
| 327 | } | 
|---|
| 328 | else | 
|---|
| 329 | { | 
|---|
| 330 | style()->unpolish(this); | 
|---|
| 331 | setProperty( "checked", false); | 
|---|
| 332 | style()->polish(this); | 
|---|
| 333 | } | 
|---|
| 334 | } | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|
| 337 | void PomodoroButton::showConfig() | 
|---|
| 338 | { | 
|---|
| 339 | const auto dlg = new PomodoroConfigDlg(mGit, this); | 
|---|
| 340 |  | 
|---|
| 341 | connect(dlg, &PomodoroConfigDlg::finished, this, [this](int result) { | 
|---|
| 342 | if (result == QDialog::Accepted) | 
|---|
| 343 | updateCounters(); | 
|---|
| 344 | }); | 
|---|
| 345 |  | 
|---|
| 346 | dlg->open(); | 
|---|
| 347 | } | 
|---|
| 348 |  | 
|---|