Skip to content

Commit 82ea48e

Browse files
committed
Re-architect View Management + Add Settings Scene
[Problem] Attempting to add a settings scene, but it turns out that removing an in-use view by the view dispatcher causes some pretty gnarly issues. [Solution] As a result, it is better to instead share views between scenes for simplicity. This is a small sacrifice in heap memory for simplicity, though maybe this can be re-addressed later. [Testing] Confirmed working on device.
1 parent e24f681 commit 82ea48e

8 files changed

+221
-59
lines changed

README.MD

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This project depends on [uFBT](https://github.com/flipperdevices/flipperzero-ufb
1111
The application is structured such that each scene can be self contained, with the sharing of data between scenes done via the app context. All of the scenes can be found with their corresponding source and header files in the `src/scenes` directory. The scenes do as follows:
1212

1313
- **Starting Scene**: The scene where the application starts. This has the main menu options that users see when they start the app.
14+
- **Settings Scene**: The scene where users can configure the tone's properties.
1415

1516
Note as well that the app context file is generic, and designed in such as way that it should not need to be updated for things specific to the application. This allows for an easier time to allow scenes to self manage, insteaed of having somewhere else that centrally manages everything.
1617

src/app_context.c

+30-16
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ bool viewDispatcherNavigationCallback(void* context) {
2020

2121
AppContextStatus initializeAppContext(
2222
struct AppContext_t** context,
23+
int viewsCount,
2324
const SceneManagerHandlers* sceneManagerHandlers) {
2425
FURI_LOG_I(TAG, "Allocating memory for app context");
2526

@@ -48,31 +49,44 @@ AppContextStatus initializeAppContext(
4849
(*context)->view_dispatcher, viewDispatcherNavigationCallback);
4950
FURI_LOG_I(TAG, "Setting view dispatcher callbacks done");
5051

52+
(*context)->activeViews = malloc(sizeof(struct View_t) * viewsCount);
53+
if((*context)->activeViews == NULL) {
54+
return APP_CONTEXT_CANT_ALLOCATE;
55+
}
56+
(*context)->activeViewsCount = viewsCount;
57+
58+
return APP_CONTEXT_OK;
59+
}
60+
61+
AppContextStatus addViewToAppContext(struct AppContext_t** context, struct View_t* view) {
62+
if(view->viewId > (*context)->activeViewsCount || view->viewId < 0) {
63+
return APP_CONTEXT_NOT_ENOUGH_VIEWS;
64+
}
65+
(*context)->activeViews[view->viewId] = view;
5166
return APP_CONTEXT_OK;
5267
}
5368

5469
AppContextStatus freeAppContextViews(struct AppContext_t** context) {
5570
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);
71+
for(int i = 0; i < (*context)->activeViewsCount; i++) {
72+
struct View_t* view = (*context)->activeViews[i];
73+
if(view != NULL) {
74+
view_dispatcher_remove_view((*context)->view_dispatcher, view->viewId);
6075

61-
switch(view->type) {
62-
case MENU:
63-
menu_free(view->viewData);
64-
break;
65-
case POPUP:
66-
popup_free(view->viewData);
67-
break;
76+
switch(view->type) {
77+
case MENU:
78+
menu_free(view->viewData);
79+
break;
80+
case POPUP:
81+
popup_free(view->viewData);
82+
break;
83+
}
84+
free(view);
6885
}
69-
root = root->next;
7086
}
7187
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-
}
88+
free((*context)->activeViews);
89+
(*context)->activeViewsCount = 0;
7690
return APP_CONTEXT_OK;
7791
}
7892

src/app_context.h

+12-6
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,33 @@ typedef enum {
2222
APP_CONTEXT_OK = 0,
2323
APP_CONTEXT_CANT_ALLOCATE = -1,
2424
APP_CONTEXT_CANT_FREE_VIEWS = -2,
25-
APP_CONTEXT_UNKNOWN_ERROR = -3,
25+
APP_CONTEXT_NOT_ENOUGH_VIEWS = -3,
26+
APP_CONTEXT_UNKNOWN_ERROR = -4,
2627
} AppContextStatus;
2728

2829
struct AppContext_t {
2930
SceneManager* scene_manager;
3031
ViewDispatcher* view_dispatcher;
31-
struct ListNode_t* activeViews;
32+
struct View_t** activeViews;
33+
int activeViewsCount;
3234
};
3335

3436
/// @brief Creates an app context with the desired scene handlers.
3537
/// @param context The app context to populate. Will be passed through to the supplied scene handlers.
38+
/// @param viewsCount The number of views that to be added to this scene.
3639
/// @param sceneManagerHandlers The scene handlers to use for each scene in your app.
3740
/// @return Returns APP_CONTEXT_OK on success, APP_CONTEXT_CANT_ALLOCATE if there is an error.
3841
AppContextStatus initializeAppContext(
3942
struct AppContext_t** context,
43+
int viewsCount,
4044
const SceneManagerHandlers* sceneManagerHandlers);
4145

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+
/// @brief Adds a view to the given app context.
47+
/// @param context The app context to add the view to.
48+
/// @param view The view to add to the app context.
49+
/// @return Returns APP_CONTEXT_OK on success, APP_CONTEXT_NOT_ENOUGH_VIEWS if the ID of
50+
// the view exceeds the number of available views in the app context.
51+
AppContextStatus addViewToAppContext(struct AppContext_t** context, struct View_t* view);
4652

4753
/// @brief Frees the app context entirely, cleaning it up from usage.
4854
/// @param context The app context to clean up.

src/scenes/settings_scene.c

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#include <gui/modules/menu.h>
2+
#include <gui/modules/popup.h>
3+
4+
#include "settings_scene.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+
SettingsMenuOptions_WaveType,
12+
SettingsMenuOptions_Amplitude,
13+
SettingsMenuOptions_Period,
14+
} SettingsMenuOptions;
15+
16+
/** main menu callback - sends a custom event to the scene manager based on the menu selection */
17+
void menu_callback_settings_scene(void* context, uint32_t index) {
18+
UNUSED(context);
19+
// struct AppContext_t* app = context;
20+
switch(index) {
21+
case SettingsMenuOptions_WaveType:
22+
FURI_LOG_I(TAG, "selection one");
23+
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_StartPlayback);
24+
break;
25+
case SettingsMenuOptions_Amplitude:
26+
FURI_LOG_I(TAG, "selection two");
27+
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_AdjustTone);
28+
break;
29+
case SettingsMenuOptions_Period:
30+
FURI_LOG_I(TAG, "selection three");
31+
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_AdjustTone);
32+
break;
33+
}
34+
}
35+
36+
/** resets the menu, gives it content, callbacks and selection enums */
37+
void scene_on_enter_settings_scene(void* context) {
38+
FURI_LOG_I(TAG, "scene_on_enter_settings_scene");
39+
struct AppContext_t* app = (struct AppContext_t*)context;
40+
41+
// Setup our menu
42+
FURI_LOG_D(TAG, "Adding view menu");
43+
struct View_t* menuView = app->activeViews[ToneGenAppView_SharedMenu];
44+
45+
// Set the currently active view
46+
menu_reset(menuView->viewData);
47+
48+
FURI_LOG_D(TAG, "Adding menu options for settings");
49+
menu_add_item(
50+
menuView->viewData,
51+
"Wave Type",
52+
NULL,
53+
SettingsMenuOptions_WaveType,
54+
menu_callback_settings_scene,
55+
app);
56+
menu_add_item(
57+
menuView->viewData,
58+
"Amplitude",
59+
NULL,
60+
SettingsMenuOptions_Amplitude,
61+
menu_callback_settings_scene,
62+
app);
63+
menu_add_item(
64+
menuView->viewData,
65+
"Period",
66+
NULL,
67+
SettingsMenuOptions_Period,
68+
menu_callback_settings_scene,
69+
app);
70+
view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_SharedMenu);
71+
}
72+
73+
/** main menu event handler - switches scene based on the event */
74+
bool scene_on_event_settings_scene(void* context, SceneManagerEvent event) {
75+
FURI_LOG_I(TAG, "scene_on_event_settings_scene");
76+
UNUSED(context);
77+
// struct AppContext_t* app = context;
78+
bool consumed = false;
79+
switch(event.type) {
80+
case SceneManagerEventTypeCustom:
81+
// switch(event.event) {
82+
// case ToneGenAppEvent_StartPlayback:
83+
// scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
84+
// consumed = true;
85+
// break;
86+
// case ToneGenAppEvent_AdjustTone:
87+
// scene_manager_next_scene(app->scene_manager, ToneGenAppScene_AdjustTone);
88+
// consumed = true;
89+
// break;
90+
// }
91+
break;
92+
default: // eg. SceneManagerEventTypeBack, SceneManagerEventTypeTick
93+
consumed = false;
94+
break;
95+
}
96+
return consumed;
97+
}
98+
99+
void scene_on_exit_settings_scene(void* context) {
100+
FURI_LOG_I(TAG, "scene_on_exit_settings_scene");
101+
UNUSED(context);
102+
}

src/scenes/settings_scene.h

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

src/scenes/starting_scene.c

+16-26
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,14 @@ typedef enum {
1616
void menu_callback_starting_scene(void* context, uint32_t index) {
1717
FURI_LOG_I(TAG, "menu_callback_starting_scene");
1818
UNUSED(context);
19-
// struct AppContext_t* app = context;
19+
struct AppContext_t* app = context;
2020
switch(index) {
2121
case ToneGenAppMenuSelection_Play:
2222
FURI_LOG_I(TAG, "selection one");
2323
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_StartPlayback);
2424
break;
2525
case ToneGenAppMenuSelection_Adjust:
26-
FURI_LOG_I(TAG, "selection two");
27-
// scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_AdjustTone);
26+
scene_manager_handle_custom_event(app->scene_manager, ToneGenAppMenuSelection_Adjust);
2827
break;
2928
}
3029
}
@@ -33,17 +32,9 @@ void menu_callback_starting_scene(void* context, uint32_t index) {
3332
void scene_on_enter_starting_scene(void* context) {
3433
FURI_LOG_I(TAG, "scene_on_enter_starting_scene");
3534
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));
35+
struct View_t* menuView = app->activeViews[ToneGenAppView_SharedMenu];
4436

4537
// Set the currently active view
46-
addNode(&app->activeViews, menuView);
4738
menu_reset(menuView->viewData);
4839

4940
// NB. icons are specified as NULL below, because:
@@ -64,27 +55,27 @@ void scene_on_enter_starting_scene(void* context) {
6455
ToneGenAppMenuSelection_Adjust,
6556
menu_callback_starting_scene,
6657
app);
67-
view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_Menu);
58+
view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_SharedMenu);
6859
}
6960

7061
/** main menu event handler - switches scene based on the event */
7162
bool scene_on_event_starting_scene(void* context, SceneManagerEvent event) {
7263
FURI_LOG_I(TAG, "scene_on_event_starting_scene");
7364
UNUSED(context);
74-
// struct AppContext_t* app = context;
65+
struct AppContext_t* app = context;
7566
bool consumed = false;
7667
switch(event.type) {
7768
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-
// }
69+
switch(event.event) {
70+
case ToneGenAppMenuSelection_Play:
71+
// scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
72+
// consumed = true;
73+
break;
74+
case ToneGenAppMenuSelection_Adjust:
75+
scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Settings);
76+
consumed = true;
77+
break;
78+
}
8879
break;
8980
default: // eg. SceneManagerEventTypeBack, SceneManagerEventTypeTick
9081
consumed = false;
@@ -95,6 +86,5 @@ bool scene_on_event_starting_scene(void* context, SceneManagerEvent event) {
9586

9687
void scene_on_exit_starting_scene(void* context) {
9788
FURI_LOG_I(TAG, "scene_on_exit_starting_scene");
98-
struct AppContext_t* app = context;
99-
freeAppContextViews(&app);
89+
UNUSED(context);
10090
}

0 commit comments

Comments
 (0)