C++ apps: templated scene controller (#517)

* C++ apps: templated scene controller
* templated app: fix type names
* templated app: text store component
* Applications: add "Templated Scene" application
* templated app: refractoring
* Gui module byte input: fix docs
* templated app: new byte input scene
* templated app: dialog ex view module
* templated app: popup view module
* templated app: dialog-ex view module, fix docs
* templated app: text input view module
* Gui module text input: fix docs
* Furi: duplicated include
* templated app: record holder (controller) class
* templated app: view modules can now be accessed via cast
* templated app: remove unused includes
* templated app: fix return code
This commit is contained in:
SG 2021-06-15 21:01:56 +10:00 committed by GitHub
parent 3a2121bbb8
commit 0b14db4fb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1242 additions and 9 deletions

View File

@ -39,6 +39,7 @@ int32_t app_accessor(void* p);
int32_t internal_storage_task(void* p);
int32_t app_archive(void* p);
int32_t notification_app(void* p);
int32_t scened_app(void* p);
// On system start hooks declaration
void irda_cli_init();
@ -293,6 +294,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
.stack_size = 1024,
.icon = A_Plugins_14},
#endif
#ifdef APP_SCENED
{.app = scened_app, .name = "Templated Scene", .stack_size = 1024, .icon = A_Plugins_14},
#endif
};
const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication);

View File

@ -46,7 +46,7 @@ void byte_input_free(ByteInput* byte_input);
View* byte_input_get_view(ByteInput* byte_input);
/**
* @brief Deinitialize and free byte input
* @brief Set byte input result callback
*
* @param byte_input byte input instance
* @param input_callback input callback fn

View File

@ -9,23 +9,31 @@ extern "C" {
typedef struct TextInput TextInput;
typedef void (*TextInputCallback)(void* context, char* text);
/* Allocate and initialize text input
* This text input is used to enter string
/**
* @brief Allocate and initialize text input
* This text input is used to enter string
*
*/
TextInput* text_input_alloc();
/* Deinitialize and free text input
/**
* @brief Deinitialize and free text input
*
* @param text_input - Text input instance
*/
void text_input_free(TextInput* text_input);
/* Get text input view
/**
* @brief Get text input view
*
* @param text_input - Text input instance
* @return View instance that can be used for embedding
*/
View* text_input_get_view(TextInput* text_input);
/* Deinitialize and free text input
/**
* @brief Set text input result callback
*
* @param text_input - Text input instance
* @param callback - callback fn
* @param callback_context - callback context
@ -39,7 +47,9 @@ void text_input_set_result_callback(
char* text,
uint8_t max_text_length);
/* Set text input header text
/**
* @brief Set text input header text
*
* @param text input - Text input instance
* @param text - text to be shown
*/

View File

@ -0,0 +1,35 @@
#include "scened-app-scene-byte-input.h"
void ScenedAppSceneByteInput::on_enter(ScenedApp* app, bool need_restore) {
ByteInputVM* byte_input = app->view_controller;
auto callback = cbc::obtain_connector(this, &ScenedAppSceneByteInput::result_callback);
byte_input->set_result_callback(callback, NULL, app, data, 4);
byte_input->set_header_text("Enter the key");
app->view_controller.switch_to<ByteInputVM>();
}
bool ScenedAppSceneByteInput::on_event(ScenedApp* app, ScenedApp::Event* event) {
bool consumed = false;
if(event->type == ScenedApp::EventType::ByteEditResult) {
app->scene_controller.switch_to_previous_scene();
consumed = true;
}
return consumed;
}
void ScenedAppSceneByteInput::on_exit(ScenedApp* app) {
app->view_controller.get<ByteInputVM>()->clean();
}
void ScenedAppSceneByteInput::result_callback(void* context, uint8_t* bytes, uint8_t bytes_count) {
ScenedApp* app = static_cast<ScenedApp*>(context);
ScenedApp::Event event;
event.type = ScenedApp::EventType::ByteEditResult;
app->view_controller.send_event(&event);
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "../scened-app.h"
class ScenedAppSceneByteInput : public GenericScene<ScenedApp> {
public:
void on_enter(ScenedApp* app, bool need_restore) final;
bool on_event(ScenedApp* app, ScenedApp::Event* event) final;
void on_exit(ScenedApp* app) final;
private:
void result_callback(void* context, uint8_t* bytes, uint8_t bytes_count);
uint8_t data[4] = {
0x01,
0xA2,
0xF4,
0xD3,
};
};

View File

@ -0,0 +1,47 @@
#include "scened-app-scene-start.h"
typedef enum {
SubmenuByteInput,
} SubmenuIndex;
void ScenedAppSceneStart::on_enter(ScenedApp* app, bool need_restore) {
auto submenu = app->view_controller.get<SubmenuVM>();
auto callback = cbc::obtain_connector(this, &ScenedAppSceneStart::submenu_callback);
submenu->add_item("Byte Input", SubmenuByteInput, callback, app);
if(need_restore) {
submenu->set_selected_item(submenu_item_selected);
}
app->view_controller.switch_to<SubmenuVM>();
}
bool ScenedAppSceneStart::on_event(ScenedApp* app, ScenedApp::Event* event) {
bool consumed = false;
if(event->type == ScenedApp::EventType::MenuSelected) {
submenu_item_selected = event->payload.menu_index;
switch(event->payload.menu_index) {
case SubmenuByteInput:
app->scene_controller.switch_to_next_scene(ScenedApp::SceneType::ByteInputScene);
break;
}
consumed = true;
}
return consumed;
}
void ScenedAppSceneStart::on_exit(ScenedApp* app) {
app->view_controller.get<SubmenuVM>()->clean();
}
void ScenedAppSceneStart::submenu_callback(void* context, uint32_t index) {
ScenedApp* app = static_cast<ScenedApp*>(context);
ScenedApp::Event event;
event.type = ScenedApp::EventType::MenuSelected;
event.payload.menu_index = index;
app->view_controller.send_event(&event);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "../scened-app.h"
class ScenedAppSceneStart : public GenericScene<ScenedApp> {
public:
void on_enter(ScenedApp* app, bool need_restore) final;
bool on_event(ScenedApp* app, ScenedApp::Event* event) final;
void on_exit(ScenedApp* app) final;
private:
void submenu_callback(void* context, uint32_t index);
uint32_t submenu_item_selected = 0;
};

View File

@ -0,0 +1,10 @@
#include "scened-app.h"
// app enter function
extern "C" int32_t scened_app(void* p) {
ScenedApp* app = new ScenedApp();
app->run();
delete app;
return 0;
}

View File

@ -0,0 +1,20 @@
#include "scened-app.h"
#include "scene/scened-app-scene-start.h"
#include "scene/scened-app-scene-byte-input.h"
ScenedApp::ScenedApp()
: scene_controller{this}
, text_store{128}
, notification{"notification"} {
}
ScenedApp::~ScenedApp() {
}
void ScenedApp::run() {
scene_controller.add_scene(SceneType::Start, new ScenedAppSceneStart());
scene_controller.add_scene(SceneType::ByteInputScene, new ScenedAppSceneByteInput());
notification_message(notification, &sequence_blink_green_10);
scene_controller.process(100);
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <furi.h>
#include <api-hal.h>
#include <generic-scene.hpp>
#include <scene-controller.hpp>
#include <view-controller.hpp>
#include <record-controller.hpp>
#include <text-store.h>
#include <view-modules/submenu-vm.h>
#include <view-modules/byte-input-vm.h>
#include <notification/notification-messages.h>
class ScenedApp {
public:
enum class EventType : uint8_t {
GENERIC_EVENT_ENUM_VALUES,
MenuSelected,
ByteEditResult,
};
enum class SceneType : uint8_t {
GENERIC_SCENE_ENUM_VALUES,
ByteInputScene,
};
class Event {
public:
union {
int32_t menu_index;
} payload;
EventType type;
};
SceneController<GenericScene<ScenedApp>, ScenedApp> scene_controller;
TextStore text_store;
ViewController<ScenedApp, SubmenuVM, ByteInputVM> view_controller;
RecordController<NotificationApp> notification;
~ScenedApp();
ScenedApp();
void run();
};

View File

@ -11,7 +11,6 @@
#include <furi/thread.h>
#include <furi/valuemutex.h>
#include <furi/log.h>
#include <furi/common_defines.h>
#include <api-hal-gpio.h>
#include <api-hal/api-interrupt-mgr.h>

View File

@ -0,0 +1,9 @@
template <typename TApp> class GenericScene {
public:
virtual void on_enter(TApp* app, bool need_restore) = 0;
virtual bool on_event(TApp* app, typename TApp::Event* event) = 0;
virtual void on_exit(TApp* app) = 0;
virtual ~GenericScene(){};
private:
};

View File

@ -0,0 +1,46 @@
#pragma once
#include <furi/record.h>
/**
* @brief Class for opening, casting, holding and closing records
*
* @tparam TRecordClass record class
*/
template <typename TRecordClass> class RecordController {
public:
/**
* @brief Construct a new Record Controller object for record with record name
*
* @param record_name record name
*/
RecordController(const char* record_name) {
name = record_name;
value = static_cast<TRecordClass*>(furi_record_open(name));
};
~RecordController() {
furi_record_close(name);
}
/**
* @brief Record getter
*
* @return TRecordClass* record value
*/
TRecordClass* get() {
return value;
}
/**
* @brief Record getter (by cast)
*
* @return TRecordClass* record value
*/
operator TRecordClass*() const {
return value;
}
private:
const char* name;
TRecordClass* value;
};

View File

@ -0,0 +1,178 @@
#include <map>
#include <forward_list>
#include <initializer_list>
#define GENERIC_SCENE_ENUM_VALUES Uninitalized, Exit, Start
#define GENERIC_EVENT_ENUM_VALUES Tick, Back
/**
* @brief Controller for scene navigation in application
*
* @tparam TScene generic scene class
* @tparam TApp application class
*/
template <typename TScene, typename TApp> class SceneController {
public:
/**
* @brief Add scene to scene container
*
* @param scene_index scene index
* @param scene_pointer scene object pointer
*/
void add_scene(typename TApp::SceneType scene_index, TScene* scene_pointer) {
furi_check(scenes.count(scene_index) == 0);
scenes[scene_index] = scene_pointer;
}
/**
* @brief Switch to next scene and store current scene in previous scenes list
*
* @param scene_index next scene index
* @param need_restore true, if we want the scene to restore its parameters
*/
void switch_to_next_scene(typename TApp::SceneType scene_index, bool need_restore = false) {
previous_scenes_list.push_front(current_scene_index);
switch_to_scene(scene_index, need_restore);
}
/**
* @brief Switch to next scene without ability to return to current scene
*
* @param scene_index next scene index
* @param need_restore true, if we want the scene to restore its parameters
*/
void switch_to_scene(typename TApp::SceneType scene_index, bool need_restore = false) {
if(scene_index != TApp::SceneType::Exit) {
scenes[current_scene_index]->on_exit(app);
current_scene_index = scene_index;
scenes[current_scene_index]->on_enter(app, need_restore);
}
}
/**
* @brief Search the scene in the list of previous scenes and switch to it
*
* @param scene_index_list list of scene indexes to which you want to switch
*/
void search_and_switch_to_previous_scene(
const std::initializer_list<typename TApp::SceneType>& scene_index_list) {
auto previous_scene_index = TApp::SceneType::Start;
bool scene_found = false;
while(!scene_found) {
previous_scene_index = get_previous_scene_index();
for(const auto& element : scene_index_list) {
if(previous_scene_index == element) {
scene_found = true;
break;
}
}
}
switch_to_scene(previous_scene_index, true);
}
/**
* @brief Start application main cycle
*
* @param tick_length_ms tick event length in milliseconds
*/
void process(uint32_t tick_length_ms = 100) {
typename TApp::Event event;
bool consumed;
bool exit = false;
current_scene_index = TApp::SceneType::Start;
scenes[current_scene_index]->on_enter(app, false);
while(!exit) {
app->view_controller.receive_event(&event);
consumed = scenes[current_scene_index]->on_event(app, &event);
if(!consumed) {
if(event.type == TApp::EventType::Back) {
exit = switch_to_previous_scene();
}
}
};
scenes[current_scene_index]->on_exit(app);
}
/**
* @brief Switch to previous scene
*
* @param count how many steps back
* @return true if app need to exit
*/
bool switch_to_previous_scene(uint8_t count = 1) {
auto previous_scene_index = TApp::SceneType::Start;
for(uint8_t i = 0; i < count; i++) previous_scene_index = get_previous_scene_index();
if(previous_scene_index == TApp::SceneType::Exit) return true;
switch_to_scene(previous_scene_index, true);
return false;
}
/**
* @brief Construct a new Scene Controller object
*
* @param app_pointer pointer to application class
*/
SceneController(TApp* app_pointer) {
app = app_pointer;
current_scene_index = TApp::SceneType::Uninitalized;
}
/**
* @brief Destroy the Scene Controller object
*
*/
~SceneController() {
for(auto& it : scenes) delete it.second;
}
private:
/**
* @brief Scenes pointers container
*
*/
std::map<typename TApp::SceneType, TScene*> scenes;
/**
* @brief List of indexes of previous scenes
*
*/
std::forward_list<typename TApp::SceneType> previous_scenes_list;
/**
* @brief Current scene index holder
*
*/
typename TApp::SceneType current_scene_index;
/**
* @brief Application pointer holder
*
*/
TApp* app;
/**
* @brief Get the previous scene index
*
* @return previous scene index
*/
typename TApp::SceneType get_previous_scene_index() {
auto scene_index = TApp::SceneType::Exit;
if(!previous_scenes_list.empty()) {
scene_index = previous_scenes_list.front();
previous_scenes_list.pop_front();
}
return scene_index;
}
};

View File

@ -0,0 +1,18 @@
#include "text-store.h"
#include <furi.h>
TextStore::TextStore(uint8_t _text_size)
: text_size(_text_size) {
text = static_cast<char*>(malloc(text_size + 1));
}
TextStore::~TextStore() {
free(text);
}
void TextStore::set_text_store(const char* _text...) {
va_list args;
va_start(args, _text);
vsnprintf(text, text_size, _text, args);
va_end(args);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
class TextStore {
public:
TextStore(uint8_t text_size);
~TextStore();
void set_text_store(const char* text...);
const uint8_t text_size;
char* text;
};

View File

@ -0,0 +1,121 @@
/*
* type_index without RTTI
*
* Copyright frickiericker 2016.
* Distributed under the Boost Software License, Version 1.0.
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the "Software") to use, reproduce, display, distribute,
* execute, and transmit the Software, and to prepare derivative works of the
* Software, and to permit third-parties to whom the Software is furnished to
* do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer,
* must be included in all copies of the Software, in whole or in part, and
* all derivative works of the Software, unless such copies or derivative
* works are solely in the form of machine-executable object code generated by
* a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <functional>
namespace ext {
/**
* Dummy type for tag-dispatching.
*/
template <typename T> struct tag_type {};
/**
* A value of tag_type<T>.
*/
template <typename T> constexpr tag_type<T> tag{};
/**
* A type_index implementation without RTTI.
*/
struct type_index {
/**
* Creates a type_index object for the specified type.
*/
template <typename T> type_index(tag_type<T>) noexcept : hash_code_{index<T>} {
}
/**
* Returns the hash code.
*/
std::size_t hash_code() const noexcept {
return hash_code_;
}
private:
/**
* Unique integral index associated to template type argument.
*/
template <typename T> static std::size_t const index;
/**
* Global counter for generating index values.
*/
static std::size_t& counter() noexcept {
static std::size_t counter_;
return counter_;
}
private:
std::size_t hash_code_;
};
template <typename> std::size_t const type_index::index = type_index::counter()++;
/**
* Creates a type_index object for the specified type.
*
* Equivalent to `ext::type_index{ext::tag<T>}`.
*/
template <typename T> type_index make_type_index() noexcept {
return tag<T>;
}
inline bool operator==(type_index const& a, type_index const& b) noexcept {
return a.hash_code() == b.hash_code();
}
inline bool operator!=(type_index const& a, type_index const& b) noexcept {
return !(a == b);
}
inline bool operator<(type_index const& a, type_index const& b) noexcept {
return a.hash_code() < b.hash_code();
}
inline bool operator<=(type_index const& a, type_index const& b) noexcept {
return a.hash_code() <= b.hash_code();
}
inline bool operator>(type_index const& a, type_index const& b) noexcept {
return !(a <= b);
}
inline bool operator>=(type_index const& a, type_index const& b) noexcept {
return !(a < b);
}
}
template <> struct std::hash<ext::type_index> {
using argument_type = ext::type_index;
using result_type = std::size_t;
result_type operator()(argument_type const& t) const noexcept {
return t.hash_code();
}
};

View File

@ -0,0 +1,163 @@
#pragma once
#include "view-modules/generic-view-module.h"
#include <map>
#include <furi/check.h>
#include <gui/view_dispatcher.h>
#include <callback-connector.h>
#include "typeindex_no_rtti.hpp"
/**
* @brief Controller for switching application views and handling inputs and events
*
* @tparam TApp application class
* @tparam TViewModules variadic list of ViewModules
*/
template <typename TApp, typename... TViewModules> class ViewController {
public:
ViewController() {
event_queue = osMessageQueueNew(10, sizeof(typename TApp::Event), NULL);
view_dispatcher = view_dispatcher_alloc();
previous_view_callback_pointer = cbc::obtain_connector(
this, &ViewController<TApp, TViewModules...>::previous_view_callback);
[](...) {
}((this->add_view(ext::make_type_index<TViewModules>().hash_code(), new TViewModules()),
0)...);
gui = static_cast<Gui*>(furi_record_open("gui"));
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
};
~ViewController() {
for(auto& it : holder) {
view_dispatcher_remove_view(view_dispatcher, static_cast<uint32_t>(it.first));
delete it.second;
}
view_dispatcher_free(view_dispatcher);
osMessageQueueDelete(event_queue);
}
/**
* @brief Get ViewModule pointer
*
* @tparam T Concrete ViewModule class
* @return T* ViewModule pointer
*/
template <typename T> T* get() {
uint32_t view_index = ext::make_type_index<T>().hash_code();
furi_check(holder.count(view_index) != 0);
return static_cast<T*>(holder[view_index]);
}
/**
* @brief Get ViewModule pointer by cast
*
* @tparam T Concrete ViewModule class
* @return T* ViewModule pointer
*/
template <typename T> operator T*() {
uint32_t view_index = ext::make_type_index<T>().hash_code();
furi_check(holder.count(view_index) != 0);
return static_cast<T*>(holder[view_index]);
}
/**
* @brief Switch view to ViewModule
*
* @tparam T Concrete ViewModule class
* @return T* ViewModule pointer
*/
template <typename T> void switch_to() {
uint32_t view_index = ext::make_type_index<T>().hash_code();
furi_check(holder.count(view_index) != 0);
view_dispatcher_switch_to_view(view_dispatcher, view_index);
}
/**
* @brief Receive event from app event queue
*
* @param event event pointer
*/
void receive_event(typename TApp::Event* event) {
if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) {
event->type = TApp::EventType::Tick;
}
}
/**
* @brief Send event to app event queue
*
* @param event event pointer
*/
void send_event(typename TApp::Event* event) {
osStatus_t result = osMessageQueuePut(event_queue, event, 0, osWaitForever);
furi_check(result == osOK);
}
private:
/**
* @brief ViewModulesHolder
*
*/
std::map<size_t, GenericViewModule*> holder;
/**
* @brief App event queue
*
*/
osMessageQueueId_t event_queue;
/**
* @brief Main ViewDispatcher pointer
*
*/
ViewDispatcher* view_dispatcher;
/**
* @brief Gui record pointer
*
*/
Gui* gui;
/**
* @brief Previous view callback fn pointer
*
*/
ViewNavigationCallback previous_view_callback_pointer;
/**
* @brief Previous view callback fn
*
* @param context not used
* @return uint32_t VIEW_IGNORE
*/
uint32_t previous_view_callback(void* context) {
(void)context;
typename TApp::Event event;
event.type = TApp::EventType::Back;
if(event_queue != NULL) {
send_event(&event);
}
return VIEW_IGNORE;
}
/**
* @brief Add ViewModule to holder
*
* @param view_index view index in holder
* @param view_module view module pointer
*/
void add_view(size_t view_index, GenericViewModule* view_module) {
furi_check(holder.count(view_index) == 0);
holder[view_index] = view_module;
View* view = view_module->get_view();
view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_index), view);
view_set_previous_callback(view, previous_view_callback_pointer);
}
};

View File

@ -0,0 +1,32 @@
#include "byte-input-vm.h"
ByteInputVM::ByteInputVM() {
byte_input = byte_input_alloc();
}
ByteInputVM::~ByteInputVM() {
byte_input_free(byte_input);
}
View* ByteInputVM::get_view() {
return byte_input_get_view(byte_input);
}
void ByteInputVM::clean() {
byte_input_set_header_text(byte_input, "");
byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0);
}
void ByteInputVM::set_result_callback(
ByteInputCallback input_callback,
ByteChangedCallback changed_callback,
void* callback_context,
uint8_t* bytes,
uint8_t bytes_count) {
byte_input_set_result_callback(
byte_input, input_callback, changed_callback, callback_context, bytes, bytes_count);
}
void ByteInputVM::set_header_text(const char* text) {
byte_input_set_header_text(byte_input, text);
}

View File

@ -0,0 +1,37 @@
#pragma once
#include "generic-view-module.h"
#include <gui/modules/byte_input.h>
class ByteInputVM : public GenericViewModule {
public:
ByteInputVM();
~ByteInputVM() final;
View* get_view() final;
void clean() final;
/**
* @brief Set byte input result callback
*
* @param input_callback input callback fn
* @param changed_callback changed callback fn
* @param callback_context callback context
* @param bytes buffer to use
* @param bytes_count buffer length
*/
void set_result_callback(
ByteInputCallback input_callback,
ByteChangedCallback changed_callback,
void* callback_context,
uint8_t* bytes,
uint8_t bytes_count);
/**
* @brief Set byte input header text
*
* @param text text to be shown
*/
void set_header_text(const char* text);
private:
ByteInput* byte_input;
};

View File

@ -0,0 +1,61 @@
#include "dialog-ex-vm.h"
DialogExVM::DialogExVM() {
dialog_ex = dialog_ex_alloc();
}
DialogExVM::~DialogExVM() {
dialog_ex_free(dialog_ex);
}
View* DialogExVM::get_view() {
return dialog_ex_get_view(dialog_ex);
}
void DialogExVM::clean() {
set_result_callback(NULL);
set_context(NULL);
set_header(NULL, 0, 0, AlignLeft, AlignBottom);
set_text(NULL, 0, 0, AlignLeft, AlignBottom);
set_icon(-1, -1, I_ButtonCenter_7x7);
set_left_button_text(NULL);
set_center_button_text(NULL);
set_right_button_text(NULL);
}
void DialogExVM::set_result_callback(DialogExResultCallback callback) {
dialog_ex_set_result_callback(dialog_ex, callback);
}
void DialogExVM::set_context(void* context) {
dialog_ex_set_context(dialog_ex, context);
}
void DialogExVM::set_header(
const char* text,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical) {
dialog_ex_set_header(dialog_ex, text, x, y, horizontal, vertical);
}
void DialogExVM::set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical) {
dialog_ex_set_text(dialog_ex, text, x, y, horizontal, vertical);
}
void DialogExVM::set_icon(int8_t x, int8_t y, IconName name) {
dialog_ex_set_icon(dialog_ex, x, y, name);
}
void DialogExVM::set_left_button_text(const char* text) {
dialog_ex_set_left_button_text(dialog_ex, text);
}
void DialogExVM::set_center_button_text(const char* text) {
dialog_ex_set_center_button_text(dialog_ex, text);
}
void DialogExVM::set_right_button_text(const char* text) {
dialog_ex_set_right_button_text(dialog_ex, text);
}

View File

@ -0,0 +1,73 @@
#pragma once
#include "generic-view-module.h"
#include <gui/modules/dialog_ex.h>
class DialogExVM : public GenericViewModule {
public:
DialogExVM();
~DialogExVM() final;
View* get_view() final;
void clean() final;
/**
* Set dialog result callback
* @param callback - result callback function
*/
void set_result_callback(DialogExResultCallback callback);
/**
* Set dialog context
* @param context - context pointer, will be passed to result callback
*/
void set_context(void* context);
/**
* Set dialog header text
* If text is null, dialog header will not be rendered
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*/
void set_header(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
/**
* Set dialog text
* If text is null, dialog text will not be rendered
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*/
void set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
/**
* Set dialog icon
* If x or y is negative, dialog icon will not be rendered
* @param x, y - icon position
* @param name - icon to be shown
*/
void set_icon(int8_t x, int8_t y, IconName name);
/**
* Set left button text
* If text is null, left button will not be rendered and processed
* @param text - text to be shown
*/
void set_left_button_text(const char* text);
/**
* Set center button text
* If text is null, center button will not be rendered and processed
* @param text - text to be shown
*/
void set_center_button_text(const char* text);
/**
* Set right button text
* If text is null, right button will not be rendered and processed
* @param text - text to be shown
*/
void set_right_button_text(const char* text);
private:
DialogEx* dialog_ex;
};

View File

@ -0,0 +1,10 @@
#pragma once
#include <gui/view.h>
class GenericViewModule {
public:
GenericViewModule(){};
virtual ~GenericViewModule(){};
virtual View* get_view() = 0;
virtual void clean() = 0;
};

View File

@ -0,0 +1,54 @@
#include "popup-vm.h"
PopupVM::PopupVM() {
popup = popup_alloc();
}
PopupVM::~PopupVM() {
popup_free(popup);
}
View* PopupVM::get_view() {
return popup_get_view(popup);
}
void PopupVM::clean() {
set_callback(NULL);
set_context(NULL);
set_header(NULL, 0, 0, AlignLeft, AlignBottom);
set_text(NULL, 0, 0, AlignLeft, AlignBottom);
set_icon(-1, -1, I_ButtonCenter_7x7);
disable_timeout();
set_timeout(1000);
}
void PopupVM::set_callback(PopupCallback callback) {
popup_set_callback(popup, callback);
}
void PopupVM::set_context(void* context) {
popup_set_context(popup, context);
}
void PopupVM::set_header(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical) {
popup_set_header(popup, text, x, y, horizontal, vertical);
}
void PopupVM::set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical) {
popup_set_text(popup, text, x, y, horizontal, vertical);
}
void PopupVM::set_icon(int8_t x, int8_t y, IconName name) {
popup_set_icon(popup, x, y, name);
}
void PopupVM::set_timeout(uint32_t timeout_in_ms) {
popup_set_timeout(popup, timeout_in_ms);
}
void PopupVM::enable_timeout() {
popup_enable_timeout(popup);
}
void PopupVM::disable_timeout() {
popup_enable_timeout(popup);
}

View File

@ -0,0 +1,68 @@
#pragma once
#include "generic-view-module.h"
#include <gui/modules/popup.h>
class PopupVM : public GenericViewModule {
public:
PopupVM();
~PopupVM() final;
View* get_view() final;
void clean() final;
/**
* Set popup header text
* @param text - text to be shown
*/
void set_callback(PopupCallback callback);
/**
* Set popup context
* @param context - context pointer, will be passed to result callback
*/
void set_context(void* context);
/**
* Set popup header text
* If text is null, popup header will not be rendered
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*/
void set_header(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
/**
* Set popup text
* If text is null, popup text will not be rendered
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*/
void set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
/**
* Set popup icon
* If icon position is negative, popup icon will not be rendered
* @param x, y - icon position
* @param name - icon to be shown
*/
void set_icon(int8_t x, int8_t y, IconName name);
/**
* Set popup timeout
* @param timeout_in_ms - popup timeout value in milliseconds
*/
void set_timeout(uint32_t timeout_in_ms);
/**
* Enable popup timeout
*/
void enable_timeout();
/**
* Disable popup timeout
*/
void disable_timeout();
private:
Popup* popup;
};

View File

@ -0,0 +1,33 @@
#include "submenu-vm.h"
SubmenuVM::SubmenuVM() {
submenu = submenu_alloc();
}
SubmenuVM::~SubmenuVM() {
submenu_free(submenu);
}
View* SubmenuVM::get_view() {
return submenu_get_view(submenu);
}
void SubmenuVM::clean() {
submenu_clean(submenu);
}
SubmenuItem* SubmenuVM::add_item(
const char* label,
uint32_t index,
SubmenuItemCallback callback,
void* callback_context) {
return submenu_add_item(submenu, label, index, callback, callback_context);
}
void SubmenuVM::set_selected_item(uint32_t index) {
submenu_set_selected_item(submenu, index);
}
void SubmenuVM::set_header(const char* header) {
submenu_set_header(submenu, header);
}

View File

@ -0,0 +1,43 @@
#pragma once
#include "generic-view-module.h"
#include <gui/modules/submenu.h>
class SubmenuVM : public GenericViewModule {
public:
SubmenuVM();
~SubmenuVM() final;
View* get_view() final;
void clean() final;
/**
* @brief Add item to submenu
*
* @param label - menu item label
* @param index - menu item index, used for callback, may be the same with other items
* @param callback - menu item callback
* @param callback_context - menu item callback context
* @return SubmenuItem instance that can be used to modify or delete that item
*/
SubmenuItem* add_item(
const char* label,
uint32_t index,
SubmenuItemCallback callback,
void* callback_context);
/**
* @brief Set submenu item selector
*
* @param index index of the item to be selected
*/
void set_selected_item(uint32_t index);
/**
* @brief Set optional header for submenu
*
* @param header header to set
*/
void set_header(const char* header);
private:
Submenu* submenu;
};

View File

@ -0,0 +1,30 @@
#include "text-input-vm.h"
TextInputVM::TextInputVM() {
text_input = text_input_alloc();
}
TextInputVM::~TextInputVM() {
text_input_free(text_input);
}
View* TextInputVM::get_view() {
return text_input_get_view(text_input);
}
void TextInputVM::clean() {
set_result_callback(NULL, NULL, NULL, 0);
set_header_text("");
}
void TextInputVM::set_result_callback(
TextInputCallback callback,
void* callback_context,
char* text,
uint8_t max_text_length) {
text_input_set_result_callback(text_input, callback, callback_context, text, max_text_length);
}
void TextInputVM::set_header_text(const char* text) {
text_input_set_header_text(text_input, text);
}

View File

@ -0,0 +1,35 @@
#pragma once
#include "generic-view-module.h"
#include <gui/modules/text_input.h>
class TextInputVM : public GenericViewModule {
public:
TextInputVM();
~TextInputVM() final;
View* get_view() final;
void clean() final;
/**
* @brief Set text input result callback
*
* @param callback - callback fn
* @param callback_context - callback context
* @param text - text buffer to use
* @param max_text_length - text buffer length
*/
void set_result_callback(
TextInputCallback callback,
void* callback_context,
char* text,
uint8_t max_text_length);
/**
* @brief Set text input header text
*
* @param text - text to be shown
*/
void set_header_text(const char* text);
private:
TextInput* text_input;
};

View File

@ -94,4 +94,9 @@ C_SOURCES += $(wildcard $(LIB_DIR)/irda/*/*.c)
#args lib
CFLAGS += -I$(LIB_DIR)/args
C_SOURCES += $(wildcard $(LIB_DIR)/args/*.c)
C_SOURCES += $(wildcard $(LIB_DIR)/args/*.c)
#scened app template lib
CFLAGS += -I$(LIB_DIR)/app-scened-template
CPP_SOURCES += $(wildcard $(LIB_DIR)/app-scened-template/*.cpp)
CPP_SOURCES += $(wildcard $(LIB_DIR)/app-scened-template/*/*.cpp)