mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-04-20 15:07:17 -04:00
492 lines
21 KiB
C++
492 lines
21 KiB
C++
|
|
#include "CalendarMainWindow.hpp"
|
|
#include "application-calendar/ApplicationCalendar.hpp"
|
|
#include "application-calendar/models/DayModel.hpp"
|
|
#include "application-calendar/models/DayModel.cpp"
|
|
#include "application-calendar/models/MonthModel.hpp"
|
|
#include "application-calendar/models/MonthModel.cpp"
|
|
#include "application-calendar/widgets/CalendarStyle.hpp"
|
|
#include "NoEvents.hpp"
|
|
#include <gui/widgets/Window.hpp>
|
|
#include <gui/widgets/Label.hpp>
|
|
#include <gui/widgets/Item.hpp>
|
|
#include <gui/widgets/BoxLayout.hpp>
|
|
#include <gui/widgets/GridLayout.hpp>
|
|
#include <time/time_conversion.hpp>
|
|
#include <gui/widgets/Margins.hpp>
|
|
#include <gui/widgets/Item.hpp>
|
|
#include <time/time_conversion.hpp>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
namespace gui
|
|
{
|
|
DayLabel::DayLabel(gui::Item *parent,
|
|
std::string number,
|
|
const uint32_t &x,
|
|
const uint32_t &y,
|
|
const uint32_t &width,
|
|
const uint32_t &height)
|
|
: Label(parent, 0, 0, 0, 0, ""), DayModel(number, x, y)
|
|
{
|
|
LOG_DEBUG("Call DayLabel constructor. With coords: x:%d, y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
parent->addWidget(this);
|
|
this->setSize(width, height);
|
|
|
|
if (y == 0) {
|
|
this->setText(number);
|
|
this->setFont(style::window::font::verysmall);
|
|
}
|
|
this->activeItem = false;
|
|
this->setPenWidth(style::window::default_border_no_focus_w);
|
|
this->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
|
|
}
|
|
|
|
void DayLabel::setLabel(std::string number, std::function<bool(Item &)> activatedCallback)
|
|
{
|
|
LOG_DEBUG("Set callback of day label. coords: x:%d, y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
this->number = number;
|
|
this->activeItem = true;
|
|
this->setText(number);
|
|
this->setFont(style::window::font::medium);
|
|
this->activatedCallback = activatedCallback;
|
|
this->setPenWidth(style::window::default_border_no_focus_w);
|
|
this->setPenFocusWidth(style::window::default_border_focus_w);
|
|
this->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_TOP | RectangleEdgeFlags::GUI_RECT_EDGE_BOTTOM);
|
|
this->setFont(style::window::font::medium);
|
|
}
|
|
|
|
MonthBox::MonthBox(app::Application *app,
|
|
gui::Item *parent,
|
|
const int &offsetTop,
|
|
const uint32_t &width,
|
|
const uint32_t &height,
|
|
const uint32_t &dayWidth,
|
|
const uint32_t &dayHeight,
|
|
const std::unique_ptr<MonthModel> &model)
|
|
: GridLayout(parent, style::window::default_left_margin, offsetTop, width, height, {dayWidth, dayHeight}),
|
|
MonthModel(model->name, model->days)
|
|
{
|
|
LOG_DEBUG("Call MonthBox constructor");
|
|
// set the costant values of first and first column
|
|
this->firstRow = 0;
|
|
this->firstColumn = 0;
|
|
|
|
assert(parent);
|
|
parent->addWidget(this);
|
|
|
|
grid.x = dayWidth;
|
|
grid.y = dayHeight;
|
|
this->rows = style::window::calendar::max_weeks_number;
|
|
this->columns = style::window::calendar::week_days_number;
|
|
for (uint32_t y = 0; y < this->rows; y++) {
|
|
for (uint32_t x = 0; x < this->columns; x++) {
|
|
auto key = std::make_pair(x, y);
|
|
std::string name = "";
|
|
if (y == 0) {
|
|
name = utils::time::Locale::get_short_day(x);
|
|
}
|
|
LOG_DEBUG("MonthBox x:%d y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
dayMap[key] = new DayLabel(this,
|
|
name,
|
|
x,
|
|
y,
|
|
style::window::calendar::day_cell_width,
|
|
style::window::calendar::day_cell_height);
|
|
addWidget(dayMap[key]);
|
|
}
|
|
}
|
|
LOG_DEBUG("MonthBox constructor Completed Successfully!");
|
|
}
|
|
|
|
void MonthBox::buildMap(app::Application *app)
|
|
{
|
|
LOG_DEBUG("Start build month box map");
|
|
for (auto &day : days) {
|
|
if (day.x < this->columns && day.y < this->rows) {
|
|
LOG_DEBUG("Set element in monthbox map in position x:%d y:%d",
|
|
static_cast<int>(day.x),
|
|
static_cast<int>(day.y));
|
|
auto key = std::make_pair(day.x, day.y);
|
|
|
|
dayMap[key]->setLabel(day.number.c_str(), [=](gui::Item &item) {
|
|
LOG_DEBUG("Switch to DayWindow");
|
|
app->switchWindow("DayWindow", nullptr);
|
|
return true;
|
|
});
|
|
}
|
|
else {
|
|
LOG_DEBUG("COLUMNS:%d ROWS:%d", static_cast<int>(this->columns), static_cast<int>(this->rows));
|
|
LOG_ERROR("Element positioned outside the box. coords x:%d , y:%d",
|
|
static_cast<int>(day.x),
|
|
static_cast<int>(day.y));
|
|
}
|
|
}
|
|
LOG_DEBUG("Build Map Completed Successfully!");
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> MonthBox::getFirstDay()
|
|
{
|
|
uint32_t y = firstRow + 1;
|
|
uint32_t x = firstColumn;
|
|
auto key = std::make_pair(x, y);
|
|
if (!dayMap.empty()) {
|
|
while (!dayMap[key]->activeItem) {
|
|
key = std::make_pair(++x, y);
|
|
}
|
|
}
|
|
return key;
|
|
}
|
|
std::pair<uint32_t, uint32_t> MonthBox::getLastDay()
|
|
{
|
|
uint32_t y = rows - 2;
|
|
uint32_t x = columns - 1;
|
|
auto key = std::make_pair(x, y);
|
|
if (!dayMap.empty()) {
|
|
while (dayMap.count(key) == 0 || !dayMap[key]->activeItem) {
|
|
key = std::make_pair(--x, y);
|
|
}
|
|
}
|
|
return key;
|
|
}
|
|
|
|
void MonthBox::setNavigation()
|
|
{
|
|
LOG_DEBUG("Start setting navigation");
|
|
// Go through each element of grid
|
|
for (uint32_t y = this->firstRow; y < this->rows; y++) {
|
|
for (uint32_t x = this->firstColumn; x < this->columns; x++) {
|
|
auto keyCenter = std::make_pair(x, y);
|
|
// if current element is active set the navigation for all neighbours in 4 directions
|
|
if (dayMap[keyCenter]->activeItem) {
|
|
// direction UP
|
|
LOG_DEBUG("Set navigation UP from coords x:%d , y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
auto keyUp = std::make_pair(x, y - 1);
|
|
// if neighbour on top is not active or outside the map it means the current element is at the
|
|
// top border
|
|
if (dayMap.count(keyUp) == 0 || !dayMap[keyUp]->activeItem) {
|
|
/// get the grid element from the opposite row
|
|
// change neighbour coords to the last row
|
|
uint32_t index = this->rows;
|
|
// go up to the first active element
|
|
while (!dayMap[keyUp]->activeItem) {
|
|
keyUp = std::make_pair(x, --index);
|
|
}
|
|
}
|
|
// set navigation from current element to the upper neighbour
|
|
dayMap[keyCenter]->setNavigationItem(NavigationDirection::UP, dayMap[keyUp]);
|
|
|
|
// direction DOWN
|
|
LOG_DEBUG("Set navigation DOWN from coords x:%d , y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
auto keyDown = std::make_pair(x, y + 1);
|
|
// if bottom neighbour is not active or outside the map it means the current element is at the
|
|
// bottom border
|
|
if (dayMap.count(keyDown) == 0 || !dayMap[keyDown]->activeItem) {
|
|
/// get the grid element from the opposite row
|
|
// change neighbour coords to the first row
|
|
uint32_t index = this->firstRow;
|
|
// go down to the first active element
|
|
while (dayMap.count(keyDown) == 0 || !dayMap[keyDown]->activeItem) {
|
|
keyDown = std::make_pair(x, ++index);
|
|
}
|
|
}
|
|
// set navigation from current element to the bottom neighbour
|
|
dayMap[keyCenter]->setNavigationItem(NavigationDirection::DOWN, dayMap[keyDown]);
|
|
|
|
// direction LEFT
|
|
LOG_DEBUG("Set navigation LEFT from coords x:%d , y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
auto keyLeft = std::make_pair(x - 1, y);
|
|
// if left neighbour is not active or outside the map it means the current element is at the
|
|
// left border
|
|
if (dayMap.count(keyLeft) == 0 || !dayMap[keyLeft]->activeItem) {
|
|
/// get the grid element from the opposite column
|
|
// change neighbour coords to the last column
|
|
uint32_t index = this->columns;
|
|
// go left to the first active element
|
|
while (dayMap.count(keyLeft) == 0 || !dayMap[keyLeft]->activeItem) {
|
|
keyLeft = std::make_pair(--index, y);
|
|
}
|
|
}
|
|
// set navigation from current element to the left neighbour
|
|
dayMap[keyCenter]->setNavigationItem(NavigationDirection::LEFT, dayMap[keyLeft]);
|
|
|
|
// direction RIGHT
|
|
LOG_DEBUG("Set navigation RIGHT from coords x:%d , y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
auto keyRight = std::make_pair(x + 1, y);
|
|
// if right neighbour is not active or outside the map it means the current element is at the
|
|
// right border
|
|
if (dayMap.count(keyRight) == 0 || !dayMap[keyRight]->activeItem) {
|
|
/// get the grid element from the opposite column
|
|
// change neighbour coords to the first column
|
|
uint32_t index = this->firstColumn - 1;
|
|
while (dayMap.count(keyRight) == 0 || !dayMap[keyRight]->activeItem) {
|
|
keyRight = std::make_pair(++index, y);
|
|
}
|
|
}
|
|
// set navigation from current element to the right neighbour
|
|
dayMap[keyCenter]->setNavigationItem(NavigationDirection::RIGHT, dayMap[keyRight]);
|
|
}
|
|
}
|
|
}
|
|
LOG_DEBUG("Setting navi completed successfully");
|
|
}
|
|
|
|
void MonthBox::changeMonthInput(gui::CalendarMainWindow *parent,
|
|
const uint32_t &x,
|
|
const uint32_t &y,
|
|
NavigationDirection direction)
|
|
{
|
|
auto key = std::make_pair(x, y);
|
|
dayMap[key]->clearNavigationItem(direction);
|
|
if (direction == NavigationDirection::LEFT) {
|
|
dayMap[key]->inputCallback = [=](gui::Item &item, const InputEvent &inputEvent) {
|
|
if (inputEvent.state == InputEvent::State::keyReleasedShort &&
|
|
inputEvent.keyCode == gui::KeyCode::KEY_LEFT) {
|
|
LOG_DEBUG("Change to prev month");
|
|
parent->rebuild(style::window::calendar::test::prev_month_id);
|
|
return true;
|
|
}
|
|
else {
|
|
LOG_DEBUG("Change month not handled. Pressed key: %d", static_cast<int>(inputEvent.keyCode));
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
else if (direction == NavigationDirection::RIGHT) {
|
|
dayMap[key]->inputCallback = [=](gui::Item &item, const InputEvent &inputEvent) {
|
|
if (inputEvent.state == InputEvent::State::keyReleasedShort &&
|
|
inputEvent.keyCode == gui::KeyCode::KEY_RIGHT) {
|
|
LOG_DEBUG("Change to next month");
|
|
parent->rebuild(style::window::calendar::test::next_month_id);
|
|
return true;
|
|
}
|
|
else {
|
|
LOG_DEBUG("Change month not handled. Pressed key: %d", static_cast<int>(inputEvent.keyCode));
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
void MonthBox::rebuildNavigation(gui::CalendarMainWindow *parent, const uint32_t &x, NavigationDirection direction)
|
|
{
|
|
// start from first row
|
|
uint32_t y = 0;
|
|
LOG_DEBUG("Start Rebuild navigation on coords x:%d , y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
auto key = std::make_pair(x, y);
|
|
while (dayMap.count(key) > 0) {
|
|
if (dayMap[key]->activeItem) {
|
|
LOG_DEBUG("Rebuild navigation on coords x:%d , y:%d", static_cast<int>(x), static_cast<int>(y));
|
|
changeMonthInput(parent, key.first, key.second, direction);
|
|
}
|
|
key = std::make_pair(x, ++y);
|
|
}
|
|
}
|
|
|
|
bool CalendarMainWindow::getData(const uint32_t &date_time)
|
|
{
|
|
switch (date_time) {
|
|
case 1: {
|
|
// Test case 1:
|
|
std::vector<DayModel> days;
|
|
uint32_t numbOfWeeks = 4;
|
|
uint32_t daysInWeek = 7;
|
|
uint32_t startDay = 0;
|
|
uint32_t numb = 0;
|
|
for (uint32_t y = 1; y <= numbOfWeeks; y++) {
|
|
for (uint32_t x = 0; x < daysInWeek; x++) {
|
|
if (y == 1 && x < startDay) {
|
|
continue;
|
|
}
|
|
++numb;
|
|
if (numb > 31) {
|
|
break;
|
|
}
|
|
DayModel day(to_string(numb).c_str(), x, y);
|
|
days.push_back(day);
|
|
}
|
|
}
|
|
this->monthModel = std::make_unique<MonthModel>("July", days);
|
|
return true;
|
|
}
|
|
case 2: {
|
|
// Test case 2:
|
|
std::vector<DayModel> days;
|
|
uint32_t numbOfWeeks = 5;
|
|
uint32_t daysInWeek = 7;
|
|
uint32_t startDay = 3;
|
|
uint32_t numb = 0;
|
|
for (uint32_t y = 1; y <= numbOfWeeks; y++) {
|
|
for (uint32_t x = 0; x < daysInWeek; x++) {
|
|
if (y == 1 && x < startDay) {
|
|
continue;
|
|
}
|
|
++numb;
|
|
if (numb > 28) {
|
|
break;
|
|
}
|
|
DayModel day(to_string(numb).c_str(), x, y);
|
|
days.push_back(day);
|
|
}
|
|
}
|
|
this->monthModel = std::make_unique<MonthModel>("July", days);
|
|
return true;
|
|
}
|
|
case 3: {
|
|
// Test case 3:
|
|
std::vector<DayModel> days;
|
|
uint32_t numbOfWeeks = 5;
|
|
uint32_t daysInWeek = 7;
|
|
uint32_t startDay = 6;
|
|
uint32_t numb = 0;
|
|
for (uint32_t y = 1; y <= numbOfWeeks; y++) {
|
|
for (uint32_t x = 0; x < daysInWeek; x++) {
|
|
if (y == 1 && x < startDay) {
|
|
continue;
|
|
}
|
|
++numb;
|
|
if (numb > 31) {
|
|
break;
|
|
}
|
|
DayModel day(to_string(numb).c_str(), x, y);
|
|
days.push_back(day);
|
|
}
|
|
}
|
|
this->monthModel = std::make_unique<MonthModel>("April", days);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CalendarMainWindow::CalendarMainWindow(app::Application *app, std::string name) : AppWindow(app, name)
|
|
{
|
|
buildInterface(style::window::calendar::test::month_id);
|
|
}
|
|
|
|
void CalendarMainWindow::rebuild(const uint32_t &ID)
|
|
{
|
|
destroyInterface();
|
|
LOG_DEBUG("Calendar Date Time ID:%d", static_cast<int>(ID));
|
|
buildInterface(ID);
|
|
}
|
|
|
|
void CalendarMainWindow::buildInterface(const uint32_t &actualDateTimeID)
|
|
{
|
|
LOG_DEBUG("AppWindow build interface");
|
|
LOG_DEBUG("Calendar Date Time ID:%d", static_cast<int>(actualDateTimeID));
|
|
AppWindow::buildInterface();
|
|
LOG_DEBUG("Start build interface for calendar main window");
|
|
auto app = dynamic_cast<app::ApplicationCalendar *>(application);
|
|
assert(app);
|
|
|
|
setTitle(utils::localize.get("app_calendar_title_main"));
|
|
offsetFromTop = title->offset_h() + style::window::calendar::month_year_height;
|
|
monthWidth = style::window::default_body_width;
|
|
monthHeight = style::window_height - title->offset_h() - style::footer::height;
|
|
dayWidth = style::window::calendar::day_cell_width;
|
|
dayHeight = style::window::calendar::day_cell_height;
|
|
|
|
bool resp = getData(actualDateTimeID);
|
|
LOG_DEBUG("Calendar Date Time ID:%d", static_cast<int>(actualDateTimeID));
|
|
if (resp) {
|
|
// create empty month box
|
|
month = new MonthBox(app, this, offsetFromTop, monthWidth, monthHeight, dayWidth, dayHeight, monthModel);
|
|
// setup month box
|
|
month->buildMap(app);
|
|
addWidget(month);
|
|
setFocusItem(month);
|
|
// rebuild first column to make the change to prev month available
|
|
month->rebuildNavigation(this, month->getFirstColumn(), NavigationDirection::LEFT);
|
|
// rebuild last column to make the change to next month available
|
|
month->rebuildNavigation(this, (month->getColumns() - 1), NavigationDirection::RIGHT);
|
|
// rebuild first day to make the change to next month available
|
|
auto key = month->getFirstDay();
|
|
month->changeMonthInput(this, key.first, key.second, NavigationDirection::LEFT);
|
|
}
|
|
|
|
auto actualDateTime = "June 2019";
|
|
// CHange label into bar
|
|
auto label = new Label(this,
|
|
style::window::default_left_margin,
|
|
title->offset_h(),
|
|
style::window::default_body_width,
|
|
style::window::calendar::month_year_height,
|
|
actualDateTime);
|
|
// label->setActive(gui::TopBar::Elements::TIME, true);
|
|
label->setPenWidth(style::window::default_border_no_focus_w);
|
|
label->setFont(style::window::font::mediumbold);
|
|
label->setAlignment(gui::Alignment(gui::Alignment::Vertical::Center));
|
|
addWidget(label);
|
|
|
|
bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
|
|
bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
|
|
bottomBar->setActive(gui::BottomBar::Side::LEFT, true);
|
|
bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
|
|
bottomBar->setText(gui::BottomBar::Side::CENTER, utils::localize.get(style::strings::common::open));
|
|
bottomBar->setText(gui::BottomBar::Side::LEFT, utils::localize.get(style::strings::common::call));
|
|
LOG_DEBUG("Calendar Date Time ID:%d", static_cast<int>(actualDateTimeID));
|
|
}
|
|
|
|
void CalendarMainWindow::destroyInterface()
|
|
{
|
|
erase();
|
|
}
|
|
|
|
bool CalendarMainWindow::onInput(const gui::InputEvent &inputEvent)
|
|
{
|
|
if (inputEvent.state == gui::InputEvent::State::keyReleasedShort ||
|
|
inputEvent.state == gui::InputEvent::State::keyReleasedLong) {
|
|
if (inputEvent.keyCode == gui::KeyCode::KEY_ENTER) {
|
|
application->switchWindow("DayWindow");
|
|
LOG_DEBUG("Switch to Day Window");
|
|
return true;
|
|
}
|
|
if (inputEvent.keyCode == gui::KeyCode::KEY_LF) {
|
|
LOG_DEBUG("Switch to List Window");
|
|
return true;
|
|
}
|
|
if (AppWindow::onInput(inputEvent)) {
|
|
return true;
|
|
}
|
|
|
|
if (!inputEvent.isShortPress()) {
|
|
return false;
|
|
}
|
|
|
|
if (inputEvent.keyCode == gui::KeyCode::KEY_ENTER) {
|
|
std::shared_ptr<CalendarEventsModel> calendarEventsModel =
|
|
std::make_shared<CalendarEventsModel>(this->application);
|
|
if (calendarEventsModel->getItemCount() == 0) {
|
|
switchToNoEventsWindow();
|
|
}
|
|
else {
|
|
LOG_DEBUG("Switch to Day Window");
|
|
application->switchWindow(style::window::calendar::name::day_window);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return AppWindow::onInput(inputEvent);
|
|
}
|
|
|
|
void CalendarMainWindow::switchToNoEventsWindow()
|
|
{
|
|
auto dialog = dynamic_cast<gui::NoEvents *>(
|
|
this->application->getWindow(style::window::calendar::name::no_events_window));
|
|
assert(dialog != nullptr);
|
|
auto meta = dialog->meta;
|
|
meta.text = "app_calendar_no_events_information";
|
|
meta.title = utils::time::Time().str("%d %B");
|
|
meta.icon = "phonebook_empty_grey_circle_W_G";
|
|
meta.action = [=]() -> bool {
|
|
LOG_DEBUG("Switch to edit window");
|
|
return true;
|
|
};
|
|
dialog->update(meta);
|
|
this->application->switchWindow(dialog->getName());
|
|
LOG_DEBUG("Switch to no events window");
|
|
}
|
|
|
|
} // namespace gui
|