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
- 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
- Loader: Use Timer instead of Thread, and move API to `tt::app::`
- 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
Tactility is a front-end application platform for ESP32.
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.
Tactility is an operating system that is focusing on the ESP32 microcontroller.
![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:
![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.
@ -26,8 +24,9 @@ Noteworthy features:
- Includes a PC simulator build target to speed up development.
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
- (for PC simulator) SDL2 library, including SDL image
## Making apps is easy!

View File

@ -36,6 +36,7 @@ static const std::vector<const service::ServiceManifest*> system_services = {
namespace app {
namespace alertdialog { extern const AppManifest manifest; }
namespace applist { extern const AppManifest manifest; }
namespace boot { extern const AppManifest manifest; }
namespace desktop { 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 = {
&app::alertdialog::manifest,
&app::applist::manifest,
&app::boot::manifest,
&app::desktop::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 "Assets.h"
#include "Check.h"
#include "lvgl.h"
#include <algorithm>
#include "service/loader/Loader.h"
namespace tt::app::desktop {
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 onAppPressed(TT_UNUSED lv_event_t* e) {
auto* appId = (const char*)lv_event_get_user_data(e);
service::loader::startApp(appId, 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 lv_obj_t* createAppButton(lv_obj_t* parent, const char* title, const char* imageFile, const char* appId, int32_t buttonPaddingLeft) {
auto* wrapper = lv_obj_create(parent);
lv_obj_set_size(wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_pad_ver(wrapper, 0, 0);
lv_obj_set_style_pad_left(wrapper, buttonPaddingLeft, 0);
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) {
lv_obj_t* list = lv_list_create(parent);
lv_obj_set_size(list, LV_PCT(100), LV_PCT(100));
lv_obj_center(list);
auto* wrapper = lv_obj_create(parent);
auto manifests = getApps();
std::sort(manifests.begin(), manifests.end(), SortAppManifestByName);
lv_obj_align(wrapper, LV_ALIGN_CENTER, 0, 0);
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");
for (const auto& manifest: manifests) {
if (manifest->type == TypeUser) {
createAppWidget(manifest, list);
}
auto* display = lv_obj_get_display(parent);
auto orientation = lv_display_get_rotation(display);
if (orientation == LV_DISPLAY_ROTATION_0 || orientation == LV_DISPLAY_ROTATION_180) {
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");
for (const auto& manifest: manifests) {
if (manifest->type == TypeSystem) {
createAppWidget(manifest, list);
}
}
int32_t available_width = lv_display_get_horizontal_resolution(display) - (3 * 80);
int32_t padding = TT_MIN(available_width / 4, 64);
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 = {

View File

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

View File

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