mirror of https://github.com/meshcore-dev/MeshCore
9 changed files with 1136 additions and 34 deletions
@ -0,0 +1,25 @@ |
|||
#include <Arduino.h> |
|||
|
|||
#include "esp_log.h" |
|||
#include "uiDefines.h" |
|||
#include "uiVars.h" |
|||
|
|||
#define TAG "clock_task" |
|||
|
|||
void clock_task(void *pvParameters) { |
|||
|
|||
vTaskSuspend(NULL); |
|||
|
|||
ESP_LOGI(TAG, "Clock manager: Task running on core %d", xPortGetCoreID()); |
|||
|
|||
uiManager->clearDateTime(); |
|||
|
|||
// TODO: sync clock
|
|||
while (1) { |
|||
// uiManager->updateDateTime(
|
|||
// myClock->getTimeStruct()
|
|||
// );
|
|||
// uiManager->updateValues();
|
|||
vTaskDelay(DELAY_CLOCK_TASK / portTICK_PERIOD_MS); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
#include <Arduino.h> |
|||
|
|||
#include "esp_log.h" |
|||
#include "uiDefines.h" |
|||
#include "uiVars.h" |
|||
|
|||
#define TAG "lvgl_task" |
|||
|
|||
void lvgl_task(void *pvParameters) { |
|||
|
|||
vTaskSuspend(NULL); |
|||
|
|||
ESP_LOGI(TAG, "UI manager: Task running on core %d", xPortGetCoreID()); |
|||
|
|||
while (1) { |
|||
lv_timer_handler(); |
|||
vTaskDelay(DELAY_LVGL_TASK / portTICK_PERIOD_MS); |
|||
} |
|||
} |
|||
@ -0,0 +1,797 @@ |
|||
#include <Arduino.h> |
|||
|
|||
#include "esp_log.h" |
|||
#include "uiDefines.h" |
|||
#include "uiVars.h" |
|||
|
|||
#include "uiManager.h" |
|||
|
|||
#include "../src/fonts/fonts.h" |
|||
|
|||
#include <helpers/ContactInfo.h> |
|||
#include <helpers/AdvertDataHelpers.h> |
|||
|
|||
#if defined(LANG_GR) |
|||
const char *UIManager::days[7] = {"Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"}; |
|||
const char *UIManager::months[12] = {"Ιαν", "Φεβ", "Μαρ", "Απρ", "Μαι", "Ιουν", |
|||
"Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"}; |
|||
#elif defined(LANG_EN) |
|||
const char *UIManager::days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
|||
const char *UIManager::months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
|||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; |
|||
#endif |
|||
|
|||
#define TAG "UIManager" |
|||
|
|||
extern void handleCommand(char *msg); |
|||
|
|||
UIManager::UIManager() { |
|||
|
|||
tmp_buf = (char*)malloc(128); |
|||
|
|||
lv_disp_t * dispp = lv_disp_get_default(); |
|||
lv_theme_t * theme = lv_theme_default_init(dispp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), |
|||
false, LV_FONT_DEFAULT); |
|||
lv_disp_set_theme(dispp, theme); |
|||
ui_Screen1_screen_init(); |
|||
ui____initial_actions0 = lv_obj_create(NULL); |
|||
lv_disp_load_scr(ui_Screen1); |
|||
|
|||
} |
|||
|
|||
void UIManager::format_time(uint32_t ts, char *buf, size_t len) |
|||
{ |
|||
time_t t = ts; |
|||
struct tm *tm_info = localtime(&t); |
|||
strftime(buf, len, "%H:%M:%S", tm_info); |
|||
} |
|||
|
|||
void UIManager::format_datetime(char *buf, size_t size, const struct tm *timeinfo) { |
|||
char tmp[64]; |
|||
strftime(tmp, sizeof(tmp), "%a, %d %b %Y", timeinfo); |
|||
|
|||
int wday = timeinfo->tm_wday; // 0=Κυρ ... 6=Σαβ
|
|||
int mon = timeinfo->tm_mon; // 0=Ιαν ... 11=Δεκ
|
|||
|
|||
// replace %a and %b with selected language
|
|||
snprintf(buf, size, "%s, %02d %s %d", days[wday], timeinfo->tm_mday, months[mon], 1900 + timeinfo->tm_year); |
|||
} |
|||
|
|||
void UIManager::updateDateTime(const struct tm timeinfo) { |
|||
// TODO: Add to settings "Date format"
|
|||
char date_str[50]; |
|||
format_datetime(date_str, sizeof(date_str), &timeinfo); |
|||
lv_label_set_text(ui_ValueDate, date_str); |
|||
|
|||
// TODO: Add to settings "Hour format"
|
|||
strftime(tmp_buf, 50, "%H:%M", &timeinfo); // 24h format
|
|||
//strftime(tmp_buf, 50, "%I:%M %p", &timeinfo); // 12h format
|
|||
lv_label_set_text(ui_ValueTime, tmp_buf); |
|||
|
|||
// TODO: Add to settings "dim at night"
|
|||
// TODO: Add to settings "dim hours"
|
|||
// TODO: Add to settings "dim percentage"
|
|||
if (timeinfo.tm_hour > 21 || timeinfo.tm_hour < 7) { |
|||
setNightMode(true); |
|||
} else { |
|||
setNightMode(false); |
|||
} |
|||
} |
|||
|
|||
void UIManager::clearDateTime() { |
|||
#if defined(LANG_EN) |
|||
uiManager->updateInfo("Clock sync...", COLOR_WHITE); |
|||
#elif defined(LANG_GR) |
|||
uiManager->updateInfo("Συγχρονισμός ώρας...", COLOR_WHITE); |
|||
#endif |
|||
lv_label_set_text(ui_ValueDate, "--- --/--/----"); |
|||
lv_label_set_text(ui_ValueTime, "--:--"); |
|||
} |
|||
|
|||
void UIManager::timestampToTime(time_t timestamp, char *buffer, size_t buffer_size) { |
|||
struct tm *time_info; |
|||
time_info = localtime(×tamp); |
|||
strftime(buffer, buffer_size, "%H:%M", time_info); |
|||
} |
|||
|
|||
const char* UIManager::convertDegreesToDirection(int degrees) { |
|||
// Normalize degrees to [0, 360)
|
|||
degrees = degrees % 360; |
|||
if (degrees < 0) degrees += 360; |
|||
|
|||
#if defined(LANG_EN) |
|||
static constexpr const char* dirs[] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"}; |
|||
#elif defined(LANG_GR) |
|||
static constexpr const char* dirs[] = {"Β", "ΒΑ", "Α", "ΝΑ", "Ν", "ΝΔ", "Δ", "ΒΔ"}; |
|||
#else |
|||
#error "No Language defined!" |
|||
#endif |
|||
|
|||
// Each direction covers 45°, starting at N = 0°
|
|||
int index = static_cast<int>((degrees + 22.5) / 45.0) % 8; |
|||
return dirs[index]; |
|||
} |
|||
|
|||
|
|||
int UIManager::windSpeedToBeaufort(float speed) { |
|||
static const float limits[] = { |
|||
0.5, 1.5, 3.3, 5.5, 7.9, 10.7, |
|||
13.8, 17.1, 20.7, 24.4, 28.4, 32.6 |
|||
}; |
|||
|
|||
for (int i = 0; i < 12; ++i) |
|||
if (speed < limits[i]) |
|||
return i; |
|||
return 12; |
|||
} |
|||
|
|||
void UIManager::updateValues() { |
|||
lv_label_set_text(ui_ValueTime, "--:--"); |
|||
} |
|||
|
|||
void UIManager::updateInfo(const char *str, uint32_t color) { |
|||
// lv_label_set_text(ui_ValueLastUpdate, str);
|
|||
// lv_obj_set_style_text_color(ui_ValueLastUpdate, lv_color_hex(color), LV_PART_MAIN | LV_STATE_DEFAULT);
|
|||
} |
|||
|
|||
void UIManager::addChatBubble(const char *time_str, const char *sender, const char *msg,bool is_self) |
|||
{ |
|||
// Remove oldest
|
|||
if (chat_count >= MAX_CHAT_MESSAGES) { |
|||
lv_obj_del(chat_items[0]); |
|||
memmove(&chat_items[0], &chat_items[1], sizeof(lv_obj_t*) * (MAX_CHAT_MESSAGES - 1)); |
|||
chat_count--; |
|||
} |
|||
|
|||
// Row container (align bubble left/right)
|
|||
lv_obj_t *row = lv_obj_create(ui_ChannelMessages); |
|||
lv_obj_set_width(row, lv_pct(100)); |
|||
lv_obj_set_height(row, LV_SIZE_CONTENT); |
|||
lv_obj_set_style_bg_opa(row, 0, 0); |
|||
lv_obj_set_style_pad_all(row, 0, 0); |
|||
lv_obj_set_style_border_width(row, 0, 0); |
|||
lv_obj_set_style_outline_width(row, 0, 0); |
|||
lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE); |
|||
|
|||
lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW); |
|||
lv_obj_set_flex_align(row, |
|||
is_self ? LV_FLEX_ALIGN_END : LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START); |
|||
|
|||
// Bubble container (COLUMN)
|
|||
lv_obj_t *bubble = lv_obj_create(row); |
|||
lv_obj_set_width(bubble, lv_pct(85)); |
|||
lv_obj_set_height(bubble, LV_SIZE_CONTENT); |
|||
lv_obj_set_style_radius(bubble, 12, 0); |
|||
lv_obj_set_style_pad_all(bubble, 10, 0); |
|||
lv_obj_set_style_bg_color(bubble, |
|||
is_self ? lv_color_hex(0x1E88E5) : lv_color_hex(0x2C2C2C), 0); |
|||
|
|||
// IMPORTANT: vertical layout inside bubble
|
|||
lv_obj_set_flex_flow(bubble, LV_FLEX_FLOW_COLUMN); |
|||
lv_obj_set_flex_align(bubble, |
|||
LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START); |
|||
|
|||
// Header row (sender + time)
|
|||
lv_obj_t *hdr = lv_obj_create(bubble); |
|||
lv_obj_set_width(hdr, lv_pct(100)); |
|||
lv_obj_set_height(hdr, LV_SIZE_CONTENT); |
|||
lv_obj_set_style_bg_opa(hdr, 0, 0); |
|||
lv_obj_set_style_border_width(hdr, 0, 0); |
|||
lv_obj_set_style_pad_all(hdr, 0, 0); |
|||
lv_obj_set_style_outline_width(hdr, 0, 0); |
|||
|
|||
lv_obj_set_flex_flow(hdr, LV_FLEX_FLOW_ROW); |
|||
lv_obj_set_flex_align(hdr, |
|||
LV_FLEX_ALIGN_SPACE_BETWEEN, |
|||
LV_FLEX_ALIGN_START, //LV_FLEX_ALIGN_CENTER, // Ευθυγράμμιση ονόματος/ώρας στον κάθετο άξονα
|
|||
LV_FLEX_ALIGN_START); |
|||
|
|||
lv_obj_t *lbl_sender = lv_label_create(hdr); |
|||
lv_label_set_text(lbl_sender, sender); |
|||
lv_obj_set_style_text_color(lbl_sender, |
|||
is_self ? lv_color_hex(0xE3F2FD) : lv_color_hex(0x90CAF9), 0); |
|||
lv_obj_set_style_text_font(lbl_sender, &lv_font_arial_22, 0); |
|||
|
|||
lv_obj_t *lbl_time = lv_label_create(hdr); |
|||
lv_label_set_text(lbl_time, time_str); |
|||
lv_obj_set_style_text_color(lbl_time, lv_color_hex(0xB0B0B0), 0); |
|||
lv_obj_set_style_text_font(lbl_time, &lv_font_arial_20, 0); |
|||
|
|||
// Message body (below header)
|
|||
lv_obj_t *lbl_msg = lv_label_create(bubble); |
|||
lv_label_set_text(lbl_msg, msg); |
|||
lv_label_set_long_mode(lbl_msg, LV_LABEL_LONG_WRAP); |
|||
lv_obj_set_width(lbl_msg, lv_pct(100)); |
|||
|
|||
lv_obj_set_style_text_color(lbl_msg, lv_color_hex(0xFFFFFF), 0); |
|||
lv_obj_set_style_text_font(lbl_msg, &lv_font_arial_26, 0); |
|||
|
|||
// Spacing between header and text
|
|||
//lv_obj_set_style_margin_top(lbl_msg, 6, 0);
|
|||
lv_obj_set_style_pad_row(bubble, 6, 0); |
|||
|
|||
chat_items[chat_count++] = row; |
|||
|
|||
lv_obj_scroll_to_view(row, LV_ANIM_ON); |
|||
} |
|||
|
|||
void UIManager::addPrivateChatBubble(const char *time_str, const char *msg, bool is_self) { |
|||
|
|||
lv_obj_set_style_pad_bottom(ui_ContactMessages, 20, 0); |
|||
|
|||
// 1. Row container
|
|||
lv_obj_t* row = LvObj(ui_ContactMessages) |
|||
.width(lv_pct(100)) |
|||
.height(LV_SIZE_CONTENT) |
|||
.bgOpa(0) |
|||
.border(0) |
|||
.padAll(4) |
|||
.scrollable(false) |
|||
.flexFlow(LV_FLEX_FLOW_ROW) |
|||
.flexAlign( |
|||
is_self ? LV_FLEX_ALIGN_END : LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START |
|||
); |
|||
|
|||
// 2. Aligner (Column) - label and time
|
|||
lv_obj_t* aligner = LvObj(row) |
|||
.width(LV_SIZE_CONTENT) |
|||
.height(LV_SIZE_CONTENT) |
|||
.bgOpa(0) |
|||
.border(0) |
|||
.padAll(0) |
|||
.scrollable(false) |
|||
.flexFlow(LV_FLEX_FLOW_ROW) |
|||
.flexAlign( |
|||
is_self ? LV_FLEX_ALIGN_END : LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START, |
|||
LV_FLEX_ALIGN_START |
|||
); |
|||
lv_obj_set_style_pad_row(aligner, 4, 0); |
|||
|
|||
// 3. Το Label-Bubble (Ενοποιημένο)
|
|||
lv_obj_t *lbl_msg = lv_label_create(aligner); |
|||
|
|||
// Long mode για wrap
|
|||
lv_label_set_long_mode(lbl_msg, LV_LABEL_LONG_WRAP); |
|||
lv_label_set_text(lbl_msg, msg); |
|||
lv_obj_set_style_text_font(lbl_msg, &lv_font_arial_22, 0); |
|||
|
|||
// Fixed max width για wrap
|
|||
lv_obj_set_width(lbl_msg, 400); // max πλάτος
|
|||
lv_obj_set_height(lbl_msg, LV_SIZE_CONTENT); // αυτόματο ύψος
|
|||
|
|||
// Bubble style
|
|||
lv_obj_set_style_bg_opa(lbl_msg, LV_OPA_COVER, 0); |
|||
lv_obj_set_style_radius(lbl_msg, 12, 0); |
|||
lv_obj_set_style_pad_all(lbl_msg, 12, 0); |
|||
|
|||
if(is_self) { |
|||
lv_obj_set_style_bg_color(lbl_msg, lv_color_hex(0x1E88E5), 0); |
|||
lv_obj_set_style_text_color(lbl_msg, lv_color_hex(0xFFFFFF), 0); |
|||
} else { |
|||
lv_obj_set_style_bg_color(lbl_msg, lv_color_hex(0xFFFFFF), 0); |
|||
lv_obj_set_style_text_color(lbl_msg, lv_color_hex(0x000000), 0); |
|||
} |
|||
|
|||
// 4. Η Ώρα
|
|||
lv_obj_t *lbl_time = lv_label_create(aligner); |
|||
lv_label_set_text(lbl_time, time_str); |
|||
lv_obj_set_style_text_color(lbl_time, lv_color_hex(0x808080), 0); |
|||
lv_obj_set_style_text_font(lbl_time, &lv_font_arial_14, 0); |
|||
|
|||
lv_obj_scroll_to_view(row, LV_ANIM_ON); |
|||
} |
|||
|
|||
void UIManager::getInitials(const char *name, char *out) { |
|||
out[0] = 0; |
|||
if (!name || !name[0]) return; |
|||
|
|||
const char *p = name; |
|||
while (*p && !isalnum((unsigned char)*p)) { |
|||
p++; |
|||
} |
|||
|
|||
char first = (*p) ? *p : name[0]; |
|||
char second = 0; |
|||
const char *space = strchr(name, ' '); |
|||
|
|||
if (space) { |
|||
const char *s = space + 1; |
|||
while (*s && !isalnum((unsigned char)*s)) { |
|||
s++; |
|||
} |
|||
if (*s) { |
|||
second = *s; |
|||
} |
|||
} |
|||
|
|||
out[0] = toupper((unsigned char)first); |
|||
if (second) { |
|||
out[1] = toupper((unsigned char)second); |
|||
out[2] = 0; |
|||
} else { |
|||
out[1] = 0; |
|||
} |
|||
} |
|||
|
|||
void UIManager::formatLastSeen(uint32_t ts, char *out, size_t len) { |
|||
if (ts == 0) { |
|||
snprintf(out, len, "Never"); |
|||
return; |
|||
} |
|||
|
|||
time_t t = (time_t)ts; |
|||
struct tm *tm = localtime(&t); |
|||
|
|||
if (tm == nullptr) { |
|||
snprintf(out, len, "Unknown"); |
|||
return; |
|||
} |
|||
|
|||
snprintf(out, len, "%02d:%02d %02d/%02d/%02d", |
|||
tm->tm_hour, |
|||
tm->tm_min, |
|||
tm->tm_mday, |
|||
tm->tm_mon + 1, |
|||
(tm->tm_year + 1900) % 100); // Προσθέτουμε το 1900 και παίρνουμε τα τελευταία 2 ψηφία
|
|||
} |
|||
|
|||
static void onContactClick(lv_event_t *e) |
|||
{ |
|||
UIManager *self = (UIManager*) lv_event_get_user_data(e); |
|||
if(self) self->handleContactClick(e); |
|||
} |
|||
|
|||
void UIManager::handleContactClick(lv_event_t *e) |
|||
{ |
|||
lv_obj_t *row = lv_event_get_target(e); |
|||
ContactInfo *c = (ContactInfo*) lv_obj_get_user_data(row); |
|||
|
|||
Serial.printf("Clicked: %s\n", c->name); |
|||
} |
|||
|
|||
void UIManager::addContactToUI(ContactInfo c) |
|||
{ |
|||
const int ROW_W = 200; |
|||
const int ROW_H = 64; |
|||
const int AVATAR = 44; |
|||
const int PAD = 4; |
|||
|
|||
// ============================
|
|||
// List row button
|
|||
// ============================
|
|||
lv_obj_t* btn = lv_list_add_btn(ui_Contacts, nullptr, nullptr); |
|||
|
|||
LvObj(btn, true) |
|||
.size(ROW_W, ROW_H) |
|||
.padAll(0) |
|||
.bgColor(0x000000) |
|||
.bgOpa(LV_OPA_COVER) |
|||
.noDecor() |
|||
.border(1) |
|||
.scrollable(false); |
|||
|
|||
lv_obj_set_layout(btn, 0); |
|||
lv_obj_set_style_border_side(btn, LV_BORDER_SIDE_BOTTOM, 0); |
|||
lv_obj_set_style_border_color(btn, lv_color_hex(0x222222), 0); |
|||
lv_obj_set_style_bg_color(btn, lv_color_hex(0x111111), LV_STATE_PRESSED); |
|||
|
|||
// Store ContactInfo
|
|||
auto* store = new ContactInfo(c); |
|||
lv_obj_set_user_data(btn, store); |
|||
|
|||
lv_obj_add_event_cb(btn, onContactClick, LV_EVENT_CLICKED, this); |
|||
|
|||
// ============================
|
|||
// Avatar container
|
|||
// ============================
|
|||
lv_obj_t* content = LvObj(btn) |
|||
.size(ROW_H, ROW_H) |
|||
.position(0, 0) |
|||
.padAll(0) |
|||
.bgOpa(LV_OPA_TRANSP) |
|||
.border(0) |
|||
.scrollable(false) |
|||
.raw(); |
|||
|
|||
// Avatar circle
|
|||
lv_obj_t* avatar = LvObj(content) |
|||
.size(AVATAR, AVATAR) |
|||
.position(0, 10) |
|||
.radius(LV_RADIUS_CIRCLE) |
|||
.bgColor(0x4A90E2) |
|||
.border(0) |
|||
.raw(); |
|||
|
|||
// Initials
|
|||
char initials[4]; |
|||
if (c.type == ADV_TYPE_REPEATER) { |
|||
strcpy(initials, "(R)"); |
|||
} else { |
|||
getInitials(c.name, initials); |
|||
} |
|||
|
|||
LvLabel(avatar) |
|||
.text(initials) |
|||
.font(&lv_font_arial_22) |
|||
.textColor(0xFFFFFF) |
|||
.align(LV_ALIGN_CENTER); |
|||
|
|||
// ============================
|
|||
// Text column
|
|||
// ============================
|
|||
int text_x = PAD + AVATAR + PAD; |
|||
int text_w = ROW_W - text_x - PAD; |
|||
|
|||
lv_obj_t* text_col = LvObj(btn) |
|||
.position(text_x, 0) |
|||
.size(text_w, ROW_H - PAD) |
|||
.padAll(0) |
|||
.bgOpa(LV_OPA_TRANSP) |
|||
.border(0) |
|||
.scrollable(false) |
|||
.raw(); |
|||
|
|||
// Name label
|
|||
LvLabel(text_col) |
|||
.text(c.name) |
|||
.position(0, PAD + 4) |
|||
.width(text_w) |
|||
.font(&lv_font_arial_20) |
|||
.textColor(0xFFFFFF) |
|||
.wrap(false); |
|||
|
|||
// Last seen
|
|||
char lastSeen[32]; |
|||
formatLastSeen(c.lastmod, lastSeen, sizeof(lastSeen)); |
|||
|
|||
LvLabel(text_col) |
|||
.text(lastSeen) |
|||
.position(0, PAD + 32) |
|||
.width(text_w) |
|||
.font(&lv_font_arial_16) |
|||
.textColor(0x888888) |
|||
.wrap(false); |
|||
|
|||
// ============================
|
|||
// Disable child clicks
|
|||
// ============================
|
|||
LvObj(avatar, true).clickable(false); |
|||
LvObj(text_col, true).clickable(false); |
|||
} |
|||
|
|||
void UIManager::onShowKeyboard() |
|||
{ |
|||
LvKeyboard(ui_Keyboard, true).show(true); |
|||
LvObj(ui_DimOverlay, true).clickable(true); |
|||
LvObj(ui_ChannelInput, true).positionY(channelInputBaseKeybOnY); |
|||
LvObj(ui_SendBtn, true).positionY(channelInputBaseKeybOnY); |
|||
} |
|||
|
|||
void UIManager::onHideKeyboard() |
|||
{ |
|||
LvKeyboard(ui_Keyboard, true).show(false); |
|||
LvObj(ui_DimOverlay, true).clickable(false); |
|||
LvObj(ui_ChannelInput, true).positionY(channelInputBaseY); |
|||
LvObj(ui_SendBtn, true).positionY(channelInputBaseY); |
|||
} |
|||
|
|||
static void s_onChannelInputFocus(lv_event_t *e) |
|||
{ |
|||
UIManager *self = (UIManager*) lv_event_get_user_data(e); |
|||
if(self) self->onChannelInputFocus(e); |
|||
} |
|||
|
|||
void UIManager::onChannelInputFocus(lv_event_t* e) |
|||
{ |
|||
lv_obj_t* ta = lv_event_get_target(e); |
|||
if(!ui_Keyboard || !ta) return; |
|||
|
|||
lv_keyboard_set_textarea(ui_Keyboard, ta); |
|||
onShowKeyboard(); |
|||
} |
|||
|
|||
static void s_onSendClick(lv_event_t *e) |
|||
{ |
|||
UIManager *self = (UIManager*) lv_event_get_user_data(e); |
|||
if(self) self->onSendClick(e); |
|||
} |
|||
|
|||
void UIManager::onSendClick(lv_event_t* e) |
|||
{ |
|||
|
|||
char fullMessage[260]; |
|||
char msgCopy[200]; |
|||
|
|||
const char* msg = lv_textarea_get_text(ui_ChannelInput); |
|||
if(msg == NULL || msg[0] == '\0') return; |
|||
|
|||
strncpy(msgCopy, msg, sizeof(msgCopy) - 1); |
|||
msgCopy[sizeof(msgCopy) - 1] = '\0'; |
|||
|
|||
lv_textarea_set_text(ui_ChannelInput, ""); |
|||
|
|||
snprintf(fullMessage, sizeof(fullMessage), "public %s", msgCopy); |
|||
//handleCommand(fullMessage);
|
|||
handleCommand(msgCopy); |
|||
|
|||
char time_buf[16]; |
|||
time_t now = time(NULL); |
|||
struct tm t; |
|||
localtime_r(&now, &t); |
|||
|
|||
sprintf(time_buf, "%02d:%02d:%02d\n", t.tm_hour, t.tm_min, t.tm_sec); |
|||
addChatBubble(time_buf, "Me", msgCopy, true); |
|||
|
|||
onHideKeyboard(); |
|||
} |
|||
|
|||
static void s_onKeyboardEvent(lv_event_t *e) |
|||
{ |
|||
UIManager *self = (UIManager*) lv_event_get_user_data(e); |
|||
if(self) self->onKeyboardEvent(e); |
|||
} |
|||
|
|||
void UIManager::onKeyboardEvent(lv_event_t* e) |
|||
{ |
|||
lv_event_code_t code = lv_event_get_code(e); |
|||
|
|||
if(code == LV_EVENT_READY || code == LV_EVENT_CANCEL) |
|||
{ |
|||
LvKeyboard(ui_Keyboard, true).show(false); |
|||
|
|||
LvObj(ui_ChannelInput, true).positionY(channelInputBaseY); |
|||
LvObj(ui_SendBtn, true).positionY(channelInputBaseY); |
|||
|
|||
LvObj(ui_DimOverlay, true) |
|||
.bgOpa(0) |
|||
.clickable(false); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void s_onDimOverlayClick(lv_event_t *e) |
|||
{ |
|||
UIManager *self = (UIManager*) lv_event_get_user_data(e); |
|||
if(self) self->onDimOverlayClick(e); |
|||
} |
|||
|
|||
void UIManager::onDimOverlayClick(lv_event_t* e) |
|||
{ |
|||
onHideKeyboard(); |
|||
} |
|||
|
|||
static void onScrollBeginEvent(lv_event_t *e) |
|||
{ |
|||
UIManager *self = (UIManager*) lv_event_get_user_data(e); |
|||
if(self) self->scroll_begin_event(e); |
|||
} |
|||
|
|||
void UIManager::scroll_begin_event(lv_event_t *e) |
|||
{ |
|||
if (lv_event_get_code(e) == LV_EVENT_SCROLL_BEGIN) { |
|||
lv_anim_t* a = (lv_anim_t*)lv_event_get_param(e); |
|||
if (a) a->time = 0; |
|||
|
|||
} |
|||
} |
|||
|
|||
void UIManager::ui_Screen1_screen_init(void) |
|||
{ |
|||
//lv_disp_set_rotation(disp, LV_DISP_ROT_90);
|
|||
//ui_Screen1 = lv_obj_create(NULL);
|
|||
|
|||
ui_Screen1 = LvObj(NULL) |
|||
.scrollable(false) |
|||
.bgColor(0x000000) |
|||
.bgOpa(255); |
|||
|
|||
LvTabView tabView(ui_Screen1); |
|||
tabView |
|||
.size(480, 480) |
|||
.align(LV_ALIGN_CENTER) |
|||
.bgColor(0x000000) |
|||
.contentNoScroll() |
|||
.tabBtnBg(0x424242) |
|||
.tabBtnText(0xFFFFFF, &lv_font_arial_18); |
|||
|
|||
ui_TabView1 = tabView.raw(); |
|||
|
|||
#if defined(LANG_EN) |
|||
ui_TabPageHome = ui_TabView1.addTab("Home"); |
|||
ui_TabPageContacts = ui_TabView1.addTab("Contacts"); |
|||
ui_TabPageChannels = ui_TabView1.addTab("Channels"); |
|||
ui_TabPageSettings = ui_TabView1.addTab("Settings"); |
|||
#elif defined(LANG_GR) |
|||
ui_TabPageHome = tabView.addTab("Αρχική"); |
|||
ui_TabPageContacts = tabView.addTab("Επαφές"); |
|||
ui_TabPageChannels = tabView.addTab("Κανάλια"); |
|||
ui_TabPageSettings = tabView.addTab("Ρυθμίσεις"); |
|||
#endif |
|||
|
|||
LvObj(ui_TabPageHome) |
|||
.scrollable(false) |
|||
.bgOpa(0) |
|||
.bgColor(0x000000); |
|||
LvObj(ui_TabPageContacts) |
|||
.scrollable(false) |
|||
.bgOpa(0) |
|||
.bgColor(0x000000); |
|||
LvObj(ui_TabPageChannels) |
|||
.scrollable(false) |
|||
.bgOpa(0) |
|||
.bgColor(0x000000); |
|||
LvObj(ui_TabPageSettings) |
|||
.scrollable(false) |
|||
.bgOpa(0) |
|||
.bgColor(0x000000); |
|||
|
|||
ui_ValueDate = LvLabel(ui_TabPageHome) |
|||
.text("--- --/--/----") |
|||
.width(LV_SIZE_CONTENT) |
|||
.height(LV_SIZE_CONTENT) |
|||
.font(&lv_font_arial_40) |
|||
.textColor(0xFFFFFF) |
|||
.opa(255) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(0, -165); |
|||
|
|||
|
|||
ui_ValueTime = LvLabel(ui_TabPageHome) |
|||
.text("--:--") |
|||
.width(LV_SIZE_CONTENT) |
|||
.height(LV_SIZE_CONTENT) |
|||
.font(&lv_font_arial_48) |
|||
.textColor(0xFFFFFF) |
|||
.opa(255) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(0, -100); |
|||
|
|||
ui_Contacts = LvList(ui_TabPageContacts) |
|||
.width(250) |
|||
.height(400) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(-274, 0) |
|||
.transparent() |
|||
.raw(); |
|||
|
|||
lv_obj_set_style_bg_opa(ui_Contacts, LV_OPA_TRANSP, 0); |
|||
lv_obj_set_style_border_width(ui_Contacts, 0, 0); |
|||
lv_obj_set_style_outline_width(ui_Contacts, 0, 0); |
|||
lv_obj_set_style_shadow_width(ui_Contacts, 0, 0); |
|||
//lv_obj_set_scrollbar_mode(ui_Contacts, LV_SCROLLBAR_MODE_OFF);
|
|||
lv_obj_set_style_bg_opa(ui_Contacts, LV_OPA_TRANSP, LV_PART_ITEMS); |
|||
lv_obj_set_style_border_width(ui_Contacts, 0, LV_PART_ITEMS); |
|||
|
|||
ui_ContactMessages = LvList(ui_TabPageContacts) |
|||
.width(500) |
|||
.height(400) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(124, 0) |
|||
.transparent() |
|||
.raw(); |
|||
|
|||
lv_obj_set_style_bg_color(ui_ContactMessages, lv_color_hex(0), 0); |
|||
lv_obj_set_style_bg_opa(ui_ContactMessages, LV_OPA_TRANSP, 0); |
|||
lv_obj_set_style_border_width(ui_ContactMessages, 0, 0); |
|||
lv_obj_set_style_outline_width(ui_ContactMessages, 0, 0); |
|||
lv_obj_set_style_shadow_width(ui_ContactMessages, 0, 0); |
|||
//lv_obj_set_scrollbar_mode(ui_ContactMessages, LV_SCROLLBAR_MODE_OFF);
|
|||
lv_obj_set_style_bg_opa(ui_ContactMessages, LV_OPA_TRANSP, LV_PART_ITEMS); |
|||
lv_obj_set_style_border_width(ui_ContactMessages, 0, LV_PART_ITEMS); |
|||
|
|||
// LvObj(ui_TabPageContacts)
|
|||
// .size(2, 400)
|
|||
// .position(222, 0)
|
|||
// .bgColor(0x444444)
|
|||
// .border(0)
|
|||
// .scrollable(false)
|
|||
// .radius(0);
|
|||
|
|||
ui_Channels = LvDropdown(ui_TabPageChannels) |
|||
.options("Public") |
|||
.width(291) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(-243, -182) |
|||
.clickable(true) |
|||
.raw(); |
|||
|
|||
ui_ChannelMessages = LvList(ui_TabPageChannels) |
|||
.width(780) |
|||
.height(280) |
|||
.align(LV_ALIGN_CENTER) |
|||
.transparent() |
|||
.padRow(10) |
|||
.position(0, 0) |
|||
.bgColor(0) |
|||
.bgOpa(0) |
|||
.border(0) |
|||
.noDecor() |
|||
.raw(); |
|||
|
|||
//lv_obj_set_scrollbar_mode(ui_ChannelMessages, LV_SCROLLBAR_MODE_OFF);
|
|||
lv_obj_set_style_bg_opa(ui_ChannelMessages, LV_OPA_TRANSP, LV_PART_ITEMS); |
|||
lv_obj_set_style_border_width(ui_ChannelMessages, 0, LV_PART_ITEMS); |
|||
|
|||
ui_ChannelDivider = LvObj(ui_TabPageChannels) |
|||
.size(780, 1) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(0, 150) |
|||
.bgColor(0x444444) |
|||
.border(0) |
|||
.raw(); |
|||
|
|||
ui_DimOverlay = LvObj(ui_Screen1) |
|||
.size(lv_pct(100), lv_pct(100)) |
|||
.align(LV_ALIGN_CENTER) |
|||
.bgColor(0x000000) |
|||
.bgOpa(0) |
|||
.bringToFront() |
|||
.onClick(s_onDimOverlayClick, this) |
|||
.scrollable(false) |
|||
.clickable(true) |
|||
.raw(); |
|||
lv_obj_remove_style_all(ui_DimOverlay); // no border/padding
|
|||
lv_obj_clear_flag(ui_DimOverlay, LV_OBJ_FLAG_SCROLL_CHAIN_HOR); |
|||
lv_obj_clear_flag(ui_DimOverlay, LV_OBJ_FLAG_SCROLL_CHAIN_VER); |
|||
|
|||
|
|||
ui_ChannelInput = LvTextArea(ui_TabPageChannels) |
|||
.size(670, 40) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(-50, channelInputBaseY) |
|||
.oneLine(true) |
|||
#if defined(LANG_EN) |
|||
.placeholder("Write message...") |
|||
#elif defined(LANG_GR) |
|||
.placeholder("Γράψε μήνυμα...") |
|||
#endif |
|||
.font(&lv_font_arial_20) |
|||
.bgColor(0x111111) |
|||
.textColor(0xFFFFFF) |
|||
.borderColor(0x444444) |
|||
.borderWidth(1) |
|||
.radius(6) |
|||
.onFocus(s_onChannelInputFocus, this) |
|||
.raw(); |
|||
|
|||
ui_SendBtn = LvButton(ui_TabPageChannels) |
|||
.size(90, 42) |
|||
.align(LV_ALIGN_CENTER) |
|||
.position(350, channelInputBaseY) |
|||
.bgColor(0x3A7AFE) |
|||
.onClick(s_onSendClick, this) |
|||
.raw(); |
|||
|
|||
iu_SendLabel = LvLabel(ui_SendBtn) |
|||
#if defined(LANG_EN) |
|||
.text("Send") |
|||
#elif defined(LANG_GR) |
|||
.text("Αποστολή") |
|||
#endif |
|||
.font(&lv_font_arial_18); |
|||
lv_obj_center(iu_SendLabel); |
|||
|
|||
ui_Keyboard = LvKeyboard(lv_layer_top()) |
|||
.size(480, 200) |
|||
.align(LV_ALIGN_BOTTOM_MID) |
|||
.show(false) |
|||
.onEvent(s_onKeyboardEvent, this); |
|||
} |
|||
|
|||
void UIManager::setNightMode(bool night) { |
|||
|
|||
if (!ui_DimOverlay) return; |
|||
if (night) { |
|||
lv_obj_set_style_bg_opa(ui_DimOverlay, 192, 0); // 75% dark
|
|||
} else { |
|||
lv_obj_set_style_bg_opa(ui_DimOverlay, 0, 0); // none
|
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,50 @@ |
|||
#include <Arduino.h> |
|||
|
|||
#include "esp_log.h" |
|||
#include "uiDefines.h" |
|||
#include "uiTasks.h" |
|||
#include "uiVars.h" |
|||
|
|||
// Tasks
|
|||
TaskHandle_t t_core0_lvgl; |
|||
TaskHandle_t t_core1_clock; |
|||
TaskHandle_t t_core1_core; |
|||
|
|||
#define TAG "createTasks" |
|||
|
|||
void createTasks() { |
|||
Serial.println("Creating Tasks..."); |
|||
|
|||
xTaskCreatePinnedToCore( |
|||
lvgl_task, // Task function.
|
|||
"LVGL_Manager", // Name of task.
|
|||
10000, // Stack size of task
|
|||
NULL, // Parameter of the task
|
|||
5, // Priority of the task
|
|||
&t_core0_lvgl, // Task handle to keep track of created task
|
|||
0); // Pin task to core 0
|
|||
|
|||
xTaskCreatePinnedToCore( |
|||
core_task, // Task function.
|
|||
"MeshCore", // Name of task.
|
|||
10000, // Stack size of task
|
|||
NULL, // Parameter of the task
|
|||
4, // Priority of the task
|
|||
&t_core1_core, // Task handle to keep track of created task
|
|||
1); // Pin task to core 1
|
|||
|
|||
xTaskCreatePinnedToCore( |
|||
clock_task, // Task function.
|
|||
"CLOCK_Manager", // Name of task.
|
|||
10000, // Stack size of task
|
|||
NULL, // Parameter of the task
|
|||
4, // Priority of the task
|
|||
&t_core1_clock, // Task handle to keep track of created task
|
|||
1); // Pin task to core 1
|
|||
|
|||
ESP_LOGD(TAG, "All tasks created\nStarting tasks..."); |
|||
|
|||
vTaskResume(t_core0_lvgl); |
|||
vTaskResume(t_core1_clock); |
|||
|
|||
} |
|||
Loading…
Reference in new issue