Skip to content

Commit c442af4

Browse files
committed
Setup Main Menu
[Problem] Currently the application does nothing. [Solution] This updates the app to have a simple main menu, with the options that will be available in the final version of the app. It also sets up some directory structures, dividing the code up into pieces that are more easily digestible. Specifically, different scenes can be managed on their own, without being as dependent on a single file. [Testing] Built and confirmed working on device. Should probably investigate unit testing but that can come later.
1 parent 6cc8608 commit c442af4

10 files changed

+423
-12
lines changed

application.fam

+1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ App(
1313
fap_author="Gerald McAlister",
1414
fap_weburl="https://github.com/GEMISIS/tone_gen",
1515
fap_icon_assets="images", # Image assets to compile for this application
16+
sources=["src/*.c", "src/menus/*.c", "src/utils/*.c"],
1617
)

src/app_context.c

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include <gui/modules/menu.h>
2+
#include <gui/modules/popup.h>
3+
4+
#include "tone_gen.h"
5+
#include "app_context.h"
6+
7+
/** custom event handler - passes the event to the scene manager */
8+
bool viewDispatcherCustomCallback(void* context, uint32_t custom_event) {
9+
furi_assert(context);
10+
struct AppContext_t* appContext = context;
11+
return scene_manager_handle_custom_event(appContext->scene_manager, custom_event);
12+
}
13+
14+
/** navigation event handler - passes the event to the scene manager */
15+
bool viewDispatcherNavigationCallback(void* context) {
16+
furi_assert(context);
17+
struct AppContext_t* appContext = context;
18+
return scene_manager_handle_back_event(appContext->scene_manager);
19+
}
20+
21+
AppContextStatus initializeAppContext(
22+
struct AppContext_t** context,
23+
const SceneManagerHandlers* sceneManagerHandlers) {
24+
FURI_LOG_I(TAG, "Allocating memory for app context");
25+
26+
*context = malloc(sizeof(struct AppContext_t));
27+
if(*context == NULL) {
28+
FURI_LOG_E(TAG, "Failed to allocate memory for app context");
29+
return APP_CONTEXT_CANT_ALLOCATE;
30+
}
31+
32+
// Allocate our scene manager with the handlers provided
33+
FURI_LOG_I(TAG, "Setting up the scene manager");
34+
(*context)->scene_manager = scene_manager_alloc(sceneManagerHandlers, *context);
35+
36+
// Now setup our view dispatchers
37+
FURI_LOG_I(TAG, "Setting up the view dispatcher");
38+
(*context)->view_dispatcher = view_dispatcher_alloc();
39+
view_dispatcher_enable_queue((*context)->view_dispatcher);
40+
41+
FURI_LOG_I(TAG, "Setting view dispatcher callbacks");
42+
view_dispatcher_set_event_callback_context((*context)->view_dispatcher, (*context));
43+
FURI_LOG_I(TAG, "Setting view dispatcher custom event callbacks");
44+
view_dispatcher_set_custom_event_callback(
45+
(*context)->view_dispatcher, viewDispatcherCustomCallback);
46+
FURI_LOG_I(TAG, "Setting view dispatcher navigation event callbacks");
47+
view_dispatcher_set_navigation_event_callback(
48+
(*context)->view_dispatcher, viewDispatcherNavigationCallback);
49+
FURI_LOG_I(TAG, "Setting view dispatcher callbacks done");
50+
51+
return APP_CONTEXT_OK;
52+
}
53+
54+
AppContextStatus freeAppContextViews(struct AppContext_t** context) {
55+
FURI_LOG_I(TAG, "Freeing views");
56+
struct ListNode_t* root = (*context)->activeViews;
57+
while(root) {
58+
struct View_t* view = root->data;
59+
view_dispatcher_remove_view((*context)->view_dispatcher, view->viewId);
60+
61+
switch(view->type) {
62+
case MENU:
63+
menu_free(view->viewData);
64+
break;
65+
case POPUP:
66+
popup_free(view->viewData);
67+
break;
68+
}
69+
root = root->next;
70+
}
71+
FURI_LOG_I(TAG, "Removing all views from list");
72+
LinkedListStatus result = removeAllNodes(&(*context)->activeViews);
73+
if(result != LIST_OK) {
74+
return APP_CONTEXT_UNKNOWN_ERROR;
75+
}
76+
return APP_CONTEXT_OK;
77+
}
78+
79+
AppContextStatus freeAppContext(struct AppContext_t** context) {
80+
FURI_LOG_I(TAG, "Ensuring views are free'd");
81+
AppContextStatus result = freeAppContextViews(context);
82+
if(result != APP_CONTEXT_OK) {
83+
return APP_CONTEXT_CANT_FREE_VIEWS;
84+
}
85+
FURI_LOG_I(TAG, "Freeing the scene");
86+
scene_manager_free((*context)->scene_manager);
87+
FURI_LOG_I(TAG, "Freeing the view dispatcher");
88+
view_dispatcher_free((*context)->view_dispatcher);
89+
FURI_LOG_I(TAG, "Freeing the app context");
90+
free((*context));
91+
return APP_CONTEXT_OK;
92+
}

src/app_context.h

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#ifndef _APP_CONTEXT_H_
2+
3+
#define _APP_CONTEXT_H_
4+
5+
#include <gui/view_dispatcher.h>
6+
#include <gui/scene_manager.h>
7+
8+
#include "utils/linked_list.h"
9+
10+
typedef enum {
11+
MENU,
12+
POPUP,
13+
} ViewType;
14+
15+
struct View_t {
16+
ViewType type;
17+
int viewId;
18+
void* viewData;
19+
};
20+
21+
typedef enum {
22+
APP_CONTEXT_OK = 0,
23+
APP_CONTEXT_CANT_ALLOCATE = -1,
24+
APP_CONTEXT_CANT_FREE_VIEWS = -2,
25+
APP_CONTEXT_UNKNOWN_ERROR = -3,
26+
} AppContextStatus;
27+
28+
struct AppContext_t {
29+
SceneManager* scene_manager;
30+
ViewDispatcher* view_dispatcher;
31+
struct ListNode_t* activeViews;
32+
};
33+
34+
/// @brief Creates an app context with the desired scene handlers.
35+
/// @param context The app context to populate. Will be passed through to the supplied scene handlers.
36+
/// @param sceneManagerHandlers The scene handlers to use for each scene in your app.
37+
/// @return Returns APP_CONTEXT_OK on success, APP_CONTEXT_CANT_ALLOCATE if there is an error.
38+
AppContextStatus initializeAppContext(
39+
struct AppContext_t** context,
40+
const SceneManagerHandlers* sceneManagerHandlers);
41+
42+
/// @brief Frees and removes all views attached to the app context.
43+
/// @param context The app context to remove all of the views from.
44+
/// @return Returns APP_CONTEXT_OK on success. Should not error.
45+
AppContextStatus freeAppContextViews(struct AppContext_t** context);
46+
47+
/// @brief Frees the app context entirely, cleaning it up from usage.
48+
/// @param context The app context to clean up.
49+
/// @return Returns APP_CONTEXT_OK on success. Should not error.
50+
AppContextStatus freeAppContext(struct AppContext_t** context);
51+
52+
#endif

src/menus/main_menu.c

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <gui/modules/menu.h>
2+
#include <gui/modules/popup.h>
3+
4+
#include "main_menu.h"
5+
#include "../app_context.h"
6+
#include "../tone_gen.h"
7+
#include "../utils/linked_list.h"
8+
9+
/** indices for menu items */
10+
typedef enum {
11+
ToneGenAppMenuSelection_Play,
12+
ToneGenAppMenuSelection_Adjust
13+
} ToneGenAppMenuSelection;
14+
15+
/** main menu callback - sends a custom event to the scene manager based on the menu selection */
16+
void menu_callback_main_menu(void* context, uint32_t index) {
17+
FURI_LOG_I(TAG, "menu_callback_main_menu");
18+
UNUSED(context);
19+
// struct AppContext_t* app = context;
20+
switch(index) {
21+
case ToneGenAppMenuSelection_Play:
22+
FURI_LOG_I(TAG, "selection one");
23+
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_StartPlayback);
24+
break;
25+
case ToneGenAppMenuSelection_Adjust:
26+
FURI_LOG_I(TAG, "selection two");
27+
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_AdjustTone);
28+
break;
29+
}
30+
}
31+
32+
/** resets the menu, gives it content, callbacks and selection enums */
33+
void scene_on_enter_main_menu(void* context) {
34+
FURI_LOG_I(TAG, "scene_on_enter_main_menu");
35+
struct AppContext_t* app = (struct AppContext_t*)context;
36+
// Setup our menu
37+
FURI_LOG_D(TAG, "Adding view menu");
38+
struct View_t* menuView = malloc(sizeof(struct View_t));
39+
menuView->viewData = menu_alloc();
40+
menuView->viewId = ToneGenAppView_Menu;
41+
menuView->type = MENU;
42+
view_dispatcher_add_view(
43+
app->view_dispatcher, ToneGenAppView_Menu, menu_get_view(menuView->viewData));
44+
45+
// Set the currently active view
46+
addNode(&app->activeViews, menuView);
47+
menu_reset(menuView->viewData);
48+
49+
// NB. icons are specified as NULL below, because:
50+
// * icons are _always_ animated by the menu
51+
// * the icons provided (&I_one, &I_two) are generated by the build process
52+
// * these icons do not have a framerate (resulting in a division by zero)
53+
menu_add_item(
54+
menuView->viewData,
55+
"Play Tone",
56+
NULL,
57+
ToneGenAppMenuSelection_Play,
58+
menu_callback_main_menu,
59+
app);
60+
menu_add_item(
61+
menuView->viewData,
62+
"Adjust Tone",
63+
NULL,
64+
ToneGenAppMenuSelection_Adjust,
65+
menu_callback_main_menu,
66+
app);
67+
view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_Menu);
68+
}
69+
70+
/** main menu event handler - switches scene based on the event */
71+
bool scene_on_event_main_menu(void* context, SceneManagerEvent event) {
72+
FURI_LOG_I(TAG, "scene_on_event_main_menu");
73+
UNUSED(context);
74+
// struct AppContext_t* app = context;
75+
bool consumed = false;
76+
switch(event.type) {
77+
case SceneManagerEventTypeCustom:
78+
// switch(event.event) {
79+
// case ToneGenAppEvent_StartPlayback:
80+
// scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
81+
// consumed = true;
82+
// break;
83+
// case ToneGenAppEvent_AdjustTone:
84+
// scene_manager_next_scene(app->scene_manager, ToneGenAppScene_AdjustTone);
85+
// consumed = true;
86+
// break;
87+
// }
88+
break;
89+
default: // eg. SceneManagerEventTypeBack, SceneManagerEventTypeTick
90+
consumed = false;
91+
break;
92+
}
93+
return consumed;
94+
}
95+
96+
void scene_on_exit_main_menu(void* context) {
97+
FURI_LOG_I(TAG, "scene_on_exit_main_menu");
98+
struct AppContext_t* app = context;
99+
freeAppContextViews(&app);
100+
}

src/menus/main_menu.h

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef _MAIN_MENU_H_
2+
3+
#define _MAIN_MENU_H_
4+
5+
#include <gui/scene_manager.h>
6+
7+
void scene_on_enter_main_menu(void* context);
8+
bool scene_on_event_main_menu(void* context, SceneManagerEvent event);
9+
void scene_on_exit_main_menu(void* context);
10+
11+
#endif

src/tone_gen.c

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include <gui/modules/menu.h>
2+
3+
/* generated by fbt from .png files in images folder */
4+
#include <tone_gen_icons.h>
5+
6+
#include "app_context.h"
7+
#include "tone_gen.h"
8+
9+
#include "menus/main_menu.h"
10+
11+
/** collection of all scene on_enter handlers - in the same order as their enum */
12+
void (*const scene_on_enter_handlers[])(void*) = {
13+
scene_on_enter_main_menu,
14+
};
15+
16+
/** collection of all scene on event handlers - in the same order as their enum */
17+
bool (*const scene_on_event_handlers[])(void*, SceneManagerEvent) = {
18+
scene_on_event_main_menu,
19+
};
20+
21+
/** collection of all scene on exit handlers - in the same order as their enum */
22+
void (*const scene_on_exit_handlers[])(void*) = {
23+
scene_on_exit_main_menu,
24+
};
25+
26+
const SceneManagerHandlers scene_event_handlers = {
27+
.on_enter_handlers = scene_on_enter_handlers,
28+
.on_event_handlers = scene_on_event_handlers,
29+
.on_exit_handlers = scene_on_exit_handlers,
30+
.scene_num = ToneGenAppScene_count};
31+
32+
int32_t tone_gen_app(void* p) {
33+
UNUSED(p);
34+
35+
FURI_LOG_I(TAG, "Tone gen app starting...");
36+
37+
struct AppContext_t* appContext;
38+
AppContextStatus result = initializeAppContext(&appContext, &scene_event_handlers);
39+
40+
if(result == APP_CONTEXT_OK) {
41+
// set the scene and launch the main loop
42+
FURI_LOG_D(TAG, "Setting the scene");
43+
Gui* gui = furi_record_open(RECORD_GUI);
44+
view_dispatcher_attach_to_gui(
45+
appContext->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
46+
scene_manager_next_scene(appContext->scene_manager, ToneGenAppScene_MainMenu);
47+
FURI_LOG_D(TAG, "Starting the view dispatcher");
48+
view_dispatcher_run(appContext->view_dispatcher);
49+
50+
// free all memory
51+
FURI_LOG_D(TAG, "Ending the app");
52+
furi_record_close(RECORD_GUI);
53+
freeAppContext(&appContext);
54+
return 0;
55+
}
56+
return -1;
57+
}

src/tone_gen.h

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef _TONE_GEN_H_
2+
3+
#define _TONE_GEN_H_
4+
5+
#define TAG "tone-gen"
6+
7+
#include <furi.h>
8+
#include <music_worker/music_worker.h>
9+
10+
// ids for all scenes used by the app
11+
typedef enum { ToneGenAppScene_MainMenu, ToneGenAppScene_count } ToneGenAppScene;
12+
13+
// ids for the 2 types of view used by the app
14+
typedef enum { ToneGenAppView_Menu, ToneGenAppView_Popup } ToneGenAppView;
15+
16+
#endif

0 commit comments

Comments
 (0)