Implement Serial Console app & more (#239)

- Implemented new app: Serial Console
- `Uart::writeString()`: fixed 2 mutex bugs
- `AlertDialog::start()` with default "OK" button added
- Created `tt::lvgl::defaultLockTime` for re-use
- Removed various usages of deprecated `lvgl::obj_set_style_no_padding()`
- Implemented `hal::uart::getNames()` to list all interface names
This commit is contained in:
Ken Van Hoeylandt
2025-03-07 21:58:52 +01:00
committed by GitHub
parent 83a82be901
commit 13d7e84ef3
19 changed files with 669 additions and 65 deletions
+2
View File
@@ -45,6 +45,7 @@ namespace app {
namespace log { extern const AppManifest manifest; }
namespace power { extern const AppManifest manifest; }
namespace selectiondialog { extern const AppManifest manifest; }
namespace serialconsole { extern const AppManifest manifest; }
namespace settings { extern const AppManifest manifest; }
namespace systeminfo { extern const AppManifest manifest; }
namespace textviewer { extern const AppManifest manifest; }
@@ -80,6 +81,7 @@ static void registerSystemApps() {
addApp(app::inputdialog::manifest);
addApp(app::launcher::manifest);
addApp(app::log::manifest);
addApp(app::serialconsole::manifest);
addApp(app::settings::manifest);
addApp(app::selectiondialog::manifest);
addApp(app::systeminfo::manifest);
@@ -31,6 +31,14 @@ void start(const std::string& title, const std::string& message, const std::vect
service::loader::startApp(manifest.id, bundle);
}
void start(const std::string& title, const std::string& message) {
auto bundle = std::make_shared<Bundle>();
bundle->putString(PARAMETER_BUNDLE_KEY_TITLE, title);
bundle->putString(PARAMETER_BUNDLE_KEY_MESSAGE, message);
bundle->putString(PARAMETER_BUNDLE_KEY_BUTTON_LABELS, "OK");
service::loader::startApp(manifest.id, bundle);
}
int32_t getResultIndex(const Bundle& bundle) {
int32_t index = -1;
bundle.optInt32(RESULT_BUNDLE_KEY_INDEX, index);
@@ -76,6 +84,7 @@ private:
lv_label_set_text(button_label, text.c_str());
lv_obj_add_event_cb(button, onButtonClickedCallback, LV_EVENT_SHORT_CLICKED, (void*)index);
}
public:
void onShow(AppContext& app, lv_obj_t* parent) override {
@@ -89,6 +98,7 @@ public:
lv_obj_t* message_label = lv_label_create(parent);
lv_obj_align(message_label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_width(message_label, LV_PCT(80));
lv_obj_set_style_text_align(message_label, LV_TEXT_ALIGN_CENTER, 0);
std::string message;
if (parameters->optString(PARAMETER_BUNDLE_KEY_MESSAGE, message)) {
@@ -107,21 +117,9 @@ public:
std::string items_concatenated;
if (parameters->optString(PARAMETER_BUNDLE_KEY_BUTTON_LABELS, items_concatenated)) {
std::vector<std::string> labels = string::split(items_concatenated, PARAMETER_ITEM_CONCATENATION_TOKEN);
if (labels.empty() || labels.front().empty()) {
TT_LOG_E(TAG, "No items provided");
setResult(Result::Error);
service::loader::stopApp();
} else if (labels.size() == 1) {
auto result_bundle = std::make_unique<Bundle>();
result_bundle->putInt32(RESULT_BUNDLE_KEY_INDEX, 0);
setResult(Result::Ok, std::move(result_bundle));
service::loader::stopApp();
TT_LOG_W(TAG, "Auto-selecting single item");
} else {
size_t index = 0;
for (const auto& label: labels) {
createButton(button_wrapper, label, index++);
}
size_t index = 0;
for (const auto& label: labels) {
createButton(button_wrapper, label, index++);
}
}
}
@@ -0,0 +1,96 @@
#include "Tactility/app/serialconsole/ConnectView.h"
#include "Tactility/app/serialconsole/ConsoleView.h"
#include "Tactility/lvgl/LvglSync.h"
#include "Tactility/lvgl/Style.h"
#include "Tactility/lvgl/Toolbar.h"
#include "Tactility/service/loader/Loader.h"
#include <Tactility/hal/uart/Uart.h>
#include <lvgl.h>
#define TAG "text_viewer"
namespace tt::app::serialconsole {
class SerialConsoleApp final : public App {
private:
lv_obj_t* disconnectButton = nullptr;
lv_obj_t* wrapperWidget = nullptr;
ConnectView connectView = ConnectView([this](auto uart){
showConsoleView(std::move(uart));
});
ConsoleView consoleView;
View* activeView = nullptr;
void stopActiveView() {
if (activeView != nullptr) {
activeView->onStop();
lv_obj_clean(wrapperWidget);
activeView = nullptr;
}
}
void showConsoleView(std::unique_ptr<hal::uart::Uart> uart) {
stopActiveView();
activeView = &consoleView;
consoleView.onStart(wrapperWidget, std::move(uart));
lv_obj_remove_flag(disconnectButton, LV_OBJ_FLAG_HIDDEN);
}
void showConnectView() {
stopActiveView();
activeView = &connectView;
connectView.onStart(wrapperWidget);
lv_obj_add_flag(disconnectButton, LV_OBJ_FLAG_HIDDEN);
}
void onDisconnect() {
// Changing views (calling ConsoleView::stop()) also disconnects the UART
showConnectView();
}
static void onDisconnectPressed(lv_event_t* event) {
auto* app = (SerialConsoleApp*)lv_event_get_user_data(event);
app->onDisconnect();
}
public:
SerialConsoleApp() = default;
void onShow(AppContext& app, lv_obj_t* parent) final {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
auto* toolbar = lvgl::toolbar_create(parent, app);
disconnectButton = lvgl::toolbar_add_button_action(toolbar, LV_SYMBOL_POWER, onDisconnectPressed, this);
lv_obj_add_flag(disconnectButton, LV_OBJ_FLAG_HIDDEN);
wrapperWidget = lv_obj_create(parent);
lv_obj_set_width(wrapperWidget, LV_PCT(100));
lv_obj_set_flex_grow(wrapperWidget, 1);
lv_obj_set_flex_flow(wrapperWidget, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_all(wrapperWidget, 0, 0);
lv_obj_set_style_border_width(wrapperWidget, 0, 0);
lvgl::obj_set_style_bg_invisible(wrapperWidget);
showConnectView();
}
void onHide(AppContext& app) final {
stopActiveView();
}
};
extern const AppManifest manifest = {
.id = "SerialConsole",
.name = "Serial Console",
.icon = LV_SYMBOL_LIST,
.type = Type::System,
.createApp = create<SerialConsoleApp>
};
} // namespace
+5 -2
View File
@@ -53,13 +53,16 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) {
auto* toolbar = (Toolbar*)obj;
obj_set_style_no_padding(obj);
lv_obj_set_style_pad_all(obj, 0, 0);
lv_obj_set_style_pad_gap(obj, 0, 0);
lv_obj_center(obj);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
toolbar->close_button = lv_button_create(obj);
lv_obj_set_size(toolbar->close_button, TOOLBAR_HEIGHT - 4, TOOLBAR_HEIGHT - 4);
obj_set_style_no_padding(toolbar->close_button);
lv_obj_set_style_pad_all(toolbar->close_button, 0, 0);
lv_obj_set_style_pad_gap(toolbar->close_button, 0, 0);
toolbar->close_button_image = lv_image_create(toolbar->close_button);
lv_obj_align(toolbar->close_button_image, LV_ALIGN_CENTER, 0, 0);
+3 -2
View File
@@ -47,13 +47,14 @@ Gui* gui_alloc() {
lv_obj_t* vertical_container = lv_obj_create(screen_root);
lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN);
lvgl::obj_set_style_no_padding(vertical_container);
lv_obj_set_style_pad_all(vertical_container, 0, 0);
lv_obj_set_style_pad_gap(vertical_container, 0, 0);
lvgl::obj_set_style_bg_blacken(vertical_container);
instance->statusbarWidget = lvgl::statusbar_create(vertical_container);
auto* app_container = lv_obj_create(vertical_container);
lvgl::obj_set_style_no_padding(app_container);
lv_obj_set_style_pad_all(app_container, 0, 0);
lv_obj_set_style_border_width(app_container, 0, 0);
lvgl::obj_set_style_bg_blacken(app_container);
lv_obj_set_width(app_container, LV_PCT(100));