* MIT License
* Copyright (c) 2023 - 2024 _VIFEXTech
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "DashboardView.h"
#include "Service/DataProc/DataProc_Def.h"
#include "Service/HAL/HAL_Def.h"
#include "Utils/Geo/Geo.h"
#include "Utils/lv_ext/lv_anim_timeline_wrapper.h"
#include "Utils/lv_ext/lv_ext_func.h"
#include <stdarg.h>
#include <stdio.h>
using namespace Page;
DashboardView::DashboardView(EventListener* listener, lv_obj_t* root)
: _listener(listener)
, _fontLarge(65, "regular")
#define BINDING_DEF(name, type) , _binding##name((Binding<type, DashboardModel>*)getBinding(BINDING_TYPE::name))
#include "BindingDef.inc"
#undef BINDING_DEF
{
lv_obj_set_style_pad_all(root, 0, 0);
lv_obj_t* topInfoCont = topInfoCreate(root);
lv_obj_t* bottomInfoCont = bottomInfoCreate(root);
lv_obj_t* btnGrpCont = btnGroupCreate(root);
lv_obj_move_foreground(topInfoCont);
_anim_timeline = lv_anim_timeline_create();
#define ANIM_DEF(start_time, obj, attr, start, end) \
{ \
start_time, obj, LV_ANIM_EXEC(attr), start, end, 500, lv_anim_path_ease_out, true \
}
auto lv_obj_set_opa = [](void* obj, int32_t v) {
lv_obj_set_style_opa((lv_obj_t*)obj, v, 0);
};
auto lv_obj_set_trans_y = [](void* obj, int32_t v) {
lv_obj_set_style_translate_y((lv_obj_t*)obj, v, 0);
};
auto lv_obj_set_trans_h = [](void* obj, int32_t v) {
lv_obj_set_style_transform_height((lv_obj_t*)obj, v, 0);
};
auto lv_obj_set_text_opa = [](void* obj, int32_t v) {
lv_obj_set_style_text_opa((lv_obj_t*)obj, v, 0);
};
lv_obj_update_layout(root);
lv_coord_t btn_h = lv_obj_get_height(lv_obj_get_child(btnGrpCont, 0));
lv_anim_timeline_wrapper_t wrapper[] = {
ANIM_DEF(0, topInfoCont, trans_y, -lv_obj_get_height(topInfoCont), 0),
ANIM_DEF(200, bottomInfoCont, trans_y, -lv_obj_get_height(bottomInfoCont), 0),
ANIM_DEF(200, bottomInfoCont, opa, LV_OPA_TRANSP, LV_OPA_COVER),
ANIM_DEF(500, lv_obj_get_child(btnGrpCont, 0), trans_h, -btn_h, 0),
ANIM_DEF(600, lv_obj_get_child(btnGrpCont, 1), trans_h, -btn_h, 0),
ANIM_DEF(700, lv_obj_get_child(btnGrpCont, 2), trans_h, -btn_h, 0),
ANIM_DEF(900, btnGrpCont, text_opa, LV_OPA_TRANSP, LV_OPA_COVER),
LV_ANIM_TIMELINE_WRAPPER_END
};
lv_anim_timeline_add_wrapper(_anim_timeline, wrapper);
lv_anim_timeline_set_progress(_anim_timeline, 0);
subscribe(
MSG_ID::ANIM_START,
root,
[](lv_event_t* e) {
auto self = (DashboardView*)lv_event_get_user_data(e);
auto msg = lv_event_get_msg(e);
if (lv_msg_get_id(msg) != self->msgID(MSG_ID::ANIM_START)) {
return;
}
lv_anim_timeline_start(self->_anim_timeline);
},
this);
}
DashboardView::~DashboardView()
{
}
lv_uintptr_t DashboardView::msgID(MSG_ID id)
{
return (lv_uintptr_t)this + (lv_uintptr_t)id;
}
void DashboardView::publish(MSG_ID id, const void* payload)
{
lv_msg_send(msgID(id), payload);
}
void DashboardView::subscribe(MSG_ID id, lv_obj_t* obj, lv_event_cb_t event_cb, void* user_data)
{
lv_msg_subscribe_obj(msgID(id), obj, this);
lv_obj_add_event(obj, event_cb, LV_EVENT_MSG_RECEIVED, user_data);
}
void* DashboardView::getBinding(BINDING_TYPE type)
{
Binding_Info_t info;
info.type = type;
info.binding = nullptr;
_listener->onViewEvent(EVENT_ID::GET_BINDING, &info);
return info.binding;
}
lv_obj_t* DashboardView::topInfoCreate(lv_obj_t* par)
{
lv_obj_t* cont = lv_obj_create(par);
{
lv_obj_remove_style_all(cont);
lv_obj_set_size(cont, lv_pct(100), 135);
lv_obj_set_style_bg_opa(cont, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(cont, lv_color_hex(0x333333), 0);
lv_obj_set_style_radius(cont, 27, 0);
lv_obj_align(cont, LV_ALIGN_TOP_MID, 0, -27);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(
cont,
LV_FLEX_ALIGN_END,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
}
{
lv_obj_t* label = lv_label_create(cont);
lv_obj_set_style_text_font(label, _fontLarge, 0);
lv_obj_set_style_text_color(label, lv_color_white(), 0);
lv_obj_set_style_translate_y(label, 10, 0);
lv_label_set_text_static(label, "00");
subscribe(
MSG_ID::SPORT_STATUS,
label,
[](lv_event_t* e) {
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::SportStatus_Info_t*)lv_msg_get_payload(msg);
auto obj = lv_event_get_current_target_obj(e);
lv_label_set_text_fmt(obj, "%02d", (int)info->speedKph);
});
}
{
lv_obj_t* label = lv_label_create(cont);
lv_obj_set_style_text_color(label, lv_color_white(), 0);
lv_obj_set_style_translate_y(label, -5, 0);
lv_label_set_text_static(label, "km/h");
}
return cont;
}
lv_obj_t* DashboardView::bottomInfoCreate(lv_obj_t* par)
{
lv_obj_t* cont = lv_obj_create(par);
lv_obj_remove_style_all(cont);
lv_obj_set_size(cont, lv_pct(100), 160);
lv_obj_align(cont, LV_ALIGN_TOP_MID, 0, 110);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(
cont,
LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_SPACE_EVENLY);
{
lv_obj_t* label = infoItemCreate(cont, _("AVG_SPEED"));
subscribe(
MSG_ID::SPORT_STATUS,
label,
[](lv_event_t* e) {
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::SportStatus_Info_t*)lv_msg_get_payload(msg);
auto obj = lv_event_get_current_target_obj(e);
lv_label_set_text_fmt(obj, "%0.1f km/h", info->speedAvgKph);
});
}
{
lv_obj_t* label = infoItemCreate(cont, _("TIME"));
subscribe(
MSG_ID::SPORT_STATUS,
label,
[](lv_event_t* e) {
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::SportStatus_Info_t*)lv_msg_get_payload(msg);
auto obj = lv_event_get_current_target_obj(e);
lv_label_set_text_fmt(obj, "%s", makeTimeString(info->singleTime));
});
}
{
lv_obj_t* label = infoItemCreate(cont, _("DISTANCE"));
subscribe(
MSG_ID::SPORT_STATUS,
label,
[](lv_event_t* e) {
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::SportStatus_Info_t*)lv_msg_get_payload(msg);
auto obj = lv_event_get_current_target_obj(e);
lv_label_set_text_fmt(obj, "%0.1f km", info->singleDistance / 1000.0f);
});
}
{
lv_obj_t* label = infoItemCreate(cont, _("ALTITUDE"));
subscribe(
MSG_ID::SPORT_STATUS,
label,
[](lv_event_t* e) {
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::SportStatus_Info_t*)lv_msg_get_payload(msg);
auto obj = lv_event_get_current_target_obj(e);
lv_label_set_text_fmt(obj, "%0.1f m", info->altitude);
});
}
{
lv_obj_t* label = infoItemCreate(cont, _("CALORIES"));
subscribe(
MSG_ID::SPORT_STATUS,
label,
[](lv_event_t* e) {
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::SportStatus_Info_t*)lv_msg_get_payload(msg);
auto obj = lv_event_get_current_target_obj(e);
lv_label_set_text_fmt(obj, "%d k", (int)info->singleCalorie);
});
}
{
lv_obj_t* label = infoItemCreate(cont, _("COURSE"));
subscribe(
MSG_ID::GNSS_STATUS,
label,
[](lv_event_t* e) {
auto msg = lv_event_get_msg(e);
auto info = (const HAL::GNSS_Info_t*)lv_msg_get_payload(msg);
auto obj = lv_event_get_current_target_obj(e);
lv_label_set_text_fmt(obj, "%s", Geo::cardinal(info->course));
});
}
return cont;
}
lv_obj_t* DashboardView::infoItemCreate(lv_obj_t* par, const char* title)
{
lv_obj_t* cont = lv_obj_create(par);
lv_obj_remove_style_all(cont);
lv_obj_remove_flag(cont, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(cont, 100, 39);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(
cont,
LV_FLEX_ALIGN_SPACE_AROUND,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
lv_obj_t* labelValue = lv_label_create(cont);
lv_label_set_text_static(labelValue, "-");
{
lv_obj_t* label = lv_label_create(cont);
lv_obj_set_style_text_color(label, lv_color_hex(0xb3b3b3), 0);
lv_label_set_text(label, title);
}
return labelValue;
}
lv_obj_t* DashboardView::btnGroupCreate(lv_obj_t* par)
{
lv_obj_t* cont = lv_obj_create(par);
lv_obj_remove_style_all(cont);
lv_obj_set_size(cont, lv_pct(100), 40);
lv_obj_align(cont, LV_ALIGN_BOTTOM_MID, 0, -10);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(
cont,
LV_FLEX_ALIGN_SPACE_AROUND,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
btnCreate(cont, LV_SYMBOL_EXT_LOCATION_DOT, "LiveMap");
btnCreate(cont, LV_SYMBOL_EXT_POWER_OFF, "Shutdown");
btnCreate(cont, LV_SYMBOL_EXT_BARS, "SystemInfos");
return cont;
}
lv_obj_t* DashboardView::btnCreate(lv_obj_t* par, const char* symbol, const char* pageID)
{
lv_obj_t* btn = lv_obj_create(par);
lv_obj_remove_style_all(btn);
lv_obj_set_size(btn, 40, 31);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text_static(label, symbol);
lv_obj_center(label);
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
lv_obj_set_style_transform_width(btn, 5, LV_STATE_PRESSED);
lv_obj_set_style_transform_height(btn, -5, LV_STATE_PRESSED);
lv_obj_set_style_bg_color(btn, lv_color_hex(0x666666), 0);
lv_obj_set_style_bg_color(btn, lv_color_hex(0xbbbbbb), LV_STATE_PRESSED);
lv_obj_set_style_bg_color(btn, lv_color_hex(0xff931e), LV_STATE_FOCUSED);
lv_obj_set_style_radius(btn, 9, 0);
static lv_style_transition_dsc_t tran;
static const lv_style_prop_t prop[] = {
LV_STYLE_TRANSFORM_WIDTH,
LV_STYLE_TRANSFORM_HEIGHT,
LV_STYLE_PROP_INV
};
lv_style_transition_dsc_init(
&tran,
prop,
lv_anim_path_ease_out,
200,
0,
nullptr);
lv_obj_set_style_transition(btn, &tran, LV_STATE_PRESSED);
lv_obj_set_style_transition(btn, &tran, LV_STATE_FOCUSED);
if (pageID) {
lv_obj_set_user_data(btn, (void*)pageID);
lv_obj_add_event_cb(
btn,
[](lv_event_t* e) {
auto self = (DashboardView*)lv_event_get_user_data(e);
auto id = lv_obj_get_user_data(lv_event_get_current_target_obj(e));
self->_listener->onViewEvent(EVENT_ID::NAVI_TO_PAGE, id);
},
LV_EVENT_CLICKED,
this);
}
return btn;
}
const char* DashboardView::makeTimeString(uint64_t ms)
{
static char buf[16];
uint64_t ss = ms / 1000;
uint64_t mm = ss / 60;
uint32_t hh = (uint32_t)(mm / 60);
if (hh < 100) {
lv_snprintf(
buf, sizeof(buf),
"%d:%02d:%02d",
(int)hh,
(int)(mm % 60),
(int)(ss % 60));
} else {
lv_snprintf(
buf, sizeof(buf),
"%d:%02d",
(int)hh,
(int)(mm % 60));
}
return buf;
}