Refactored desktop (#118)

This commit is contained in:
Ken Van Hoeylandt 2024-12-10 19:40:11 +01:00 committed by GitHub
parent 77280def1d
commit 74a53b2735
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 122 additions and 37 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,4 +1,6 @@
# TODOs # TODOs
- Crash logs stored on sdcard or elsewhere: perhaps show crash screen after recovering from crash (with QR code? https://github.com/ricmoo/QRCode)
- Logging
- AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched - AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched
- Loader: Use Timer instead of Thread, and move API to `tt::app::` - Loader: Use Timer instead of Thread, and move API to `tt::app::`
- Gpio: Use Timer instead of Thread - Gpio: Use Timer instead of Thread

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,8 +1,6 @@
## Overview ## Overview
Tactility is a front-end application platform for ESP32. Tactility is an operating system that is focusing on the ESP32 microcontroller.
It is currently intended for touchscreen devices, but the goal is to also support different types of input in the future.
Tactility provides an application framework that borrows concepts from [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware/) and mobile phone operating systems.
![photo of devices running Tactility](Documentation/pics/tactility-devices.webp)  ![screenshot of desktop app](Documentation/pics/screenshot-Desktop.png) ![photo of devices running Tactility](Documentation/pics/tactility-devices.webp)  ![screenshot of desktop app](Documentation/pics/screenshot-Desktop.png)
@ -14,7 +12,7 @@ Next to desktop functionality, Tactility makes it easy to manage system settings
There are also built-in apps: There are also built-in apps:
![screenshot of GPIO app](Documentation/pics/screenshot-Gpio.png) ![screenshot of files app](Documentation/pics/screenshot-Files.png) ![screenshot off app list app](Documentation/pics/screenshot-AppList.png) ![screenshot of files app](Documentation/pics/screenshot-Files.png)
Play with the built-in apps or build your own! Use one of the supported devices or set up the drivers for your own hardware platform. Play with the built-in apps or build your own! Use one of the supported devices or set up the drivers for your own hardware platform.
@ -26,8 +24,9 @@ Noteworthy features:
- Includes a PC simulator build target to speed up development. - Includes a PC simulator build target to speed up development.
Requirements: Requirements:
- ESP32 (any?) with a touchscreen - ESP32 (any?)
- [esp-idf 5.3](https://docs.espressif.com/projects/esp-idf/en/release-v5.3/esp32/get-started/index.html) or a newer v5.3.x - [esp-idf 5.3](https://docs.espressif.com/projects/esp-idf/en/release-v5.3/esp32/get-started/index.html) or a newer v5.3.x
- (for PC simulator) SDL2 library, including SDL image
## Making apps is easy! ## Making apps is easy!

View File

@ -36,6 +36,7 @@ static const std::vector<const service::ServiceManifest*> system_services = {
namespace app { namespace app {
namespace alertdialog { extern const AppManifest manifest; } namespace alertdialog { extern const AppManifest manifest; }
namespace applist { extern const AppManifest manifest; }
namespace boot { extern const AppManifest manifest; } namespace boot { extern const AppManifest manifest; }
namespace desktop { extern const AppManifest manifest; } namespace desktop { extern const AppManifest manifest; }
namespace files { extern const AppManifest manifest; } namespace files { extern const AppManifest manifest; }
@ -64,6 +65,7 @@ extern const app::AppManifest screenshot_app;
static const std::vector<const app::AppManifest*> system_apps = { static const std::vector<const app::AppManifest*> system_apps = {
&app::alertdialog::manifest, &app::alertdialog::manifest,
&app::applist::manifest,
&app::boot::manifest, &app::boot::manifest,
&app::desktop::manifest, &app::desktop::manifest,
&app::display::manifest, &app::display::manifest,

View File

@ -0,0 +1,64 @@
#include "app/ManifestRegistry.h"
#include "Assets.h"
#include "Check.h"
#include "lvgl.h"
#include <algorithm>
#include "service/loader/Loader.h"
#include "lvgl/Toolbar.h"
namespace tt::app::applist {
static void onAppPressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const AppManifest*>(lv_event_get_user_data(e));
service::loader::startApp(manifest->id, false);
}
}
static void createAppWidget(const AppManifest* manifest, void* parent) {
tt_check(parent);
auto* list = static_cast<lv_obj_t*>(parent);
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str());
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
}
static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
auto* toolbar = lvgl::toolbar_create(parent, app);
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_t* list = lv_list_create(parent);
lv_obj_set_width(list, LV_PCT(100));
lv_obj_align_to(list, toolbar, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
auto toolbar_height = lv_obj_get_height(toolbar);
auto parent_content_height = lv_obj_get_content_height(parent);
lv_obj_set_height(list, parent_content_height - toolbar_height);
auto manifests = getApps();
std::sort(manifests.begin(), manifests.end(), SortAppManifestByName);
lv_list_add_text(list, "User");
for (const auto& manifest: manifests) {
if (manifest->type == TypeUser) {
createAppWidget(manifest, list);
}
}
lv_list_add_text(list, "System");
for (const auto& manifest: manifests) {
if (manifest->type == TypeSystem) {
createAppWidget(manifest, list);
}
}
}
extern const AppManifest manifest = {
.id = "AppList",
.name = "Apps",
.type = TypeHidden,
.onShow = onShow,
};
} // namespace

View File

@ -1,49 +1,67 @@
#include "app/ManifestRegistry.h" #include "app/ManifestRegistry.h"
#include "Assets.h"
#include "Check.h" #include "Check.h"
#include "lvgl.h" #include "lvgl.h"
#include <algorithm>
#include "service/loader/Loader.h" #include "service/loader/Loader.h"
namespace tt::app::desktop { namespace tt::app::desktop {
static void onAppPressed(lv_event_t* e) { static void onAppPressed(TT_UNUSED lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e); auto* appId = (const char*)lv_event_get_user_data(e);
if (code == LV_EVENT_CLICKED) { service::loader::startApp(appId, false);
const auto* manifest = static_cast<const AppManifest*>(lv_event_get_user_data(e));
service::loader::startApp(manifest->id, false);
}
} }
static void createAppWidget(const AppManifest* manifest, void* parent) { static lv_obj_t* createAppButton(lv_obj_t* parent, const char* title, const char* imageFile, const char* appId, int32_t buttonPaddingLeft) {
tt_check(parent); auto* wrapper = lv_obj_create(parent);
auto* list = static_cast<lv_obj_t*>(parent); lv_obj_set_size(wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK; lv_obj_set_style_pad_ver(wrapper, 0, 0);
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str()); lv_obj_set_style_pad_left(wrapper, buttonPaddingLeft, 0);
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest); lv_obj_set_style_pad_right(wrapper, 0, 0);
lv_obj_set_style_border_width(wrapper, 0, 0);
auto* apps_button = lv_button_create(wrapper);
lv_obj_set_style_pad_hor(apps_button, 0, 0);
lv_obj_set_style_pad_top(apps_button, 0, 0);
lv_obj_set_style_pad_bottom(apps_button, 16, 0);
lv_obj_set_style_shadow_width(apps_button, 0, 0);
lv_obj_set_style_border_width(apps_button, 0, 0);
lv_obj_set_style_bg_color(apps_button, lv_color_white(), 0);
auto* button_image = lv_image_create(apps_button);
lv_image_set_src(button_image, imageFile);
lv_obj_add_event_cb(apps_button, onAppPressed, LV_EVENT_CLICKED, (void*)appId);
lv_obj_set_style_image_recolor(button_image, lv_theme_get_color_primary(parent), 0);
lv_obj_set_style_image_recolor_opa(button_image, LV_OPA_COVER, 0);
auto* label = lv_label_create(wrapper);
lv_label_set_text(label, title);
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, 0);
return wrapper;
} }
static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) { static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
lv_obj_t* list = lv_list_create(parent); auto* wrapper = lv_obj_create(parent);
lv_obj_set_size(list, LV_PCT(100), LV_PCT(100));
lv_obj_center(list);
auto manifests = getApps(); lv_obj_align(wrapper, LV_ALIGN_CENTER, 0, 0);
std::sort(manifests.begin(), manifests.end(), SortAppManifestByName); lv_obj_set_style_pad_all(wrapper, 0, 0);
lv_obj_set_size(wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_border_width(wrapper, 0, 0);
lv_obj_set_flex_grow(wrapper, 1);
lv_list_add_text(list, "User"); auto* display = lv_obj_get_display(parent);
for (const auto& manifest: manifests) { auto orientation = lv_display_get_rotation(display);
if (manifest->type == TypeUser) { if (orientation == LV_DISPLAY_ROTATION_0 || orientation == LV_DISPLAY_ROTATION_180) {
createAppWidget(manifest, list); lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_ROW);
} } else {
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
} }
lv_list_add_text(list, "System"); int32_t available_width = lv_display_get_horizontal_resolution(display) - (3 * 80);
for (const auto& manifest: manifests) { int32_t padding = TT_MIN(available_width / 4, 64);
if (manifest->type == TypeSystem) {
createAppWidget(manifest, list); createAppButton(wrapper, "Apps", "A:/assets/desktop_icon_apps.png", "AppList", 0);
} createAppButton(wrapper, "Files", "A:/assets/desktop_icon_files.png", "Files", padding);
} createAppButton(wrapper, "Settings", "A:/assets/desktop_icon_settings.png", "Settings", padding);
} }
extern const AppManifest manifest = { extern const AppManifest manifest = {

View File

@ -256,7 +256,7 @@ extern const AppManifest manifest = {
.id = "Files", .id = "Files",
.name = "Files", .name = "Files",
.icon = TT_ASSETS_APP_ICON_FILES, .icon = TT_ASSETS_APP_ICON_FILES,
.type = TypeSystem, .type = TypeHidden,
.onStart = onStart, .onStart = onStart,
.onShow = onShow, .onShow = onShow,
}; };

View File

@ -46,7 +46,7 @@ extern const AppManifest manifest = {
.id = "Settings", .id = "Settings",
.name = "Settings", .name = "Settings",
.icon = TT_ASSETS_APP_ICON_SETTINGS, .icon = TT_ASSETS_APP_ICON_SETTINGS,
.type = TypeSystem, .type = TypeHidden,
.onShow = onShow, .onShow = onShow,
}; };