Files
ink-board/main/ui/apps/travel/ui/settings.cpp
GW_MC c4635948e4 feat(travel): Implement settings UI and web server for MTR route configuration
- Added MainUIHandler class to manage the main UI and polling for arrival data.
- Introduced SettingsUI class for displaying QR code and configuration options.
- Created SettingsUIHandler to manage settings UI lifecycle and web server interactions.
- Developed WebHandler to handle HTTP requests for MTR route settings, including adding and removing routes.
- Implemented web endpoints for fetching MTR lines, routes, and saving settings.
- Enhanced UI with responsive design for e-ink displays and added error handling for web interactions.
2026-02-03 19:26:53 +08:00

151 lines
4.5 KiB
C++

#include "ui/apps/travel/ui/settings.h"
#include "display/lvgl_handler.h"
#include "esp_log.h"
static const char* TAG = "TravelSettingsUI";
namespace travel {
SettingsUI::SettingsUI() = default;
SettingsUI::~SettingsUI() {
deinit();
}
esp_err_t SettingsUI::init(lv_obj_t* parent) {
if (!parent) {
ESP_LOGE(TAG, "Parent is null");
return ESP_ERR_INVALID_ARG;
}
parent_ = parent;
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
return ESP_ERR_TIMEOUT;
}
// Create main container
container_ = lv_obj_create(parent_);
lv_obj_set_size(container_, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(container_, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(container_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_all(container_, 10, 0);
lv_obj_set_style_bg_opa(container_, LV_OPA_TRANSP, 0);
lv_obj_set_style_border_width(container_, 0, 0);
// Disable animations and scrolling for e-ink
lv_obj_set_style_anim_time(container_, 0, 0);
lv_obj_clear_flag(container_, LV_OBJ_FLAG_SCROLLABLE);
// Title
title_label_ = lv_label_create(container_);
lv_label_set_text(title_label_, "設定路線");
lv_obj_set_style_text_font(title_label_, &lv_font_montserrat_14, 0);
lv_obj_set_style_pad_bottom(title_label_, 10, 0);
// QR Code container
lv_obj_t* qr_container = lv_obj_create(container_);
lv_obj_set_size(qr_container, QR_CODE_SIZE + 10, QR_CODE_SIZE + 10);
lv_obj_set_style_bg_color(qr_container, lv_color_white(), 0);
lv_obj_set_style_border_width(qr_container, 2, 0);
lv_obj_set_style_border_color(qr_container, lv_color_black(), 0);
lv_obj_set_style_anim_time(qr_container, 0, 0);
lv_obj_clear_flag(qr_container, LV_OBJ_FLAG_SCROLLABLE);
// QR Code
qr_code_ = lv_qrcode_create(qr_container);
lv_qrcode_set_size(qr_code_, QR_CODE_SIZE);
lv_qrcode_set_dark_color(qr_code_, lv_color_black());
lv_qrcode_set_light_color(qr_code_, lv_color_white());
lv_obj_center(qr_code_);
// URL label
url_label_ = lv_label_create(container_);
lv_obj_set_width(url_label_, LV_PCT(100));
lv_label_set_text(url_label_, "");
lv_obj_set_style_text_font(url_label_, &lv_font_montserrat_14, 0);
lv_label_set_long_mode(url_label_, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_align(url_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_pad_top(url_label_, 10, 0);
// Status message
status_label_ = lv_label_create(container_);
lv_obj_set_width(status_label_, LV_PCT(100));
lv_label_set_text(status_label_, "正在啟動伺服器...");
lv_obj_set_style_text_font(status_label_, &lv_font_montserrat_14, 0);
lv_label_set_long_mode(status_label_, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_pad_top(status_label_, 15, 0);
// Instructions
instruction_label_ = lv_label_create(container_);
lv_obj_set_width(instruction_label_, LV_PCT(100));
lv_label_set_text(instruction_label_,
"請使用手機掃描QR碼或瀏覽器開啟網址\n"
"以設定MTR路線");
lv_obj_set_style_text_font(instruction_label_, &lv_font_montserrat_14, 0);
lv_label_set_long_mode(instruction_label_, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_align(instruction_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_color(instruction_label_, lv_color_hex(0x606060), 0);
lv_obj_set_style_pad_top(instruction_label_, 15, 0);
lvgl_port_unlock();
return ESP_OK;
}
esp_err_t SettingsUI::deinit() {
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
return ESP_ERR_TIMEOUT;
}
if (container_) {
lv_obj_del(container_);
container_ = nullptr;
}
title_label_ = nullptr;
qr_code_ = nullptr;
url_label_ = nullptr;
status_label_ = nullptr;
instruction_label_ = nullptr;
lvgl_port_unlock();
parent_ = nullptr;
return ESP_OK;
}
void SettingsUI::update_qr_code(const std::string& url) {
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
return;
}
if (qr_code_) {
lv_qrcode_update(qr_code_, url.c_str(), url.length());
}
if (url_label_) {
lv_label_set_text(url_label_, url.c_str());
}
lvgl_port_unlock();
}
void SettingsUI::update_status_message(const std::string& message) {
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
return;
}
if (status_label_) {
lv_label_set_text(status_label_, message.c_str());
}
lvgl_port_unlock();
}
} // namespace travel