Updated skeleton app
This commit is contained in:
parent
27a50ac914
commit
3d0aed8283
@ -1,17 +1,23 @@
|
||||
# skeleton_app
|
||||
You can use this application as a starting point for creating your own application.
|
||||
You can use this application as a starting point for creating your own application. It contains the skeleton framework to get an application running on the Flipper Zero.
|
||||
|
||||
Please let me know any feedback!
|
||||
- Discord - https://discord.com/invite/NsjCvqwPAd (@CodeAllNight)
|
||||
- YouTube - https://youtube.com/@MrDerekJamison/playlists
|
||||
- GitHub - https://github.com/jamisonderek/flipper-zero-tutorials
|
||||
- Wiki - https://github.com/jamisonderek/flipper-zero-tutorials/wiki
|
||||
|
||||
# Overview
|
||||
This application has three submenu items:
|
||||
* Config
|
||||
* Flip the world
|
||||
* Play
|
||||
* About
|
||||
|
||||
# Config
|
||||
The "Config" menu item currently has 1 setting. You can modify the menu to contain multiple settings if needed.
|
||||
The "Config" menu item currently has 2 settings. The first setting has a selection of 3 options (and demonstrates how the numeric values associated with them don't have to be sequential.) The other setting has a text field which is set by clicking OK while selecting the setting (depending on your application, you may want to do something else when an item is clicked, like bring up a hex selector.)
|
||||
|
||||
# Flip the world
|
||||
The "Flip the world" is where you would put your primary application. It currently just renders the model. Pressing the back button goes back to the main menu.
|
||||
# Play
|
||||
The "Play" screen is where you would put your primary application. The current implementation renders some data. When Left/Right buttons are clicked the value of x changes and the icon moves left/right. Up/Down buttons don't do anything in our implementation. As soon as the OK button is pressed, a tone is made based on the value of x. Pressing the back button goes back to the menu.
|
||||
|
||||
# About
|
||||
The "About" menu item contains information about your application, so people know what to do with it & how to contact you.
|
@ -4,161 +4,429 @@
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "my_app_id_42_icons.h"
|
||||
#include "skeleton_app_icons.h"
|
||||
|
||||
#define TAG "MyAppId"
|
||||
#define TAG "Skeleton"
|
||||
|
||||
// Comment this line if you don't want the backlight to be continuously on.
|
||||
#define BACKLIGHT_ALWAYS_ON yep
|
||||
// Change this to BACKLIGHT_AUTO if you don't want the backlight to be continuously on.
|
||||
#define BACKLIGHT_ON 1
|
||||
|
||||
// Our application menu has 3 items. You can add more items if you want.
|
||||
typedef enum {
|
||||
SkeletonSubmenuIndexConfigure,
|
||||
SkeletonSubmenuIndexGame,
|
||||
SkeletonSubmenuIndexAbout,
|
||||
} SkeletonSubmenuIndex;
|
||||
|
||||
// Each view is a screen we show the user.
|
||||
typedef enum {
|
||||
SkeletonViewSubmenu, // The menu when the app starts
|
||||
SkeletonViewTextInput, // Input for configuring text settings
|
||||
SkeletonViewConfigure, // The configuration screen
|
||||
SkeletonViewGame, // The main screen
|
||||
SkeletonViewAbout, // The about screen with directions, link to social channel, etc.
|
||||
} SkeletonView;
|
||||
|
||||
typedef enum {
|
||||
MyAppSubmenuIndexConfigure,
|
||||
MyAppSubmenuIndexFlipTheWorld,
|
||||
MyAppSubmenuIndexAbout,
|
||||
} MyAppSubmenuIndex;
|
||||
|
||||
typedef enum {
|
||||
MyAppViewSubmenu,
|
||||
MyAppViewConfigure,
|
||||
MyAppViewFlipTheWorld,
|
||||
MyAppViewAbout,
|
||||
} MyAppView;
|
||||
SkeletonEventIdRedrawScreen = 0, // Custom event to redraw the screen
|
||||
SkeletonEventIdOkPressed = 42, // Custom event to process OK button getting pressed down
|
||||
} SkeletonEventId;
|
||||
|
||||
typedef struct {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
NotificationApp* notifications;
|
||||
Submenu* submenu;
|
||||
VariableItemList* variable_item_list_config;
|
||||
View* view_flip_the_world;
|
||||
Widget* widget_about;
|
||||
} MyApp;
|
||||
ViewDispatcher* view_dispatcher; // Switches between our views
|
||||
NotificationApp* notifications; // Used for controlling the backlight
|
||||
Submenu* submenu; // The application menu
|
||||
TextInput* text_input; // The text input screen
|
||||
VariableItemList* variable_item_list_config; // The configuration screen
|
||||
View* view_game; // The main screen
|
||||
Widget* widget_about; // The about screen
|
||||
|
||||
VariableItem* setting_2_item; // The name setting item (so we can update the text)
|
||||
char* temp_buffer; // Temporary buffer for text input
|
||||
uint32_t temp_buffer_size; // Size of temporary buffer
|
||||
|
||||
FuriTimer* timer; // Timer for redrawing the screen
|
||||
} SkeletonApp;
|
||||
|
||||
typedef struct {
|
||||
uint32_t setting_1_index;
|
||||
} MyModel;
|
||||
uint32_t setting_1_index; // The team color setting index
|
||||
FuriString* setting_2_name; // The name setting
|
||||
uint8_t x; // The x coordinate
|
||||
} SkeletonGameModel;
|
||||
|
||||
/**
|
||||
* @brief Callback for navigation events
|
||||
* @brief Callback for exiting the application.
|
||||
* @details This function is called when user press back button. We return VIEW_NONE to
|
||||
* indicate that we want to exit the application.
|
||||
* @param context The context
|
||||
* @param _context The context - unused
|
||||
* @return next view id
|
||||
*/
|
||||
uint32_t my_app_navigation_exit_callback(void* context) {
|
||||
UNUSED(context);
|
||||
static uint32_t skeleton_navigation_exit_callback(void* _context) {
|
||||
UNUSED(_context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for navigation events
|
||||
* @brief Callback for returning to submenu.
|
||||
* @details This function is called when user press back button. We return VIEW_NONE to
|
||||
* indicate that we want to exit the application.
|
||||
* @param context The context
|
||||
* indicate that we want to navigate to the submenu.
|
||||
* @param _context The context - unused
|
||||
* @return next view id
|
||||
*/
|
||||
uint32_t my_app_navigation_submenu_callback(void* context) {
|
||||
UNUSED(context);
|
||||
return MyAppViewSubmenu;
|
||||
static uint32_t skeleton_navigation_submenu_callback(void* _context) {
|
||||
UNUSED(_context);
|
||||
return SkeletonViewSubmenu;
|
||||
}
|
||||
|
||||
void my_app_submenu_callback(void* context, uint32_t index) {
|
||||
MyApp* app = (MyApp*)context;
|
||||
/**
|
||||
* @brief Callback for returning to configure screen.
|
||||
* @details This function is called when user press back button. We return VIEW_NONE to
|
||||
* indicate that we want to navigate to the configure screen.
|
||||
* @param _context The context - unused
|
||||
* @return next view id
|
||||
*/
|
||||
static uint32_t skeleton_navigation_configure_callback(void* _context) {
|
||||
UNUSED(_context);
|
||||
return SkeletonViewConfigure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle submenu item selection.
|
||||
* @details This function is called when user selects an item from the submenu.
|
||||
* @param context The context - SkeletonApp object.
|
||||
* @param index The SkeletonSubmenuIndex item that was clicked.
|
||||
*/
|
||||
static void skeleton_submenu_callback(void* context, uint32_t index) {
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
switch(index) {
|
||||
case MyAppSubmenuIndexConfigure:
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MyAppViewConfigure);
|
||||
case SkeletonSubmenuIndexConfigure:
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewConfigure);
|
||||
break;
|
||||
case MyAppSubmenuIndexFlipTheWorld:
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MyAppViewFlipTheWorld);
|
||||
case SkeletonSubmenuIndexGame:
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewGame);
|
||||
break;
|
||||
case MyAppSubmenuIndexAbout:
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MyAppViewAbout);
|
||||
case SkeletonSubmenuIndexAbout:
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewAbout);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t setting_1_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
char* setting_1_names[] =
|
||||
{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
|
||||
|
||||
void my_app_setting_1_change(VariableItem* item) {
|
||||
MyApp* app = variable_item_get_context(item);
|
||||
/**
|
||||
* Our 1st sample setting is a team color. We have 3 options: red, green, and blue.
|
||||
*/
|
||||
static const char* setting_1_config_label = "Team color";
|
||||
static uint8_t setting_1_values[] = {1, 2, 4};
|
||||
static char* setting_1_names[] = {"Red", "Green", "Blue"};
|
||||
static void skeleton_setting_1_change(VariableItem* item) {
|
||||
SkeletonApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(item, setting_1_names[index]);
|
||||
MyModel* model = view_get_model(app->view_flip_the_world);
|
||||
SkeletonGameModel* model = view_get_model(app->view_game);
|
||||
model->setting_1_index = index;
|
||||
}
|
||||
|
||||
void my_app_view_draw_callback(Canvas* canvas, void* model) {
|
||||
MyModel* my_model = (MyModel*)model;
|
||||
canvas_draw_str(canvas, 25, 15, "FLIP THE WORLD!!!");
|
||||
canvas_draw_icon(canvas, 64, 32, &I_glyph_1_7x9);
|
||||
canvas_draw_str(canvas, 64, 60, setting_1_names[my_model->setting_1_index]);
|
||||
/**
|
||||
* Our 2nd sample setting is a text field. When the user clicks OK on the configuration
|
||||
* setting we use a text input screen to allow the user to enter a name. This function is
|
||||
* called when the user clicks OK on the text input screen.
|
||||
*/
|
||||
static const char* setting_2_config_label = "Name";
|
||||
static const char* setting_2_entry_text = "Enter name";
|
||||
static const char* setting_2_default_value = "Bob";
|
||||
static void skeleton_setting_2_text_updated(void* context) {
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
bool redraw = true;
|
||||
with_view_model(
|
||||
app->view_game,
|
||||
SkeletonGameModel * model,
|
||||
{
|
||||
furi_string_set(model->setting_2_name, app->temp_buffer);
|
||||
variable_item_set_current_value_text(
|
||||
app->setting_2_item, furi_string_get_cstr(model->setting_2_name));
|
||||
},
|
||||
redraw);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewConfigure);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback when item in configuration screen is clicked.
|
||||
* @details This function is called when user clicks OK on an item in the configuration screen.
|
||||
* If the item clicked is our text field then we switch to the text input screen.
|
||||
* @param context The context - SkeletonApp object.
|
||||
* @param index - The index of the item that was clicked.
|
||||
*/
|
||||
static void skeleton_setting_item_clicked(void* context, uint32_t index) {
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
index++; // The index starts at zero, but we want to start at 1.
|
||||
|
||||
// Our configuration UI has the 2nd item as a text field.
|
||||
if(index == 2) {
|
||||
// Header to display on the text input screen.
|
||||
text_input_set_header_text(app->text_input, setting_2_entry_text);
|
||||
|
||||
// Copy the current name into the temporary buffer.
|
||||
bool redraw = false;
|
||||
with_view_model(
|
||||
app->view_game,
|
||||
SkeletonGameModel * model,
|
||||
{
|
||||
strncpy(
|
||||
app->temp_buffer,
|
||||
furi_string_get_cstr(model->setting_2_name),
|
||||
app->temp_buffer_size);
|
||||
},
|
||||
redraw);
|
||||
|
||||
// Configure the text input. When user enters text and clicks OK, skeleton_setting_text_updated be called.
|
||||
bool clear_previous_text = false;
|
||||
text_input_set_result_callback(
|
||||
app->text_input,
|
||||
skeleton_setting_2_text_updated,
|
||||
app,
|
||||
app->temp_buffer,
|
||||
app->temp_buffer_size,
|
||||
clear_previous_text);
|
||||
|
||||
// Pressing the BACK button will reload the configure screen.
|
||||
view_set_previous_callback(
|
||||
text_input_get_view(app->text_input), skeleton_navigation_configure_callback);
|
||||
|
||||
// Show text input dialog.
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewTextInput);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for drawing the game screen.
|
||||
* @details This function is called when the screen needs to be redrawn, like when the model gets updated.
|
||||
* @param canvas The canvas to draw on.
|
||||
* @param model The model - MyModel object.
|
||||
*/
|
||||
static void skeleton_view_game_draw_callback(Canvas* canvas, void* model) {
|
||||
SkeletonGameModel* my_model = (SkeletonGameModel*)model;
|
||||
canvas_draw_icon(canvas, my_model->x, 20, &I_glyph_1_14x40);
|
||||
canvas_draw_str(canvas, 1, 10, "LEFT/RIGHT to change x");
|
||||
FuriString* xstr = furi_string_alloc();
|
||||
furi_string_printf(xstr, "x: %u OK=play tone", my_model->x);
|
||||
canvas_draw_str(canvas, 44, 24, furi_string_get_cstr(xstr));
|
||||
furi_string_printf(xstr, "random: %u", (uint8_t)(furi_hal_random_get() % 256));
|
||||
canvas_draw_str(canvas, 44, 36, furi_string_get_cstr(xstr));
|
||||
furi_string_printf(
|
||||
xstr,
|
||||
"team: %s (%u)",
|
||||
setting_1_names[my_model->setting_1_index],
|
||||
setting_1_values[my_model->setting_1_index]);
|
||||
canvas_draw_str(canvas, 44, 48, furi_string_get_cstr(xstr));
|
||||
furi_string_printf(xstr, "name: %s", furi_string_get_cstr(my_model->setting_2_name));
|
||||
canvas_draw_str(canvas, 44, 60, furi_string_get_cstr(xstr));
|
||||
furi_string_free(xstr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for timer elapsed.
|
||||
* @details This function is called when the timer is elapsed. We use this to queue a redraw event.
|
||||
* @param context The context - SkeletonApp object.
|
||||
*/
|
||||
static void skeleton_view_game_timer_callback(void* context) {
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SkeletonEventIdRedrawScreen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback when the user starts the game screen.
|
||||
* @details This function is called when the user enters the game screen. We start a timer to
|
||||
* redraw the screen periodically (so the random number is refreshed).
|
||||
* @param context The context - SkeletonApp object.
|
||||
*/
|
||||
static void skeleton_view_game_enter_callback(void* context) {
|
||||
uint32_t period = furi_ms_to_ticks(200);
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
furi_assert(app->timer == NULL);
|
||||
app->timer =
|
||||
furi_timer_alloc(skeleton_view_game_timer_callback, FuriTimerTypePeriodic, context);
|
||||
furi_timer_start(app->timer, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback when the user exits the game screen.
|
||||
* @details This function is called when the user exits the game screen. We stop the timer.
|
||||
* @param context The context - SkeletonApp object.
|
||||
*/
|
||||
static void skeleton_view_game_exit_callback(void* context) {
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
furi_timer_stop(app->timer);
|
||||
furi_timer_free(app->timer);
|
||||
app->timer = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for custom events.
|
||||
* @details This function is called when a custom event is sent to the view dispatcher.
|
||||
* @param event The event id - SkeletonEventId value.
|
||||
* @param context The context - SkeletonApp object.
|
||||
*/
|
||||
static bool skeleton_view_game_custom_event_callback(uint32_t event, void* context) {
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
switch(event) {
|
||||
case SkeletonEventIdRedrawScreen:
|
||||
// Redraw screen by passing true to last parameter of with_view_model.
|
||||
{
|
||||
bool redraw = true;
|
||||
with_view_model(
|
||||
app->view_game, SkeletonGameModel * _model, { UNUSED(_model); }, redraw);
|
||||
return true;
|
||||
}
|
||||
case SkeletonEventIdOkPressed:
|
||||
// Process the OK button. We play a tone based on the x coordinate.
|
||||
if(furi_hal_speaker_acquire(500)) {
|
||||
float frequency;
|
||||
bool redraw = false;
|
||||
with_view_model(
|
||||
app->view_game,
|
||||
SkeletonGameModel * model,
|
||||
{ frequency = model->x * 100 + 100; },
|
||||
redraw);
|
||||
furi_hal_speaker_start(frequency, 1.0);
|
||||
furi_delay_ms(100);
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_release();
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for game screen input.
|
||||
* @details This function is called when the user presses a button while on the game screen.
|
||||
* @param event The event - InputEvent object.
|
||||
* @param context The context - SkeletonApp object.
|
||||
* @return true if the event was handled, false otherwise.
|
||||
*/
|
||||
static bool skeleton_view_game_input_callback(InputEvent* event, void* context) {
|
||||
SkeletonApp* app = (SkeletonApp*)context;
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
// Left button clicked, reduce x coordinate.
|
||||
bool redraw = true;
|
||||
with_view_model(
|
||||
app->view_game,
|
||||
SkeletonGameModel * model,
|
||||
{
|
||||
if(model->x > 0) {
|
||||
model->x--;
|
||||
}
|
||||
},
|
||||
redraw);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
// Right button clicked, increase x coordinate.
|
||||
bool redraw = true;
|
||||
with_view_model(
|
||||
app->view_game,
|
||||
SkeletonGameModel * model,
|
||||
{
|
||||
// Should we have some maximum value?
|
||||
model->x++;
|
||||
},
|
||||
redraw);
|
||||
}
|
||||
} else if(event->type == InputTypePress) {
|
||||
if(event->key == InputKeyOk) {
|
||||
// We choose to send a custom event when user presses OK button. skeleton_custom_event_callback will
|
||||
// handle our SkeletonEventIdOkPressed event. We could have just put the code from
|
||||
// skeleton_custom_event_callback here, it's a matter of preference.
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SkeletonEventIdOkPressed);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool my_app_view_input_callback(InputEvent* event, void* context) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
MyApp* my_app_alloc() {
|
||||
MyApp* app = (MyApp*)malloc(sizeof(MyApp));
|
||||
/**
|
||||
* @brief Allocate the skeleton application.
|
||||
* @details This function allocates the skeleton application resources.
|
||||
* @return SkeletonApp object.
|
||||
*/
|
||||
static SkeletonApp* skeleton_app_alloc() {
|
||||
SkeletonApp* app = (SkeletonApp*)malloc(sizeof(SkeletonApp));
|
||||
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
|
||||
app->submenu = submenu_alloc();
|
||||
submenu_add_item(
|
||||
app->submenu, "Config", MyAppSubmenuIndexConfigure, my_app_submenu_callback, app);
|
||||
app->submenu, "Config", SkeletonSubmenuIndexConfigure, skeleton_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Flip the world",
|
||||
MyAppSubmenuIndexFlipTheWorld,
|
||||
my_app_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(app->submenu, "About", MyAppSubmenuIndexAbout, my_app_submenu_callback, app);
|
||||
view_set_previous_callback(submenu_get_view(app->submenu), my_app_navigation_exit_callback);
|
||||
app->submenu, "Play", SkeletonSubmenuIndexGame, skeleton_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "About", SkeletonSubmenuIndexAbout, skeleton_submenu_callback, app);
|
||||
view_set_previous_callback(submenu_get_view(app->submenu), skeleton_navigation_exit_callback);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MyAppViewSubmenu, submenu_get_view(app->submenu));
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MyAppViewSubmenu);
|
||||
app->view_dispatcher, SkeletonViewSubmenu, submenu_get_view(app->submenu));
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewSubmenu);
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, SkeletonViewTextInput, text_input_get_view(app->text_input));
|
||||
app->temp_buffer_size = 32;
|
||||
app->temp_buffer = (char*)malloc(app->temp_buffer_size);
|
||||
|
||||
app->variable_item_list_config = variable_item_list_alloc();
|
||||
variable_item_list_reset(app->variable_item_list_config);
|
||||
VariableItem* item = variable_item_list_add(
|
||||
app->variable_item_list_config,
|
||||
"Setting 1",
|
||||
setting_1_config_label,
|
||||
COUNT_OF(setting_1_values),
|
||||
my_app_setting_1_change,
|
||||
skeleton_setting_1_change,
|
||||
app);
|
||||
uint8_t setting_1_index = 0;
|
||||
variable_item_set_current_value_index(item, setting_1_index);
|
||||
variable_item_set_current_value_text(item, setting_1_names[setting_1_index]);
|
||||
|
||||
FuriString* setting_2_name = furi_string_alloc();
|
||||
furi_string_set_str(setting_2_name, setting_2_default_value);
|
||||
app->setting_2_item = variable_item_list_add(
|
||||
app->variable_item_list_config, setting_2_config_label, 1, NULL, NULL);
|
||||
variable_item_set_current_value_text(
|
||||
app->setting_2_item, furi_string_get_cstr(setting_2_name));
|
||||
variable_item_list_set_enter_callback(
|
||||
app->variable_item_list_config, skeleton_setting_item_clicked, app);
|
||||
|
||||
view_set_previous_callback(
|
||||
variable_item_list_get_view(app->variable_item_list_config),
|
||||
my_app_navigation_submenu_callback);
|
||||
skeleton_navigation_submenu_callback);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
MyAppViewConfigure,
|
||||
SkeletonViewConfigure,
|
||||
variable_item_list_get_view(app->variable_item_list_config));
|
||||
|
||||
app->view_flip_the_world = view_alloc();
|
||||
view_set_draw_callback(app->view_flip_the_world, my_app_view_draw_callback);
|
||||
view_set_input_callback(app->view_flip_the_world, my_app_view_input_callback);
|
||||
view_set_previous_callback(app->view_flip_the_world, my_app_navigation_submenu_callback);
|
||||
view_allocate_model(app->view_flip_the_world, ViewModelTypeLockFree, sizeof(MyModel));
|
||||
MyModel* model = view_get_model(app->view_flip_the_world);
|
||||
app->view_game = view_alloc();
|
||||
view_set_draw_callback(app->view_game, skeleton_view_game_draw_callback);
|
||||
view_set_input_callback(app->view_game, skeleton_view_game_input_callback);
|
||||
view_set_previous_callback(app->view_game, skeleton_navigation_submenu_callback);
|
||||
view_set_enter_callback(app->view_game, skeleton_view_game_enter_callback);
|
||||
view_set_exit_callback(app->view_game, skeleton_view_game_exit_callback);
|
||||
view_set_context(app->view_game, app);
|
||||
view_set_custom_callback(app->view_game, skeleton_view_game_custom_event_callback);
|
||||
view_allocate_model(app->view_game, ViewModelTypeLockFree, sizeof(SkeletonGameModel));
|
||||
SkeletonGameModel* model = view_get_model(app->view_game);
|
||||
model->setting_1_index = setting_1_index;
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MyAppViewFlipTheWorld, app->view_flip_the_world);
|
||||
model->setting_2_name = setting_2_name;
|
||||
model->x = 0;
|
||||
view_dispatcher_add_view(app->view_dispatcher, SkeletonViewGame, app->view_game);
|
||||
|
||||
app->widget_about = widget_alloc();
|
||||
widget_add_text_scroll_element(
|
||||
@ -169,32 +437,40 @@ MyApp* my_app_alloc() {
|
||||
64,
|
||||
"This is a sample application.\n---\nReplace code and message\nwith your content!\n\nauthor: @codeallnight\nhttps://discord.com/invite/NsjCvqwPAd\nhttps://youtube.com/@MrDerekJamison");
|
||||
view_set_previous_callback(
|
||||
widget_get_view(app->widget_about), my_app_navigation_submenu_callback);
|
||||
widget_get_view(app->widget_about), skeleton_navigation_submenu_callback);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MyAppViewAbout, widget_get_view(app->widget_about));
|
||||
app->view_dispatcher, SkeletonViewAbout, widget_get_view(app->widget_about));
|
||||
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
#ifdef BACKLIGHT_ALWAYS_ON
|
||||
#ifdef BACKLIGHT_ON
|
||||
notification_message(app->notifications, &sequence_display_backlight_enforce_on);
|
||||
#endif
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void my_app_free(MyApp* app) {
|
||||
#ifdef BACKLIGHT_ALWAYS_ON
|
||||
/**
|
||||
* @brief Free the skeleton application.
|
||||
* @details This function frees the skeleton application resources.
|
||||
* @param app The skeleton application object.
|
||||
*/
|
||||
static void skeleton_app_free(SkeletonApp* app) {
|
||||
#ifdef BACKLIGHT_ON
|
||||
notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
|
||||
#endif
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MyAppViewAbout);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewTextInput);
|
||||
text_input_free(app->text_input);
|
||||
free(app->temp_buffer);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewAbout);
|
||||
widget_free(app->widget_about);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MyAppViewFlipTheWorld);
|
||||
view_free(app->view_flip_the_world);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MyAppViewConfigure);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewGame);
|
||||
view_free(app->view_game);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewConfigure);
|
||||
variable_item_list_free(app->variable_item_list_config);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MyAppViewSubmenu);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
furi_record_close(RECORD_GUI);
|
||||
@ -202,12 +478,19 @@ void my_app_free(MyApp* app) {
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t my_app_name_app(void* p) {
|
||||
UNUSED(p);
|
||||
/**
|
||||
* @brief Main function for skeleton application.
|
||||
* @details This function is the entry point for the skeleton application. It should be defined in
|
||||
* application.fam as the entry_point setting.
|
||||
* @param _p Input parameter - unused
|
||||
* @return 0 - Success
|
||||
*/
|
||||
int32_t main_skeleton_app(void* _p) {
|
||||
UNUSED(_p);
|
||||
|
||||
MyApp* app = my_app_alloc();
|
||||
SkeletonApp* app = skeleton_app_alloc();
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
my_app_free(app);
|
||||
skeleton_app_free(app);
|
||||
return 0;
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
App(
|
||||
appid="my_app_id_42",
|
||||
name="[SUBMENU] My App Name",
|
||||
appid="skeleton_app",
|
||||
name="[WIP] Skeleton Sample App",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="my_app_name_app",
|
||||
entry_point="main_skeleton_app",
|
||||
stack_size=4 * 1024,
|
||||
requires=[
|
||||
"gui",
|
||||
@ -11,4 +11,5 @@ App(
|
||||
fap_icon="app.png",
|
||||
fap_category="GPIO",
|
||||
fap_icon_assets="assets",
|
||||
fap_description="Skeleton Sample App. This is intended to be used as a starting point for new applications with one primary screen.",
|
||||
)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.7 KiB |
Loading…
Reference in New Issue
Block a user