Files
trail-mate/modules/ui_shared/src/ui/widgets/busy_overlay.cpp
T

302 lines
7.6 KiB
C++

#include "ui/widgets/busy_overlay.h"
#include <cstring>
#include "lvgl.h"
#include "platform/ui/screen_runtime.h"
#include "ui/page/page_profile.h"
#include "ui/ui_theme.h"
#if !defined(LV_FONT_MONTSERRAT_12) || !LV_FONT_MONTSERRAT_12
#define lv_font_montserrat_12 lv_font_montserrat_14
#endif
#if !defined(LV_FONT_MONTSERRAT_16) || !LV_FONT_MONTSERRAT_16
#define lv_font_montserrat_16 lv_font_montserrat_14
#endif
namespace ui::widgets::busy_overlay
{
namespace
{
constexpr lv_coord_t kRequestedWidth = 220;
constexpr lv_coord_t kRequestedHeight = 118;
constexpr lv_coord_t kBarHeight = 8;
constexpr lv_coord_t kBarRadius = 4;
constexpr uint32_t kOverlayBgColor = 0x1B1208;
constexpr uint32_t kAnimTimeMs = 820;
lv_obj_t* s_root = nullptr;
lv_obj_t* s_panel = nullptr;
lv_obj_t* s_title_label = nullptr;
lv_obj_t* s_detail_label = nullptr;
lv_obj_t* s_bar = nullptr;
uint32_t s_depth = 0;
void request_refresh()
{
if (s_root && lv_obj_is_valid(s_root))
{
lv_obj_invalidate(s_root);
return;
}
if (lv_obj_t* screen = lv_screen_active())
{
lv_obj_invalidate(screen);
}
}
void set_bar_value(void* bar, int32_t value)
{
lv_obj_t* bar_obj = static_cast<lv_obj_t*>(bar);
if (!bar_obj || !lv_obj_is_valid(bar_obj))
{
return;
}
lv_bar_set_value(bar_obj, static_cast<int32_t>(value), LV_ANIM_OFF);
}
int clamp_progress(int progress_percent)
{
if (progress_percent < 0)
{
return -1;
}
if (progress_percent > 100)
{
return 100;
}
return progress_percent;
}
void stop_bar_animation()
{
if (s_bar && lv_obj_is_valid(s_bar))
{
lv_anim_del(s_bar, set_bar_value);
}
}
void start_bar_animation()
{
if (!s_bar || !lv_obj_is_valid(s_bar))
{
return;
}
stop_bar_animation();
lv_bar_set_range(s_bar, 0, 100);
lv_bar_set_value(s_bar, 12, LV_ANIM_OFF);
lv_anim_t anim;
lv_anim_init(&anim);
lv_anim_set_var(&anim, s_bar);
lv_anim_set_values(&anim, 12, 100);
lv_anim_set_time(&anim, kAnimTimeMs);
lv_anim_set_playback_time(&anim, kAnimTimeMs);
lv_anim_set_repeat_count(&anim, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_exec_cb(&anim, set_bar_value);
lv_anim_start(&anim);
}
void apply_progress(int progress_percent)
{
if (!s_bar || !lv_obj_is_valid(s_bar))
{
return;
}
const int clamped = clamp_progress(progress_percent);
if (clamped < 0)
{
start_bar_animation();
return;
}
stop_bar_animation();
lv_bar_set_range(s_bar, 0, 100);
lv_bar_set_value(s_bar, clamped, LV_ANIM_OFF);
}
void swallow_event_cb(lv_event_t* e)
{
lv_event_stop_bubbling(e);
lv_event_stop_processing(e);
if (lv_indev_t* indev = lv_event_get_indev(e))
{
lv_indev_stop_processing(indev);
}
}
void destroy_overlay()
{
stop_bar_animation();
if (s_root && lv_obj_is_valid(s_root))
{
lv_obj_del(s_root);
}
s_root = nullptr;
s_panel = nullptr;
s_title_label = nullptr;
s_detail_label = nullptr;
s_bar = nullptr;
}
void ensure_overlay()
{
if (s_root && lv_obj_is_valid(s_root))
{
return;
}
lv_obj_t* parent = lv_layer_top();
if (!parent)
{
return;
}
s_root = lv_obj_create(parent);
lv_obj_remove_style_all(s_root);
lv_obj_set_size(s_root, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_bg_color(s_root, lv_color_hex(kOverlayBgColor), 0);
lv_obj_set_style_bg_opa(s_root, LV_OPA_50, 0);
lv_obj_set_style_border_width(s_root, 0, 0);
lv_obj_clear_flag(s_root, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(s_root, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(s_root, swallow_event_cb, LV_EVENT_ALL, nullptr);
const auto size = ::ui::page_profile::resolve_modal_size(kRequestedWidth, kRequestedHeight, s_root);
const lv_coord_t pad = ::ui::page_profile::resolve_modal_pad();
s_panel = lv_obj_create(s_root);
lv_obj_set_size(s_panel, size.width, size.height);
lv_obj_center(s_panel);
lv_obj_set_style_bg_color(s_panel, ::ui::theme::surface(), 0);
lv_obj_set_style_bg_opa(s_panel, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(s_panel, 1, 0);
lv_obj_set_style_border_color(s_panel, ::ui::theme::border(), 0);
lv_obj_set_style_radius(s_panel, 10, 0);
lv_obj_set_style_pad_all(s_panel, pad, 0);
lv_obj_set_style_pad_row(s_panel, pad, 0);
lv_obj_set_flex_flow(s_panel, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(s_panel, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(s_panel, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(s_panel, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(s_panel, swallow_event_cb, LV_EVENT_ALL, nullptr);
s_title_label = lv_label_create(s_panel);
lv_obj_set_width(s_title_label, LV_PCT(100));
lv_obj_set_style_text_align(s_title_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_font(s_title_label, &lv_font_montserrat_16, 0);
lv_obj_set_style_text_color(s_title_label, ::ui::theme::text(), 0);
lv_label_set_long_mode(s_title_label, LV_LABEL_LONG_WRAP);
s_detail_label = lv_label_create(s_panel);
lv_obj_set_width(s_detail_label, LV_PCT(100));
lv_obj_set_style_text_align(s_detail_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_font(s_detail_label, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(s_detail_label, ::ui::theme::text_muted(), 0);
lv_label_set_long_mode(s_detail_label, LV_LABEL_LONG_DOT);
s_bar = lv_bar_create(s_panel);
lv_obj_set_width(s_bar, LV_PCT(100));
lv_obj_set_height(s_bar, kBarHeight);
lv_obj_set_style_radius(s_bar, kBarRadius, LV_PART_MAIN);
lv_obj_set_style_bg_color(s_bar, ::ui::theme::separator(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(s_bar, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_border_width(s_bar, 0, LV_PART_MAIN);
lv_obj_set_style_radius(s_bar, kBarRadius, LV_PART_INDICATOR);
lv_obj_set_style_bg_color(s_bar, ::ui::theme::accent(), LV_PART_INDICATOR);
lv_obj_set_style_bg_opa(s_bar, LV_OPA_COVER, LV_PART_INDICATOR);
start_bar_animation();
lv_obj_move_foreground(s_root);
}
void set_label_text(lv_obj_t* label, const char* text)
{
if (!label || !lv_obj_is_valid(label))
{
return;
}
lv_label_set_text(label, text ? text : "");
}
void update_content(const char* title, const char* detail)
{
set_label_text(s_title_label, title ? title : "Loading...");
set_label_text(s_detail_label, detail ? detail : "");
if (detail && detail[0] != '\0')
{
lv_obj_clear_flag(s_detail_label, LV_OBJ_FLAG_HIDDEN);
}
else
{
lv_obj_add_flag(s_detail_label, LV_OBJ_FLAG_HIDDEN);
}
}
} // namespace
void show(const char* title, const char* detail)
{
++s_depth;
::platform::ui::screen::disable_sleep();
ensure_overlay();
if (!s_root)
{
return;
}
update_content(title, detail);
apply_progress(-1);
lv_obj_move_foreground(s_root);
request_refresh();
}
void update(const char* title, const char* detail)
{
if (!s_root || !lv_obj_is_valid(s_root))
{
return;
}
update_content(title, detail);
}
void set_progress(int progress_percent)
{
if (!s_root || !lv_obj_is_valid(s_root))
{
return;
}
apply_progress(progress_percent);
}
void hide()
{
if (s_depth == 0)
{
return;
}
--s_depth;
::platform::ui::screen::enable_sleep();
if (s_depth > 0)
{
return;
}
destroy_overlay();
request_refresh();
}
bool visible()
{
return s_root != nullptr && lv_obj_is_valid(s_root);
}
} // namespace ui::widgets::busy_overlay