diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 6f3c4a0f..a48f5bde 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -51,6 +51,7 @@ - Display app: Add toggle to display performance measurement overlay (consider showing FPS in statusbar!) - Files app: copy/paste actions - On crash, try to save current log to flash or SD card? (this is risky, though, so ask in Discord first) +- Support more than 1 hardware keyboard (see lvgl::hardware_keyboard_set_indev()). LVGL init currently calls keyboard init, but that part should probably be done from the KeyboardDevice base class. # App Ideas - Map widget: diff --git a/Tactility/Include/Tactility/lvgl/Keyboard.h b/Tactility/Include/Tactility/lvgl/Keyboard.h new file mode 100644 index 00000000..92534363 --- /dev/null +++ b/Tactility/Include/Tactility/lvgl/Keyboard.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace tt::lvgl { + +/** + * Show the on-screen keyboard. + * @param[in] textarea the textarea to focus the input for + */ +void software_keyboard_show(lv_obj_t* textarea); + +/** + * Hide the on-screen keyboard. + * Has no effect when the keyboard is not visible. + */ +void software_keyboard_hide(); + +/** + * The on-screen keyboard is only shown when both of these conditions are true: + * - there is no hardware keyboard + * - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h + * @return if we should show a on-screen keyboard for text input inside our apps + */ +bool software_keyboard_is_enabled(); + +/** + * Activate the keypad for a widget group. + * @param group + */ +void software_keyboard_activate(lv_group_t* group); + +/** + * Deactivate the keypad for the current widget group (if any). + * You don't have to call this after calling _activate() because widget + * cleanup automatically removes itself from the group it belongs to. + */ +void software_keyboard_deactivate(); + +/** + * @return true if LVGL is configured with a keypad + */ +bool hardware_keyboard_is_available(); + +/** + * Set the keypad. + * @param device the keypad device + */ +void hardware_keyboard_set_indev(lv_indev_t* device); + +/** + * Glue code for the on-screen keyboard and the hardware keyboard: + * - Attach automatic hide/show parameters for the on-screen keyboard. + * - Registers the textarea to the default lv_group_t for hardware keyboards. + * @param[in] textarea + */ +void keyboard_add_textarea(lv_obj_t* textarea); + +} \ No newline at end of file diff --git a/Tactility/Include/Tactility/lvgl/LvglKeypad.h b/Tactility/Include/Tactility/lvgl/LvglKeypad.h deleted file mode 100644 index 7a0c7115..00000000 --- a/Tactility/Include/Tactility/lvgl/LvglKeypad.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * This code relates to the hardware keyboard support also known as "keypads" in LVGL. - */ -#pragma once - -#include - -namespace tt::lvgl { - -/** - * @return true if LVGL is configured with a keypad - */ -bool keypad_is_available(); - -/** - * Set the keypad. - * @param device the keypad device - */ -void keypad_set_indev(lv_indev_t* device); - -/** - * Activate the keypad for a widget group. - * @param group - */ -void keypad_activate(lv_group_t* group); - -/** - * Deactivate the keypad for the current widget group (if any). - * You don't have to call this after calling _activate() because widget - * cleanup automatically removes itself from the group it belongs to. - */ -void keypad_deactivate(); - -} // namespace diff --git a/Tactility/Include/Tactility/service/gui/Gui.h b/Tactility/Private/Tactility/service/gui/Gui.h similarity index 53% rename from Tactility/Include/Tactility/service/gui/Gui.h rename to Tactility/Private/Tactility/service/gui/Gui.h index bd3cd673..4aa6c98d 100644 --- a/Tactility/Include/Tactility/service/gui/Gui.h +++ b/Tactility/Private/Tactility/service/gui/Gui.h @@ -1,11 +1,48 @@ #pragma once -#include "Tactility/app/AppInstance.h" +#include +#include +#include + #include "Tactility/app/AppContext.h" +#include + +#include + namespace tt::service::gui { -typedef struct Gui Gui; +#define GUI_THREAD_FLAG_DRAW (1 << 0) +#define GUI_THREAD_FLAG_INPUT (1 << 1) +#define GUI_THREAD_FLAG_EXIT (1 << 2) +#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT) + +/** Gui structure */ +struct Gui { + // Thread and lock + Thread* thread = nullptr; + Mutex mutex = Mutex(Mutex::Type::Recursive); + PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; + + // Layers and Canvas + lv_obj_t* appRootWidget = nullptr; + lv_obj_t* statusbarWidget = nullptr; + + // App-specific + std::shared_ptr appToRender = nullptr; + + lv_obj_t* _Nullable keyboard = nullptr; + lv_group_t* keyboardGroup = nullptr; +}; + +/** Update GUI, request redraw */ +void requestDraw(); + +/** Lock GUI */ +void lock(); + +/** Unlock GUI */ +void unlock(); /** * Set the app viewport in the gui state and request the gui to draw it. @@ -24,13 +61,13 @@ void hideApp(); * Show the on-screen keyboard. * @param[in] textarea the textarea to focus the input for */ -void keyboardShow(lv_obj_t* textarea); +void softwareKeyboardShow(lv_obj_t* textarea); /** * Hide the on-screen keyboard. * Has no effect when the keyboard is not visible. */ -void keyboardHide(); +void softwareKeyboardHide(); /** * The on-screen keyboard is only shown when both of these conditions are true: @@ -38,7 +75,7 @@ void keyboardHide(); * - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h * @return if we should show a on-screen keyboard for text input inside our apps */ -bool keyboardIsEnabled(); +bool softwareKeyboardIsEnabled(); /** * Glue code for the on-screen keyboard and the hardware keyboard: diff --git a/Tactility/Private/Tactility/service/gui/Gui_i.h b/Tactility/Private/Tactility/service/gui/Gui_i.h deleted file mode 100644 index 138e7f02..00000000 --- a/Tactility/Private/Tactility/service/gui/Gui_i.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -#include - -namespace tt::service::gui { - -#define GUI_THREAD_FLAG_DRAW (1 << 0) -#define GUI_THREAD_FLAG_INPUT (1 << 1) -#define GUI_THREAD_FLAG_EXIT (1 << 2) -#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT) - -/** Gui structure */ -struct Gui { - // Thread and lock - Thread* thread = nullptr; - Mutex mutex = Mutex(Mutex::Type::Recursive); - PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; - - // Layers and Canvas - lv_obj_t* appRootWidget = nullptr; - lv_obj_t* statusbarWidget = nullptr; - - // App-specific - std::shared_ptr appToRender = nullptr; - - lv_obj_t* _Nullable keyboard = nullptr; - lv_group_t* keyboardGroup = nullptr; -}; - -/** Update GUI, request redraw */ -void requestDraw(); - -/** Lock GUI */ -void lock(); - -/** Unlock GUI */ -void unlock(); - -} // namespace diff --git a/Tactility/Source/lvgl/Init.cpp b/Tactility/Source/lvgl/Init.cpp index 7da432c5..39ffe624 100644 --- a/Tactility/Source/lvgl/Init.cpp +++ b/Tactility/Source/lvgl/Init.cpp @@ -1,5 +1,5 @@ #include "Tactility/app/display/DisplaySettings.h" -#include "Tactility/lvgl/LvglKeypad.h" +#include "Tactility/lvgl/Keyboard.h" #include "Tactility/hal/display/DisplayDevice.h" #include "Tactility/hal/touch/TouchDevice.h" @@ -68,7 +68,7 @@ static bool initKeyboard(const std::shared_ptr& dis if (keyboard->start(display->getLvglDisplay())) { lv_indev_t* keyboard_indev = keyboard->getLvglIndev(); lv_indev_set_user_data(keyboard_indev, keyboard.get()); - tt::lvgl::keypad_set_indev(keyboard_indev); + tt::lvgl::hardware_keyboard_set_indev(keyboard_indev); TT_LOG_I(TAG, "Keyboard started"); return true; } else { diff --git a/Tactility/Source/lvgl/Keyboard.cpp b/Tactility/Source/lvgl/Keyboard.cpp new file mode 100644 index 00000000..34783226 --- /dev/null +++ b/Tactility/Source/lvgl/Keyboard.cpp @@ -0,0 +1,44 @@ +#include "Tactility/lvgl/Keyboard.h" +#include "Tactility/service/gui/Gui.h" + +namespace tt::lvgl { + +static lv_indev_t* keyboard_device = nullptr; + +void software_keyboard_show(lv_obj_t* textarea) { + service::gui::softwareKeyboardShow(textarea); +} + +void software_keyboard_hide() { + service::gui::softwareKeyboardHide(); +} + +bool software_keyboard_is_enabled() { + return service::gui::softwareKeyboardIsEnabled(); +} + +void software_keyboard_activate(lv_group_t* group) { + if (keyboard_device != nullptr) { + lv_indev_set_group(keyboard_device, group); + } +} + +void software_keyboard_deactivate() { + if (keyboard_device != nullptr) { + lv_indev_set_group(keyboard_device, nullptr); + } +} + +bool hardware_keyboard_is_available() { + return keyboard_device != nullptr; +} + +void hardware_keyboard_set_indev(lv_indev_t* device) { + keyboard_device = device; +} + +void keyboard_add_textarea(lv_obj_t* textarea) { + service::gui::keyboardAddTextArea(textarea); +} + +} diff --git a/Tactility/Source/lvgl/LvglKeypad.cpp b/Tactility/Source/lvgl/LvglKeypad.cpp deleted file mode 100644 index 017f5af6..00000000 --- a/Tactility/Source/lvgl/LvglKeypad.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "Tactility/lvgl/LvglKeypad.h" - -namespace tt::lvgl { - -static lv_indev_t* keyboard_device = nullptr; - -bool keypad_is_available() { - return keyboard_device != nullptr; -} - -void keypad_set_indev(lv_indev_t* device) { - keyboard_device = device; -} - -void keypad_activate(lv_group_t* group) { - if (keyboard_device != nullptr) { - lv_indev_set_group(keyboard_device, group); - } -} - -void keypad_deactivate() { - if (keyboard_device != nullptr) { - lv_indev_set_group(keyboard_device, nullptr); - } -} - -} // namespace diff --git a/Tactility/Source/service/gui/Gui.cpp b/Tactility/Source/service/gui/Gui.cpp index 77faa649..959b6075 100644 --- a/Tactility/Source/service/gui/Gui.cpp +++ b/Tactility/Source/service/gui/Gui.cpp @@ -1,8 +1,8 @@ -#include "Tactility/service/gui/Gui_i.h" -#include "Tactility/service/loader/Loader_i.h" +#include "Tactility/service/gui/Gui.h" #include "Tactility/lvgl/LvglSync.h" -#include "Tactility/lvgl/Style.h" #include "Tactility/lvgl/Statusbar.h" +#include "Tactility/lvgl/Style.h" +#include "Tactility/service/loader/Loader_i.h" #include #include diff --git a/Tactility/Source/service/gui/GuiDraw.cpp b/Tactility/Source/service/gui/GuiDraw.cpp index 016aa546..ed52c19e 100644 --- a/Tactility/Source/service/gui/GuiDraw.cpp +++ b/Tactility/Source/service/gui/GuiDraw.cpp @@ -1,6 +1,7 @@ -#include "Tactility/service/gui/Gui_i.h" +#include "Tactility/service/gui/Gui.h" + +#include "Tactility/app/AppInstance.h" #include "Tactility/lvgl/LvglSync.h" -#include "Tactility/lvgl/Statusbar.h" #include "Tactility/lvgl/Style.h" #include @@ -17,7 +18,7 @@ static lv_obj_t* createAppViews(Gui* gui, lv_obj_t* parent) { lv_obj_set_width(child_container, LV_PCT(100)); lv_obj_set_flex_grow(child_container, 1); - if (keyboardIsEnabled()) { + if (softwareKeyboardIsEnabled()) { gui->keyboard = lv_keyboard_create(parent); lv_obj_add_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN); } else { diff --git a/Tactility/Source/service/gui/Keyboard.cpp b/Tactility/Source/service/gui/Keyboard.cpp index 90338f20..0aede061 100644 --- a/Tactility/Source/service/gui/Keyboard.cpp +++ b/Tactility/Source/service/gui/Keyboard.cpp @@ -1,7 +1,7 @@ +#include "Tactility/lvgl/Keyboard.h" #include "Tactility/Check.h" -#include "Tactility/service/gui/Gui_i.h" -#include "Tactility/lvgl/LvglKeypad.h" #include "Tactility/lvgl/LvglSync.h" +#include "Tactility/service/gui/Gui.h" #include @@ -11,19 +11,19 @@ extern Gui* gui; static void show_keyboard(lv_event_t* event) { lv_obj_t* target = lv_event_get_current_target_obj(event); - keyboardShow(target); + softwareKeyboardShow(target); lv_obj_scroll_to_view(target, LV_ANIM_ON); } static void hide_keyboard(TT_UNUSED lv_event_t* event) { - keyboardHide(); + softwareKeyboardHide(); } -bool keyboardIsEnabled() { - return !lvgl::keypad_is_available() || TT_CONFIG_FORCE_ONSCREEN_KEYBOARD; +bool softwareKeyboardIsEnabled() { + return !lvgl::hardware_keyboard_is_available() || TT_CONFIG_FORCE_ONSCREEN_KEYBOARD; } -void keyboardShow(lv_obj_t* textarea) { +void softwareKeyboardShow(lv_obj_t* textarea) { lock(); if (gui->keyboard) { @@ -34,7 +34,7 @@ void keyboardShow(lv_obj_t* textarea) { unlock(); } -void keyboardHide() { +void softwareKeyboardHide() { lock(); if (gui->keyboard) { @@ -48,7 +48,7 @@ void keyboardAddTextArea(lv_obj_t* textarea) { lock(); tt_check(lvgl::lock(0), "lvgl should already be locked before calling this method"); - if (keyboardIsEnabled()) { + if (softwareKeyboardIsEnabled()) { lv_obj_add_event_cb(textarea, show_keyboard, LV_EVENT_FOCUSED, nullptr); lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_DEFOCUSED, nullptr); lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_READY, nullptr); @@ -57,7 +57,7 @@ void keyboardAddTextArea(lv_obj_t* textarea) { // lv_obj_t auto-remove themselves from the group when they are destroyed (last checked in LVGL 8.3) lv_group_add_obj(gui->keyboardGroup, textarea); - lvgl::keypad_activate(gui->keyboardGroup); + lvgl::software_keyboard_activate(gui->keyboardGroup); lvgl::unlock(); unlock(); diff --git a/TactilityC/Include/tt_lvgl_keyboard.h b/TactilityC/Include/tt_lvgl_keyboard.h new file mode 100644 index 00000000..b7469c9c --- /dev/null +++ b/TactilityC/Include/tt_lvgl_keyboard.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Show the on-screen keyboard. + * @param[in] textarea the textarea to focus the input for + */ +void tt_lvgl_software_keyboard_show(lv_obj_t* textarea); + +/** + * Hide the on-screen keyboard. + * Has no effect when the keyboard is not visible. + */ +void tt_lvgl_software_keyboard_hide(); + +/** + * The on-screen keyboard is only shown when both of these conditions are true: + * - there is no hardware keyboard + * - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h + * @return if we should show a on-screen keyboard for text input inside our apps + */ +bool tt_lvgl_software_keyboard_is_enabled(); + +/** + * Activate the keypad for a widget group. + * @param group + */ +void tt_lvgl_software_keyboard_activate(lv_group_t* group); + +/** + * Deactivate the keypad for the current widget group (if any). + * You don't have to call this after calling _activate() because widget + * cleanup automatically removes itself from the group it belongs to. + */ +void tt_lvgl_software_keyboard_deactivate(); + +/** + * @return true if LVGL is configured with a keypad + */ +bool tt_lvgl_hardware_keyboard_is_available(); + +/** + * Set the keypad. + * @param device the keypad device + */ +void tt_lvgl_hardware_keyboard_set_indev(lv_indev_t* device); + +/** + * Glue code for the on-screen keyboard and the hardware keyboard: + * - Attach automatic hide/show parameters for the on-screen keyboard. + * - Registers the textarea to the default lv_group_t for hardware keyboards. + * @param[in] textarea + */ +void tt_lvgl_keyboard_add_textarea(lv_obj_t* textarea); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index 10099bd4..a766a883 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -6,6 +6,7 @@ #include "tt_app_selectiondialog.h" #include "tt_bundle.h" #include "tt_hal_i2c.h" +#include "tt_lvgl_keyboard.h" #include "tt_lvgl_spinner.h" #include "tt_lvgl_toolbar.h" #include "tt_message_queue.h" @@ -49,6 +50,14 @@ const struct esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(tt_hal_i2c_master_has_device_at_address), ESP_ELFSYM_EXPORT(tt_hal_i2c_lock), ESP_ELFSYM_EXPORT(tt_hal_i2c_unlock), + ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_show), + ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_hide), + ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_is_enabled), + ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_activate), + ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_deactivate), + ESP_ELFSYM_EXPORT(tt_lvgl_hardware_keyboard_is_available), + ESP_ELFSYM_EXPORT(tt_lvgl_hardware_keyboard_set_indev), + ESP_ELFSYM_EXPORT(tt_lvgl_keyboard_add_textarea), ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create), ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create_for_app), ESP_ELFSYM_EXPORT(tt_message_queue_alloc), diff --git a/TactilityC/Source/tt_lvgl_keyboard.cpp b/TactilityC/Source/tt_lvgl_keyboard.cpp new file mode 100644 index 00000000..1a46ce40 --- /dev/null +++ b/TactilityC/Source/tt_lvgl_keyboard.cpp @@ -0,0 +1,37 @@ +#include "Tactility/lvgl/Keyboard.h" + +extern "C" { + +void tt_lvgl_software_keyboard_show(lv_obj_t* textarea) { + tt::lvgl::software_keyboard_show(textarea); +} + +void tt_lvgl_software_keyboard_hide() { + tt::lvgl::software_keyboard_hide(); +} + +bool tt_lvgl_software_keyboard_is_enabled() { + return tt::lvgl::software_keyboard_is_enabled(); +} + +void tt_lvgl_software_keyboard_activate(lv_group_t* group) { + tt::lvgl::software_keyboard_activate(group); +} + +void tt_lvgl_software_keyboard_deactivate() { + tt::lvgl::software_keyboard_deactivate(); +} + +bool tt_lvgl_hardware_keyboard_is_available() { + return tt::lvgl::hardware_keyboard_is_available(); +} + +void tt_lvgl_hardware_keyboard_set_indev(lv_indev_t* device) { + tt::lvgl::hardware_keyboard_set_indev(device); +} + +void tt_lvgl_keyboard_add_textarea(lv_obj_t* textarea) { + tt::lvgl::keyboard_add_textarea(textarea); +} + +}