I am currently learning how to use PDcurses and decided to make a clock with it. **NOTE: this Watch that uses the abstract factory design pattern is not the same as this question!!. This question is a rework of the previous.
I settled on abstract factory design pattern because there are two families of objects that can be created: a Date family(segmented + digital) and a Time family(segmented + digital), although i still have doubts that just making free functions such as:
void printSegmentedTime(WINDOW*);
void printSegmentedDate(WINDOW*);
void printDigitalTime(WINDOW*);
void printDigitalDate(WINDOW*);
would be a better choice. Any thoughts on what choice would be a better approach?
clock.h:
#ifndef CLOCK_H
#define CLOCK_H
#include "curses.h"
#include <memory>
namespace curses_clock
{
class AbstractView
{
public:
virtual void printView() = 0;
explicit AbstractView(WINDOW* view_win)
: view_win_{ view_win } {}
virtual ~AbstractView() { delwin(view_win_); }
protected:
WINDOW* view_win_;
};
class SegmentedTime final : public AbstractView
{
public:
virtual void printView() override;
explicit SegmentedTime(WINDOW* segmented_win)
: AbstractView{ segmented_win } {}
};
class SegmentedDate final : public AbstractView
{
public:
virtual void printView() override;
explicit SegmentedDate(WINDOW* segmented_win)
: AbstractView{ segmented_win } {}
};
class DigitalTime final : public AbstractView
{
public:
virtual void printView() override;
explicit DigitalTime(WINDOW* digital_win)
: AbstractView{ digital_win } {}
};
class DigitalDate final : public AbstractView
{
public:
virtual void printView() override;
explicit DigitalDate(WINDOW* digital_win)
: AbstractView{ digital_win } {}
};
class ViewFactoryInterface
{
public:
virtual std::unique_ptr<AbstractView> createSegmentedView(int x, int y) = 0;
virtual std::unique_ptr<AbstractView> createDigitalView(int x, int y) = 0;
virtual ~ViewFactoryInterface() = default;
};
class TimeFactory final : public ViewFactoryInterface
{
public:
virtual std::unique_ptr<AbstractView> createSegmentedView(int x, int y) override;
virtual std::unique_ptr<AbstractView> createDigitalView(int x, int y) override;
};
class DateFactory final : public ViewFactoryInterface
{
public:
virtual std::unique_ptr<AbstractView> createSegmentedView(int x, int y) override;
virtual std::unique_ptr<AbstractView> createDigitalView(int x, int y) override;
};
}
#endif
clock.cpp:
#include "clock.h"
#include <sstream>
#include <iomanip>
#include <string>
#include <chrono>
#include <ctime>
namespace
{
using SysClock = std::chrono::system_clock;
constexpr int SEGMENTED_DATE_WIDTH{ 43 };
constexpr int SEGMENTED_TIME_WIDTH{ 33 };
constexpr int SECOND_SEPARATOR_X { 19 };
constexpr int DIGITAL_DATE_WIDTH { 10 };
constexpr int FIRST_SEPARATOR_X { 9 };
constexpr int DIGITAL_TIME_WIDTH { 8 };
constexpr int SEGMENTED_HEIGHT { 5 };
constexpr int DIGIT_PADDING { 5 };
constexpr int DIGITAL_HEIGHT { 1 };
struct MDY
{
int month_;
int day_;
int year_;
};
struct HMS
{
int hours_;
int minutes_;
int seconds_;
};
std::tm getTimeDate()
{
SysClock::time_point now = SysClock::now();
time_t raw_time = SysClock::to_time_t(now);
std::tm time_info;
localtime_s(&time_info, &raw_time);
return time_info;
}
HMS toHMS(const std::tm& time_data)
{
int hours{ time_data.tm_hour };
int minutes{ time_data.tm_min };
int seconds{ time_data.tm_sec };
return{ hours, minutes, seconds };
}
MDY toMDY(const std::tm& time_data)
{
int month = time_data.tm_mon + 1;
int day = time_data.tm_mday;
int year = time_data.tm_year + 1900;
return{ month, day, year };
}
std::string toStr(HMS hms) {
std::stringstream ss;
ss << std::setfill('0') << std::setw(2) << hms.hours_ << ':'
<< std::setw(2) << hms.minutes_ << ':'
<< std::setw(2) << hms.seconds_;
return ss.str();
}
std::string toStr(MDY mdy) {
std::stringstream ss;
ss << std::setfill('0') << std::setw(2) << mdy.month_ << '.'
<< std::setw(2) << mdy.day_ << '.'
<< mdy.year_;
return ss.str();
}
void printDigit(WINDOW* win, int y, int x, int num) {
static const char* digits[]{
{ " -- "
"| |"
" "
"| |"
" -- " },
{ " "
" |"
" "
" |"
" " },
{ " -- "
" |"
" -- "
"| "
" -- " },
{ " -- "
" |"
" -- "
" |"
" -- " },
{ " "
"| |"
" -- "
" |"
" " },
{ " -- "
"| "
" -- "
" |"
" -- " },
{ " -- "
"| "
" -- "
"| |"
" -- " },
{ " -- "
" |"
" "
" |"
" " },
{ " -- "
"| |"
" -- "
"| |"
" -- " },
{ " -- "
"| |"
" -- "
" |"
" -- " }
};
if (num < 0 || num > 9)
throw std::runtime_error("Digit out of range, must be (0 < digit < 9)");
for (auto line{ digits[num] }; *line; line += 4) {
for (auto ch{ line }; ch < line + 4; ++ch)
mvwaddch(win, y, x++, *ch);
++y; x -= 4;
}
}
}
void curses_clock::SegmentedTime::printView()
{
std::string hms{ toStr(toHMS(getTimeDate())) };
int digits[6]{
hms[0] - '0', hms[1] - '0',
hms[3] - '0', hms[4] - '0',
hms[6] - '0', hms[7] - '0'
};
int xpos{};
for (const auto& digit : digits)
printDigit(view_win_, 0, DIGIT_PADDING * xpos++, digit);
for (auto x : { FIRST_SEPARATOR_X, SECOND_SEPARATOR_X })
for (auto y : { 1, 3 })
mvwaddch(view_win_, y, x, '*');
wrefresh(view_win_);
}
void curses_clock::SegmentedDate::printView()
{
std::string mdy{ toStr(toMDY(getTimeDate())) };
int digits[8]{
mdy[0] - '0', mdy[1] - '0',
mdy[3] - '0', mdy[4] - '0',
mdy[6] - '0', mdy[7] - '0',
mdy[8] - '0', mdy[9] - '0'
};
int xpos{};
for (const auto& digit : digits)
printDigit(view_win_, 0, DIGIT_PADDING * xpos++, digit);
for (auto x : { FIRST_SEPARATOR_X, SECOND_SEPARATOR_X })
mvwaddch(view_win_, 4, x, '*');
wrefresh(view_win_);
}
void curses_clock::DigitalTime::printView()
{
mvwprintw(view_win_, 0, 0, toStr(toHMS(getTimeDate())).c_str());
wrefresh(view_win_);
}
void curses_clock::DigitalDate::printView()
{
mvwprintw(view_win_, 0, 0, toStr(toMDY(getTimeDate())).c_str());
wrefresh(view_win_);
}
std::unique_ptr<curses_clock::AbstractView> curses_clock::TimeFactory::createSegmentedView(int x, int y)
{
return std::make_unique<curses_clock::SegmentedTime>(newwin(SEGMENTED_HEIGHT, SEGMENTED_TIME_WIDTH, y, x));
}
std::unique_ptr<curses_clock::AbstractView> curses_clock::TimeFactory::createDigitalView(int x, int y)
{
return std::make_unique<curses_clock::DigitalTime>(newwin(DIGITAL_HEIGHT, DIGITAL_TIME_WIDTH, y, x));
}
std::unique_ptr<curses_clock::AbstractView> curses_clock::DateFactory::createSegmentedView(int x, int y)
{
return std::make_unique<curses_clock::SegmentedDate>(newwin(SEGMENTED_HEIGHT, SEGMENTED_DATE_WIDTH, y, x));
}
std::unique_ptr<curses_clock::AbstractView> curses_clock::DateFactory::createDigitalView(int x, int y)
{
return std::make_unique<curses_clock::DigitalDate>(newwin(DIGITAL_HEIGHT, DIGITAL_DATE_WIDTH, y, x));
}
source.cpp:
#include "clock.h"
#include <chrono>
using namespace std::chrono_literals;
using namespace curses_clock;
using SteadyClock = std::chrono::steady_clock;
int main()
{
std::unique_ptr<ViewFactoryInterface> time_fact{ std::make_unique<TimeFactory>() };
std::unique_ptr<ViewFactoryInterface> date_fact{ std::make_unique<DateFactory>() };
initscr();
curs_set(0);
noecho();
refresh();
std::shared_ptr<AbstractView> time{ std::move(time_fact->createSegmentedView(0,0)) };
std::shared_ptr<AbstractView> date{ std::move(date_fact->createDigitalView(0,8)) };
SteadyClock::time_point sec_point{ SteadyClock::now() - 1s };
SteadyClock::time_point day_point{ SteadyClock::now() - 24h };
while (1) {
if (SteadyClock::now() - sec_point >= 1s) {
sec_point = SteadyClock::now();
time->printView();
}
if (SteadyClock::now() - day_point >= 24h) {
day_point = SteadyClock::now();
date->printView();
}
}
getch();
endwin();
return 0;
}