1 /*
2 SPDX-FileCopyrightText: 2009-2021 Graeme Gott <graeme@gottcode.org>
3
4 SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7 #include "clock.h"
8
9 #include <QPainter>
10 #include <QPainterPath>
11 #include <QSettings>
12 #include <QTime>
13 #include <QTimer>
14
15 #include <algorithm>
16
17 //-----------------------------------------------------------------------------
18
19 /**
20 * @brief The Clock::Timer class defines how the timer updates for gameplay.
21 */
22 class Clock::Timer
23 {
24 public:
25 /**
26 * Constructs a timer instance.
27 */
28 Timer();
29
30 /**
31 * Destroys the timer.
32 */
33 virtual ~Timer();
34
35 /**
36 * Updates timer based on adding a correctly spelled word.
37 * @param score how much the word is worth
38 * @return @c true if the time remaining has changed by adding the word
39 */
40 virtual bool addWord(int score) = 0;
41
42 /**
43 * Updates timer based on adding an incorrectly spelled word.
44 * @param score how much the word is worth
45 * @return @c true if the time remaining has changed by adding the word
46 */
47 virtual bool addIncorrectWord(int score);
48
49 /**
50 * @return the current display color of the timer
51 */
52 virtual QColor color();
53
54 /**
55 * @return whether the timer has reached its end
56 */
57 virtual bool isFinished();
58
59 /**
60 * Resets the timer to its default amount.
61 */
62 virtual void start() = 0;
63
64 /**
65 * Stops the timer and resets to @c 0.
66 */
67 virtual void stop();
68
69 /**
70 * @return the timer unique ID
71 */
72 virtual int type() const = 0;
73
74 /**
75 * Updates the amount of time remaining and the display string of the timer.
76 * @return the current display string
77 */
78 virtual QString update();
79
80 /**
81 * @return how full the timer should be out of a maximum of 180
82 */
83 virtual int width() const;
84
85 /**
86 * Loads the timer values.
87 * @param game where to load the timer values from
88 */
89 void load(const QSettings& game);
90
91 /**
92 * Stores the timer values.
93 * @param game where to save the timer values
94 */
95 void save(QSettings& game);
96
97 protected:
98 int m_time; /**< The time remaining on the clock */
99
100 private:
101 /**
102 * Implementation specific handling of loading timer details.
103 * @param game where to load the timer values from
104 */
105 virtual void loadDetails(const QSettings& game);
106
107 /**
108 * Implementation specific handling of saving timer details.
109 * @param game where to save the timer values
110 */
111 virtual void saveDetails(QSettings& game);
112 };
113
Timer()114 Clock::Timer::Timer()
115 : m_time(0)
116 {
117 }
118
~Timer()119 Clock::Timer::~Timer()
120 {
121 }
122
addIncorrectWord(int)123 bool Clock::Timer::addIncorrectWord(int)
124 {
125 return false;
126 }
127
color()128 QColor Clock::Timer::color()
129 {
130 QColor c;
131 if (m_time > 20) {
132 c = QColor(0x37, 0xa4, 0x2b);
133 } else if (m_time > 10) {
134 c = QColor(0xff, 0xaa, 0);
135 } else if (m_time > 0) {
136 c = QColor(0xbf, 0, 0);
137 } else {
138 c = QColor(0x37, 0x37, 0x37);
139 }
140 return c;
141 }
142
isFinished()143 bool Clock::Timer::isFinished()
144 {
145 return m_time == 0;
146 }
147
stop()148 void Clock::Timer::stop()
149 {
150 m_time = 0;
151 }
152
update()153 QString Clock::Timer::update()
154 {
155 m_time = std::max(m_time - 1, 0);
156 return QTime(0, 0, 0).addSecs(m_time).toString(tr("m:ss"));
157 }
158
width() const159 int Clock::Timer::width() const
160 {
161 return m_time;
162 }
163
load(const QSettings & game)164 void Clock::Timer::load(const QSettings& game)
165 {
166 m_time = std::max(0, game.value("TimerDetails/Time", m_time).toInt()) + 1;
167 loadDetails(game);
168 }
169
save(QSettings & game)170 void Clock::Timer::save(QSettings& game)
171 {
172 if (!isFinished()) {
173 game.setValue("TimerDetails/Time", m_time);
174 saveDetails(game);
175 } else {
176 stop();
177 }
178 }
179
loadDetails(const QSettings &)180 void Clock::Timer::loadDetails(const QSettings&)
181 {
182 }
183
saveDetails(QSettings &)184 void Clock::Timer::saveDetails(QSettings&)
185 {
186 }
187
188 //-----------------------------------------------------------------------------
189
190 /**
191 * @brief The Clock::AllotmentTimer class is a timer that allows 30 guesses.
192 */
193 class Clock::AllotmentTimer : public Clock::Timer
194 {
195 public:
196 int type() const override;
197
198 /**
199 * Reduce guesses remaining when a correct guess is made.
200 * @return @c true to update display
201 */
202 bool addWord(int) override;
203
204 /**
205 * Reduce guesses remaining when an incorrect guess is made.
206 * @return @c true to update display
207 */
208 bool addIncorrectWord(int) override;
209
210 /**
211 * Start with 30 guesses.
212 */
213 void start() override;
214
215 /**
216 * @return the time as "X guesses".
217 */
218 QString update() override;
219
220 /**
221 * @return the time multiplied by 6 to make it fill the timer display
222 */
223 int width() const override;
224
225 private:
226 void loadDetails(const QSettings&) override;
227 };
228
addWord(int)229 bool Clock::AllotmentTimer::addWord(int)
230 {
231 m_time = std::max(m_time - 1, 0);
232 return true;
233 }
234
addIncorrectWord(int)235 bool Clock::AllotmentTimer::addIncorrectWord(int)
236 {
237 m_time = std::max(m_time - 1, 0);
238 return true;
239 }
240
start()241 void Clock::AllotmentTimer::start()
242 {
243 m_time = 30;
244 }
245
type() const246 int Clock::AllotmentTimer::type() const
247 {
248 return Clock::Allotment;
249 }
250
update()251 QString Clock::AllotmentTimer::update()
252 {
253 return tr("%n guesses(s)", "", m_time);
254 }
255
width() const256 int Clock::AllotmentTimer::width() const
257 {
258 return m_time * 6;
259 }
260
loadDetails(const QSettings &)261 void Clock::AllotmentTimer::loadDetails(const QSettings&)
262 {
263 m_time--;
264 }
265
266 //-----------------------------------------------------------------------------
267
268 /**
269 * @brief The Clock::ClassicTimer class is a timer that only counts down from 3 minutes.
270 */
271 class Clock::ClassicTimer : public Clock::Timer
272 {
273 public:
274 int type() const override;
275
276 /**
277 * Ignore as guesses do not impact time remaining.
278 * @return always returns @c false
279 */
280 bool addWord(int) override;
281
282 /**
283 * Start with 3 minutes.
284 */
285 void start() override;
286 };
287
addWord(int)288 bool Clock::ClassicTimer::addWord(int)
289 {
290 return false;
291 }
292
start()293 void Clock::ClassicTimer::start()
294 {
295 m_time = 181;
296 }
297
type() const298 int Clock::ClassicTimer::type() const
299 {
300 return Clock::Classic;
301 }
302
303 //-----------------------------------------------------------------------------
304
305 class Clock::DisciplineTimer : public Clock::Timer
306 {
307 public:
308 int type() const override;
309
310 /**
311 * Increase time based on how much a correct guess is worth.
312 * @param score how much the word is worth
313 * @return @c true to update display
314 */
315 bool addWord(int score) override;
316
317 /**
318 * Decrease time based on how much an incorrect guess is worth.
319 * @param score how much the word is worth
320 * @return @c true to update display
321 */
322 bool addIncorrectWord(int score) override;
323
324 /**
325 * Start with 30 seconds.
326 */
327 void start() override;
328 };
329
addWord(int score)330 bool Clock::DisciplineTimer::addWord(int score)
331 {
332 m_time += score + 8;
333 return true;
334 }
335
addIncorrectWord(int score)336 bool Clock::DisciplineTimer::addIncorrectWord(int score)
337 {
338 m_time = std::max(0, m_time - (score + 8));
339 return true;
340 }
341
start()342 void Clock::DisciplineTimer::start()
343 {
344 m_time = 31;
345 }
346
type() const347 int Clock::DisciplineTimer::type() const
348 {
349 return Clock::Discipline;
350 }
351
352 //-----------------------------------------------------------------------------
353
354 /**
355 * @brief The Clock::RefillTimer class is a timer that refills on correct guesses.
356 */
357 class Clock::RefillTimer : public Clock::Timer
358 {
359 public:
360 int type() const override;
361
362 /**
363 * Refills time back to 30 seconds on a correct guess.
364 * @return @c true to update display
365 */
366 bool addWord(int) override;
367
368 /**
369 * Start with 30 seconds.
370 */
371 void start() override;
372
373 /**
374 * @return the time multiplied by 6 to make it fill the timer display
375 */
376 int width() const override;
377 };
378
addWord(int)379 bool Clock::RefillTimer::addWord(int)
380 {
381 m_time = 31;
382 return true;
383 }
384
start()385 void Clock::RefillTimer::start()
386 {
387 m_time = 31;
388 }
389
type() const390 int Clock::RefillTimer::type() const
391 {
392 return Clock::Refill;
393 }
394
width() const395 int Clock::RefillTimer::width() const
396 {
397 return m_time * 6;
398 }
399
400 //-----------------------------------------------------------------------------
401
402 /**
403 * @brief The Clock::StaminaTimer class is a timer that pauses for 5 seconds on correct guesses.
404 */
405 class Clock::StaminaTimer : public Clock::Timer
406 {
407 public:
408 int type() const override;
409
410 /**
411 * Pauses the timer on a correct guess.
412 * @return @c true to update display
413 */
414 bool addWord(int) override;
415
416 /**
417 * @return blue if the timer is paused, otherwise returns the regular timer color
418 */
419 QColor color() override;
420
421 /**
422 * Start with 45 seconds.
423 */
424 void start() override;
425
426 /**
427 * Reduces the pause if it exists.
428 * @return the pause remaining or the regular display if the time is not paused
429 */
430 QString update() override;
431
432 /**
433 * @return full 180 if paused, or time multiplied by 4 to fill timer display
434 */
435 int width() const override;
436
437 private:
438 /**
439 * Load how much pause remains.
440 * @param game where to load how much pause remains
441 */
442 void loadDetails(const QSettings& game) override;
443
444 /**
445 * Store how much pause remains.
446 * @param game where to store how much pause remains
447 */
448 void saveDetails(QSettings& game) override;
449
450 private:
451 int m_freeze; /**< how much pause is left */
452 };
453
addWord(int)454 bool Clock::StaminaTimer::addWord(int)
455 {
456 m_freeze = 6;
457 return true;
458 }
459
color()460 QColor Clock::StaminaTimer::color()
461 {
462 return (m_time && m_freeze) ? QColor(0x33, 0x89, 0xea) : Timer::color();
463 }
464
start()465 void Clock::StaminaTimer::start()
466 {
467 m_time = 46;
468 m_freeze = 0;
469 }
470
type() const471 int Clock::StaminaTimer::type() const
472 {
473 return Clock::Stamina;
474 }
475
update()476 QString Clock::StaminaTimer::update()
477 {
478 m_freeze = std::max(0, m_freeze - 1);
479 return (m_freeze) ? tr("+%1").arg(m_freeze) : Timer::update();
480 }
481
width() const482 int Clock::StaminaTimer::width() const
483 {
484 if (m_freeze) {
485 return 180;
486 } else {
487 return m_time * 4;
488 }
489 }
490
loadDetails(const QSettings & game)491 void Clock::StaminaTimer::loadDetails(const QSettings& game)
492 {
493 m_freeze = qBound(0, game.value("TimerDetails/Freeze").toInt(), 5) + 1;
494 }
495
saveDetails(QSettings & game)496 void Clock::StaminaTimer::saveDetails(QSettings& game)
497 {
498 game.setValue("TimerDetails/Freeze", m_freeze);
499 }
500
501 //-----------------------------------------------------------------------------
502
503 /**
504 * @brief The Clock::StrikeoutTimer class is a timer that stops after 3 incorrect guesses.
505 */
506 class Clock::StrikeoutTimer : public Clock::Timer
507 {
508 public:
509 int type() const override;
510
511 /**
512 * Ignore adding a correct word as guesses do not impact time remaining.
513 * @return always returns @c false
514 */
515 bool addWord(int) override;
516
517 /**
518 * Decreases amount of strikes remaining.
519 * @return @c true to update timer display
520 */
521 bool addIncorrectWord(int score) override;
522
523 /**
524 * Start with 0 incorrect guesses.
525 */
526 void start() override;
527
528 /**
529 * Resets count of incorrect guesses.
530 */
531 void stop() override;
532
533 /**
534 * @return how many guesses remain
535 */
536 QString update() override;
537
538 /**
539 * @return remaining guesses multipled by 60 to fill timer display
540 */
541 int width() const override;
542
543 private:
544 /**
545 * Load how many incorrect guesses have happened.
546 * @param game where to load the count of incorrect guesses
547 */
548 void loadDetails(const QSettings& game) override;
549
550 /**
551 * Store how many incorrect guesses have happened.
552 * @param game where to store count of incorrect guesses
553 */
554 void saveDetails(QSettings& game) override;
555
556 private:
557 int m_strikes; /**< how many incorrect guesses have been made */
558 };
559
addWord(int)560 bool Clock::StrikeoutTimer::addWord(int)
561 {
562 return false;
563 }
564
addIncorrectWord(int)565 bool Clock::StrikeoutTimer::addIncorrectWord(int)
566 {
567 m_strikes = std::min(m_strikes + 1, 3);
568 m_time = (3 - m_strikes) * 10;
569 return true;
570 }
571
start()572 void Clock::StrikeoutTimer::start()
573 {
574 m_time = 30;
575 m_strikes = 0;
576 }
577
stop()578 void Clock::StrikeoutTimer::stop()
579 {
580 m_time = 0;
581 m_strikes = 3;
582 }
583
type() const584 int Clock::StrikeoutTimer::type() const
585 {
586 return Clock::Strikeout;
587 }
588
update()589 QString Clock::StrikeoutTimer::update()
590 {
591 return tr("%n strike(s)", "", m_strikes);
592 }
593
width() const594 int Clock::StrikeoutTimer::width() const
595 {
596 return (3 - m_strikes) * 60;
597 }
598
loadDetails(const QSettings & game)599 void Clock::StrikeoutTimer::loadDetails(const QSettings& game)
600 {
601 m_strikes = qBound(0, game.value("TimerDetails/Strikes").toInt(), 3);
602 m_time = (3 - m_strikes) * 10;
603 }
604
saveDetails(QSettings & game)605 void Clock::StrikeoutTimer::saveDetails(QSettings& game)
606 {
607 game.setValue("TimerDetails/Strikes", m_strikes);
608 }
609
610 //-----------------------------------------------------------------------------
611
612 /**
613 * @brief The Clock::TangletTimer class is a timer that rewards on correct guesses.
614 */
615 class Clock::TangletTimer : public Clock::Timer
616 {
617 public:
618 int type() const override;
619
620 /**
621 * Increase time based on how much a correct guess is worth.
622 * @param score how much the word is worth
623 * @return @c true to update display
624 */
625 bool addWord(int score) override;
626
627 /**
628 * Start with 30 seconds.
629 */
630 void start() override;
631 };
632
addWord(int score)633 bool Clock::TangletTimer::addWord(int score)
634 {
635 m_time += score + 8;
636 return true;
637 }
638
start()639 void Clock::TangletTimer::start()
640 {
641 m_time = 31;
642 }
643
type() const644 int Clock::TangletTimer::type() const
645 {
646 return Clock::Tanglet;
647 }
648
649 //-----------------------------------------------------------------------------
650
651 /**
652 * @brief The Clock::UnlimitedTimer class is a timer that does nothing.
653 */
654 class Clock::UnlimitedTimer : public Clock::Timer
655 {
656 public:
657 int type() const override;
658
659 /**
660 * Ignore adding a correct word as guesses do not impact anything.
661 * @return always returns @c false
662 */
663 bool addWord(int) override;
664
665 /**
666 * Start with 180 seconds to fill display.
667 */
668 void start() override;
669
670 /**
671 * @return infinity symbol
672 */
673 QString update() override;
674 };
675
addWord(int)676 bool Clock::UnlimitedTimer::addWord(int)
677 {
678 return false;
679 }
680
start()681 void Clock::UnlimitedTimer::start()
682 {
683 m_time = 180;
684 }
685
type() const686 int Clock::UnlimitedTimer::type() const
687 {
688 return Clock::Unlimited;
689 }
690
update()691 QString Clock::UnlimitedTimer::update()
692 {
693 return "∞";
694 }
695
696 //-----------------------------------------------------------------------------
697
Clock(QWidget * parent)698 Clock::Clock(QWidget* parent)
699 : QWidget(parent)
700 , m_timer(nullptr)
701 {
702 setTimer(Tanglet);
703 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
704
705 m_update = new QTimer(this);
706 m_update->setInterval(1000);
707 connect(m_update, &QTimer::timeout, this, &Clock::updateTime);
708
709 QFont f = font();
710 f.setBold(true);
711 setFont(f);
712 }
713
714 //-----------------------------------------------------------------------------
715
~Clock()716 Clock::~Clock()
717 {
718 delete m_timer;
719 }
720
721 //-----------------------------------------------------------------------------
722
sizeHint() const723 QSize Clock::sizeHint() const
724 {
725 return QSize(186, fontMetrics().height() + 8);
726 }
727
728 //-----------------------------------------------------------------------------
729
isFinished() const730 bool Clock::isFinished() const
731 {
732 return m_timer->isFinished();
733 }
734
735 //-----------------------------------------------------------------------------
736
addWord(int score)737 void Clock::addWord(int score)
738 {
739 if (m_timer->addWord(score)) {
740 updateTime();
741 }
742 }
743
744 //-----------------------------------------------------------------------------
745
addIncorrectWord(int score)746 void Clock::addIncorrectWord(int score)
747 {
748 if (m_timer->addIncorrectWord(score)) {
749 updateTime();
750 }
751 }
752
753 //-----------------------------------------------------------------------------
754
setPaused(bool paused)755 void Clock::setPaused(bool paused)
756 {
757 if (paused) {
758 m_update->stop();
759 } else {
760 m_update->start();
761 }
762 }
763
764 //-----------------------------------------------------------------------------
765
setText(const QString & text)766 void Clock::setText(const QString& text)
767 {
768 m_text = text;
769 update();
770 }
771
772 //-----------------------------------------------------------------------------
773
start()774 void Clock::start()
775 {
776 m_timer->start();
777 updateTime();
778 }
779
780 //-----------------------------------------------------------------------------
781
stop()782 void Clock::stop()
783 {
784 m_timer->stop();
785 updateTime();
786 }
787
788 //-----------------------------------------------------------------------------
789
load(const QSettings & game)790 void Clock::load(const QSettings& game)
791 {
792 m_timer->load(game);
793 updateTime();
794 }
795
796 //-----------------------------------------------------------------------------
797
save(QSettings & game)798 void Clock::save(QSettings& game)
799 {
800 m_timer->save(game);
801 }
802
803 //-----------------------------------------------------------------------------
804
setTimer(int timer)805 void Clock::setTimer(int timer)
806 {
807 if (!m_timer || m_timer->type() != timer) {
808 delete m_timer;
809
810 switch (timer) {
811 case Classic:
812 m_timer = new ClassicTimer;
813 break;
814 case Refill:
815 m_timer = new RefillTimer;
816 break;
817 case Stamina:
818 m_timer = new StaminaTimer;
819 break;
820 case Strikeout:
821 m_timer = new StrikeoutTimer;
822 break;
823 case Allotment:
824 m_timer = new AllotmentTimer;
825 break;
826 case Discipline:
827 m_timer = new DisciplineTimer;
828 break;
829 case Unlimited:
830 m_timer = new UnlimitedTimer;
831 break;
832 case Tanglet:
833 default:
834 m_timer = new TangletTimer;
835 break;
836 }
837 }
838 }
839
840 //-----------------------------------------------------------------------------
841
timer() const842 int Clock::timer() const
843 {
844 return m_timer->type();
845 }
846
847 //-----------------------------------------------------------------------------
848
timerToString(int timer)849 QString Clock::timerToString(int timer)
850 {
851 static const QStringList timers = QStringList()
852 << tr("Tanglet")
853 << tr("Classic")
854 << tr("Refill")
855 << tr("Stamina")
856 << tr("Strikeout")
857 << tr("Allotment")
858 << tr("Discipline")
859 << tr("Unlimited");
860 return timers.at(qBound(0, timer, TotalTimers - 1));
861 }
862
863 //-----------------------------------------------------------------------------
864
timerDescription(int timer)865 QString Clock::timerDescription(int timer)
866 {
867 static const QStringList timers = QStringList()
868 << tr("Counts down from 30 seconds and increases on correct guesses.")
869 << tr("Counts down from 3 minutes.")
870 << tr("Counts down from 30 seconds and refills on correct guesses.")
871 << tr("Counts down from 45 seconds and pauses on correct guesses.")
872 << tr("Game ends after 3 incorrect guesses.")
873 << tr("Game ends after 30 guesses.")
874 << tr("Counts down from 30 seconds and increases or decreases on guesses.")
875 << tr("Game ends when all words are found.");
876 return timers.at(qBound(0, timer, TotalTimers - 1));
877 }
878
879 //-----------------------------------------------------------------------------
880
timerScoresGroup(int timer)881 QString Clock::timerScoresGroup(int timer)
882 {
883 static const QStringList timers = QStringList()
884 << QStringLiteral("Scores_Tanglet")
885 << QStringLiteral("Scores_Classic")
886 << QStringLiteral("Scores_Refill")
887 << QStringLiteral("Scores_Stamina")
888 << QStringLiteral("Scores_Strikeout")
889 << QStringLiteral("Scores_Allotment")
890 << QStringLiteral("Scores_Discipline")
891 << QStringLiteral("Scores_Unlimited");
892 return timers.at(qBound(0, timer, TotalTimers - 1));
893 }
894
895 //-----------------------------------------------------------------------------
896
paintEvent(QPaintEvent * event)897 void Clock::paintEvent(QPaintEvent* event)
898 {
899 QWidget::paintEvent(event);
900
901 QColor color = m_timer->color();
902
903 QPainter painter(this);
904 painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
905 painter.setPen(Qt::NoPen);
906
907 // Draw indent
908 QLinearGradient indent_gradient(0, 0, 0, height());
909 QColor shadow = palette().color(QPalette::Shadow);
910 indent_gradient.setColorAt(0, QColor(shadow.red(), shadow.green(), shadow.blue(), 64));
911 indent_gradient.setColorAt(1, QColor(shadow.red(), shadow.green(), shadow.blue(), 0));
912 painter.setBrush(indent_gradient);
913 painter.drawRoundedRect(rect(), 4, 4);
914
915 // Draw outside border
916 painter.setBrush(color.darker());
917 painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 3, 3);
918
919 // Draw filled background
920 QLinearGradient gradient(0, 2, 0, height() - 2);
921 gradient.setColorAt(0, color.lighter(115));
922 gradient.setColorAt(0.49, color.darker(125));
923 gradient.setColorAt(0.5, color.darker(150));
924 gradient.setColorAt(1, color.lighter(105));
925 painter.setBrush(gradient);
926 painter.drawRoundedRect(rect().adjusted(2, 2, -2, -2), 3, 3);
927
928 int x = m_timer->width();
929 int width = 180 - std::min(180, x);
930 if ((width < 180) && (width > 0)) {
931 // Draw empty space
932 painter.setBrush(QColor(255, 255, 255, 160));
933 painter.drawRoundedRect(x + 3, 2, width + 1, rect().height() - 4, 2, 2);
934
935 // Draw dividing line
936 painter.setRenderHint(QPainter::Antialiasing, false);
937 painter.setPen(color.darker(150));
938 painter.drawLine(x + 3, 2, x + 3, rect().height() - 2);
939 painter.setRenderHint(QPainter::Antialiasing, true);
940 }
941
942 // Draw text shadow
943 QPainterPath path;
944 path.addText(93 - (fontMetrics().boundingRect(m_text).width() / 2), fontMetrics().ascent() + 3, font(), m_text);
945 painter.setBrush(Qt::black);
946 painter.translate(0.6, 0.6);
947 painter.setPen(QPen(QColor(0, 0, 0, 32), 3));
948 painter.drawPath(path);
949 painter.translate(-0.2, -0.2);
950 painter.setPen(QPen(QColor(0, 0, 0, 64), 2));
951 painter.drawPath(path);
952 painter.translate(-0.2, -0.2);
953 painter.setPen(QPen(QColor(0, 0, 0, 192), 1));
954 painter.drawPath(path);
955 painter.translate(-0.2, -0.2);
956
957 // Draw text
958 painter.setPen(Qt::NoPen);
959 painter.setBrush(Qt::white);
960 painter.fillPath(path, Qt::white);
961 }
962
963 //-----------------------------------------------------------------------------
964
updateTime()965 void Clock::updateTime()
966 {
967 m_text = m_timer->update();
968 update();
969
970 if (!isFinished()) {
971 m_update->start();
972 } else {
973 m_update->stop();
974 emit finished();
975 }
976 }
977
978 //-----------------------------------------------------------------------------
979