diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index bdae9245..f88c0a23 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -59,13 +59,11 @@ namespace app { namespace alertdialog { extern const AppManifest manifest; } namespace applist { extern const AppManifest manifest; } namespace boot { extern const AppManifest manifest; } - namespace calculator { extern const AppManifest manifest; } namespace chat { extern const AppManifest manifest; } namespace development { extern const AppManifest manifest; } namespace display { extern const AppManifest manifest; } namespace files { extern const AppManifest manifest; } namespace fileselection { extern const AppManifest manifest; } - namespace gpio { extern const AppManifest manifest; } namespace gpssettings { extern const AppManifest manifest; } namespace i2cscanner { extern const AppManifest manifest; } namespace i2csettings { extern const AppManifest manifest; } @@ -102,11 +100,9 @@ namespace app { static void registerSystemApps() { addApp(app::alertdialog::manifest); addApp(app::applist::manifest); - addApp(app::calculator::manifest); addApp(app::display::manifest); addApp(app::files::manifest); addApp(app::fileselection::manifest); - addApp(app::gpio::manifest); addApp(app::imageviewer::manifest); addApp(app::inputdialog::manifest); addApp(app::launcher::manifest); diff --git a/Tactility/Source/app/calculator/Calculator.cpp b/Tactility/Source/app/calculator/Calculator.cpp deleted file mode 100644 index 6324474e..00000000 --- a/Tactility/Source/app/calculator/Calculator.cpp +++ /dev/null @@ -1,233 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace tt::app::calculator { - -constexpr const char* TAG = "Calculator"; - -class CalculatorApp : public App { - - lv_obj_t* displayLabel; - lv_obj_t* resultLabel; - char formulaBuffer[128] = {0}; // Stores the full input expression - bool newInput = true; - - static void button_event_cb(lv_event_t* e) { - CalculatorApp* self = static_cast(lv_event_get_user_data(e)); - lv_obj_t* btnm = lv_event_get_current_target_obj(e); - lv_event_code_t code = lv_event_get_code(e); - - uint32_t btn_id = lv_buttonmatrix_get_selected_button(btnm); - const char* txt = lv_buttonmatrix_get_button_text(btnm, btn_id); - - if (code == LV_EVENT_VALUE_CHANGED) { - self->handleInput(txt); - } - } - - void handleInput(const char* txt) { - if (std::strcmp(txt, "C") == 0) { - resetCalculator(); - return; - } - - if (std::strcmp(txt, "=") == 0) { - evaluateExpression(); - return; - } - - if (std::strlen(formulaBuffer) + std::strlen(txt) < sizeof(formulaBuffer) - 1) { - if (newInput) { - std::memset(formulaBuffer, 0, sizeof(formulaBuffer)); - newInput = false; - } - std::strcat(formulaBuffer, txt); - lv_label_set_text(displayLabel, formulaBuffer); - } - } - - void evaluateExpression() { - double result = computeFormula(); - - size_t formulaLen = std::strlen(formulaBuffer); - size_t maxAvailable = sizeof(formulaBuffer) - formulaLen - 1; - - if (maxAvailable > 10) { - char resultBuffer[32]; - snprintf(resultBuffer, sizeof(resultBuffer), " = %.8g", result); - strncat(formulaBuffer, resultBuffer, maxAvailable); - } else { - snprintf(formulaBuffer, sizeof(formulaBuffer), "%.8g", result); - } - - lv_label_set_text(displayLabel, "0"); - lv_label_set_text(resultLabel, formulaBuffer); - newInput = true; - } - - double computeFormula() { - return evaluateRPN(infixToRPN(std::string(formulaBuffer))); - } - - int precedence(char op) { - if (op == '+' || op == '-') return 1; - if (op == '*' || op == '/') return 2; - return 0; - } - - std::queue infixToRPN(const std::string& infix) { - std::stack opStack; - std::queue output; - std::string token; - size_t i = 0; - - while (i < infix.size()) { - char ch = infix[i]; - - if (isdigit(ch)) { - token.clear(); - while (i < infix.size() && (isdigit(infix[i]) || infix[i] == '.')) { - token += infix[i++]; - } - output.push(token); - continue; - } - - if (ch == '(') { - opStack.push(ch); - } else if (ch == ')') { - while (!opStack.empty() && opStack.top() != '(') { - output.push(std::string(1, opStack.top())); - opStack.pop(); - } - opStack.pop(); - } else if (strchr("+-*/", ch)) { - while (!opStack.empty() && precedence(opStack.top()) >= precedence(ch)) { - output.push(std::string(1, opStack.top())); - opStack.pop(); - } - opStack.push(ch); - } - - i++; - } - - while (!opStack.empty()) { - output.push(std::string(1, opStack.top())); - opStack.pop(); - } - - return output; - } - - double evaluateRPN(std::queue rpnQueue) { - std::stack values; - - while (!rpnQueue.empty()) { - std::string token = rpnQueue.front(); - rpnQueue.pop(); - - if (isdigit(token[0])) { - values.push(std::stod(token)); - } else if (strchr("+-*/", token[0])) { - if (values.size() < 2) return 0; - - double b = values.top(); - values.pop(); - double a = values.top(); - values.pop(); - - if (token[0] == '+') values.push(a + b); - else if (token[0] == '-') - values.push(a - b); - else if (token[0] == '*') - values.push(a * b); - else if (token[0] == '/' && b != 0) - values.push(a / b); - } - } - - return values.empty() ? 0 : values.top(); - } - - void resetCalculator() { - std::memset(formulaBuffer, 0, sizeof(formulaBuffer)); - lv_label_set_text(displayLabel, "0"); - lv_label_set_text(resultLabel, ""); - newInput = true; - } - - void onShow(AppContext& context, lv_obj_t* parent) override { - lv_obj_remove_flag(parent, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); - - lv_obj_t* toolbar = lvgl::toolbar_create(parent, context); - lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); - - lv_obj_t* wrapper = lv_obj_create(parent); - lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(wrapper, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START); - lv_obj_set_width(wrapper, LV_PCT(100)); - lv_obj_set_height(wrapper, LV_SIZE_CONTENT); - lv_obj_set_flex_grow(wrapper, 0); - lv_obj_set_style_pad_top(wrapper, 4, LV_PART_MAIN); - lv_obj_set_style_pad_bottom(wrapper, 4, LV_PART_MAIN); - lv_obj_set_style_pad_left(wrapper, 10, LV_PART_MAIN); - lv_obj_set_style_pad_right(wrapper, 10, LV_PART_MAIN); - lv_obj_set_style_pad_column(wrapper, 40, LV_PART_MAIN); - lv_obj_set_style_border_width(wrapper, 0, 0); - lv_obj_remove_flag(wrapper, LV_OBJ_FLAG_SCROLLABLE); - - displayLabel = lv_label_create(wrapper); - lv_label_set_text(displayLabel, "0"); - lv_obj_set_width(displayLabel, LV_SIZE_CONTENT); - lv_obj_set_align(displayLabel, LV_ALIGN_LEFT_MID); - - resultLabel = lv_label_create(wrapper); - lv_label_set_text(resultLabel, ""); - lv_obj_set_width(resultLabel, LV_SIZE_CONTENT); - lv_obj_set_align(resultLabel, LV_ALIGN_RIGHT_MID); - - static const char* btn_map[] = { - "(", ")", "C", "/", "\n", - "7", "8", "9", "*", "\n", - "4", "5", "6", "-", "\n", - "1", "2", "3", "+", "\n", - "0", "=", "", "", "" - }; - - lv_obj_t* btnm = lv_btnmatrix_create(parent); - lv_btnmatrix_set_map(btnm, btn_map); - - lv_obj_set_style_pad_all(btnm, 5, LV_PART_MAIN); - lv_obj_set_style_pad_row(btnm, 10, LV_PART_MAIN); - lv_obj_set_style_pad_column(btnm, 5, LV_PART_MAIN); - lv_obj_set_style_border_width(btnm, 2, LV_PART_MAIN); - lv_obj_set_style_bg_color(btnm, lv_palette_main(LV_PALETTE_BLUE), LV_PART_ITEMS); - - if (lv_display_get_horizontal_resolution(nullptr) <= 240 || lv_display_get_vertical_resolution(nullptr) <= 240) { //small screens - lv_obj_set_size(btnm, lv_pct(100), lv_pct(60)); - } else { //large screens - lv_obj_set_size(btnm, lv_pct(100), lv_pct(80)); - } - lv_obj_align(btnm, LV_ALIGN_BOTTOM_MID, 0, -5); - - lv_obj_add_event_cb(btnm, button_event_cb, LV_EVENT_VALUE_CHANGED, this); - } -}; - -extern const AppManifest manifest = { - .appId = "Calculator", - .appName = "Calculator", - .appIcon = TT_ASSETS_APP_ICON_CALCULATOR, - .createApp = create -}; - -} // namespace tt::app::calculator diff --git a/Tactility/Source/app/gpio/Gpio.cpp b/Tactility/Source/app/gpio/Gpio.cpp deleted file mode 100644 index f3c5f456..00000000 --- a/Tactility/Source/app/gpio/Gpio.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include -#include -#include -#include "Tactility/lvgl/Toolbar.h" -#include -#include -#include -#include - -namespace tt::app::gpio { - -extern const AppManifest manifest; - -class GpioApp : public App { - - std::vector pinWidgets; - std::vector pinStates; - std::unique_ptr timer; - Mutex mutex; - - static lv_obj_t* createGpioRowWrapper(lv_obj_t* parent); - void onTimer(); - -public: - - void onShow(AppContext& app, lv_obj_t* parent) override; - void onHide(AppContext& app) override; - - void startTask(); - void stopTask(); - - void updatePinStates(); - void updatePinWidgets(); -}; - -void GpioApp::updatePinStates() { - mutex.lock(); - // Update pin states - for (int i = 0; i < pinStates.size(); ++i) { - pinStates[i] = hal::gpio::getLevel(i); - } - mutex.unlock(); -} - -void GpioApp::updatePinWidgets() { - auto scoped_lvgl_lock = lvgl::getSyncLock()->asScopedLock(); - auto scoped_gpio_lock = mutex.asScopedLock(); - if (scoped_gpio_lock.lock() && scoped_lvgl_lock.lock(lvgl::defaultLockTime)) { - assert(pinStates.size() == pinWidgets.size()); - for (int j = 0; j < pinStates.size(); ++j) { - int level = pinStates[j]; - lv_obj_t* label = pinWidgets[j]; - void* label_user_data = lv_obj_get_user_data(label); - // The user data stores the state, so we can avoid unnecessary updates - if (reinterpret_cast(level) != label_user_data) { - lv_obj_set_user_data(label, reinterpret_cast(level)); - if (level == 0) { - lv_obj_set_style_text_color(label, lv_color_background_darkest(), LV_STATE_DEFAULT); - } else { - lv_obj_set_style_text_color(label, lv_color_make(0, 200, 0), LV_STATE_DEFAULT); - } - } - } - } -} - -lv_obj_t* GpioApp::createGpioRowWrapper(lv_obj_t* parent) { - lv_obj_t* wrapper = lv_obj_create(parent); - lv_obj_set_style_pad_all(wrapper, 0, LV_STATE_DEFAULT); - lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT); - lv_obj_set_size(wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT); - return wrapper; -} - -// region Task - -void GpioApp::onTimer() { - updatePinStates(); - updatePinWidgets(); -} - -void GpioApp::startTask() { - mutex.lock(); - assert(timer == nullptr); - timer = std::make_unique(Timer::Type::Periodic, [this] { - onTimer(); - }); - timer->start(100 / portTICK_PERIOD_MS); - mutex.unlock(); -} - -void GpioApp::stopTask() { - assert(timer); - - timer->stop(); - timer = nullptr; -} - -// endregion Task - -static int getSquareSpacing(hal::UiScale uiScale) { - if (uiScale == hal::UiScale::Smallest) { - return 0; - } else { - return 4; - } -} - -void GpioApp::onShow(AppContext& app, lv_obj_t* parent) { - auto ui_scale = hal::getConfiguration()->uiScale; - - lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); - lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); - - auto* toolbar = lvgl::toolbar_create(parent, app); - lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); - - // Main content wrapper, enables scrolling content without scrolling the toolbar - auto* expansion_wrapper = lv_obj_create(parent); - lv_obj_set_width(expansion_wrapper, LV_PCT(100)); - lv_obj_set_flex_grow(expansion_wrapper, 1); - lv_obj_set_style_border_width(expansion_wrapper, 0, LV_STATE_DEFAULT); - lv_obj_set_style_pad_all(expansion_wrapper, 0, LV_STATE_DEFAULT); - - auto* centering_wrapper = lv_obj_create(expansion_wrapper); - lv_obj_set_size(centering_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT); - lv_obj_set_align(centering_wrapper, LV_ALIGN_CENTER); - lv_obj_set_style_border_width(centering_wrapper, 0, LV_STATE_DEFAULT); - lv_obj_set_style_pad_all(centering_wrapper, 0, LV_STATE_DEFAULT); - - auto* display = lv_obj_get_display(parent); - auto horizontal_px = lv_display_get_horizontal_resolution(display); - auto vertical_px = lv_display_get_vertical_resolution(display); - bool is_landscape_display = horizontal_px > vertical_px; - - constexpr auto block_width = 16; - const auto square_spacing = getSquareSpacing(ui_scale); - int32_t x_spacing = block_width + square_spacing; - uint8_t column = 0; - const uint8_t column_limit = is_landscape_display ? 10 : 5; - - auto* row_wrapper = createGpioRowWrapper(centering_wrapper); - lv_obj_align(row_wrapper, LV_ALIGN_TOP_MID, 0, 0); - - mutex.lock(); - - auto pin_count = hal::gpio::getPinCount(); - pinStates.resize(pin_count); - pinWidgets.resize(pin_count); - - for (int i = 0; i < pin_count; ++i) { - constexpr uint8_t offset_from_left_label = 4; - - // Add the GPIO number before the first item on a row - if (column == 0) { - auto* prefix = lv_label_create(row_wrapper); - lv_label_set_text_fmt(prefix, "%02d", i); - } - - // Add a new GPIO status indicator - auto* status_label = lv_label_create(row_wrapper); - lv_obj_set_pos(status_label, (column+1) * x_spacing + offset_from_left_label, 0); - lv_label_set_text_fmt(status_label, "%s", LV_SYMBOL_STOP); - lv_obj_set_style_text_color(status_label, lv_color_background_darkest(), LV_STATE_DEFAULT); - pinWidgets[i] = status_label; - pinStates[i] = false; - - column++; - - if (column >= column_limit) { - // Add the GPIO number after the last item on a row - auto* postfix = lv_label_create(row_wrapper); - lv_label_set_text_fmt(postfix, "%02d", i); - lv_obj_set_pos(postfix, (column + 1) * x_spacing + offset_from_left_label, 0); - - // Add a new row wrapper underneath the last one - auto* new_row_wrapper = createGpioRowWrapper(centering_wrapper); - lv_obj_align_to(new_row_wrapper, row_wrapper, LV_ALIGN_BOTTOM_LEFT, 0, square_spacing); - row_wrapper = new_row_wrapper; - - column = 0; - } - } - mutex.unlock(); - - startTask(); -} - -void GpioApp::onHide(AppContext& app) { - stopTask(); - - mutex.lock(); - pinWidgets.clear(); - pinStates.clear(); - mutex.unlock(); -} - -extern const AppManifest manifest = { - .appId = "Gpio", - .appName = "GPIO", - .appIcon = TT_ASSETS_APP_ICON_GPIO, - .appCategory = Category::System, - .createApp = create -}; - -} // namespace