Просмотр исходного кода

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.
Gerald McAlister 2 лет назад
Родитель
Сommit
82ea48ed98
8 измененных файлов с 221 добавлено и 59 удалено
  1. 1 0
      README.MD
  2. 30 16
      src/app_context.c
  3. 12 6
      src/app_context.h
  4. 102 0
      src/scenes/settings_scene.c
  5. 11 0
      src/scenes/settings_scene.h
  6. 16 26
      src/scenes/starting_scene.c
  7. 39 9
      src/tone_gen.c
  8. 10 2
      src/tone_gen.h

+ 1 - 0
README.MD

@@ -11,6 +11,7 @@ This project depends on [uFBT](https://github.com/flipperdevices/flipperzero-ufb
 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:
 
 - **Starting Scene**: The scene where the application starts. This has the main menu options that users see when they start the app.
+- **Settings Scene**: The scene where users can configure the tone's properties.
 
 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.
 

+ 30 - 16
src/app_context.c

@@ -20,6 +20,7 @@ bool viewDispatcherNavigationCallback(void* context) {
 
 AppContextStatus initializeAppContext(
     struct AppContext_t** context,
+    int viewsCount,
     const SceneManagerHandlers* sceneManagerHandlers) {
     FURI_LOG_I(TAG, "Allocating memory for app context");
 
@@ -48,31 +49,44 @@ AppContextStatus initializeAppContext(
         (*context)->view_dispatcher, viewDispatcherNavigationCallback);
     FURI_LOG_I(TAG, "Setting view dispatcher callbacks done");
 
+    (*context)->activeViews = malloc(sizeof(struct View_t) * viewsCount);
+    if((*context)->activeViews == NULL) {
+        return APP_CONTEXT_CANT_ALLOCATE;
+    }
+    (*context)->activeViewsCount = viewsCount;
+
+    return APP_CONTEXT_OK;
+}
+
+AppContextStatus addViewToAppContext(struct AppContext_t** context, struct View_t* view) {
+    if(view->viewId > (*context)->activeViewsCount || view->viewId < 0) {
+        return APP_CONTEXT_NOT_ENOUGH_VIEWS;
+    }
+    (*context)->activeViews[view->viewId] = view;
     return APP_CONTEXT_OK;
 }
 
 AppContextStatus freeAppContextViews(struct AppContext_t** context) {
     FURI_LOG_I(TAG, "Freeing views");
-    struct ListNode_t* root = (*context)->activeViews;
-    while(root) {
-        struct View_t* view = root->data;
-        view_dispatcher_remove_view((*context)->view_dispatcher, view->viewId);
+    for(int i = 0; i < (*context)->activeViewsCount; i++) {
+        struct View_t* view = (*context)->activeViews[i];
+        if(view != NULL) {
+            view_dispatcher_remove_view((*context)->view_dispatcher, view->viewId);
 
-        switch(view->type) {
-        case MENU:
-            menu_free(view->viewData);
-            break;
-        case POPUP:
-            popup_free(view->viewData);
-            break;
+            switch(view->type) {
+            case MENU:
+                menu_free(view->viewData);
+                break;
+            case POPUP:
+                popup_free(view->viewData);
+                break;
+            }
+            free(view);
         }
-        root = root->next;
     }
     FURI_LOG_I(TAG, "Removing all views from list");
-    LinkedListStatus result = removeAllNodes(&(*context)->activeViews);
-    if(result != LIST_OK) {
-        return APP_CONTEXT_UNKNOWN_ERROR;
-    }
+    free((*context)->activeViews);
+    (*context)->activeViewsCount = 0;
     return APP_CONTEXT_OK;
 }
 

+ 12 - 6
src/app_context.h

@@ -22,27 +22,33 @@ typedef enum {
     APP_CONTEXT_OK = 0,
     APP_CONTEXT_CANT_ALLOCATE = -1,
     APP_CONTEXT_CANT_FREE_VIEWS = -2,
-    APP_CONTEXT_UNKNOWN_ERROR = -3,
+    APP_CONTEXT_NOT_ENOUGH_VIEWS = -3,
+    APP_CONTEXT_UNKNOWN_ERROR = -4,
 } AppContextStatus;
 
 struct AppContext_t {
     SceneManager* scene_manager;
     ViewDispatcher* view_dispatcher;
-    struct ListNode_t* activeViews;
+    struct View_t** activeViews;
+    int activeViewsCount;
 };
 
 /// @brief Creates an app context with the desired scene handlers.
 /// @param context The app context to populate. Will be passed through to the supplied scene handlers.
+/// @param viewsCount The number of views that to be added to this scene.
 /// @param sceneManagerHandlers The scene handlers to use for each scene in your app.
 /// @return Returns APP_CONTEXT_OK on success, APP_CONTEXT_CANT_ALLOCATE if there is an error.
 AppContextStatus initializeAppContext(
     struct AppContext_t** context,
+    int viewsCount,
     const SceneManagerHandlers* sceneManagerHandlers);
 
-/// @brief Frees and removes all views attached to the app context.
-/// @param context The app context to remove all of the views from.
-/// @return Returns APP_CONTEXT_OK on success. Should not error.
-AppContextStatus freeAppContextViews(struct AppContext_t** context);
+/// @brief Adds a view to the given app context.
+/// @param context The app context to add the view to.
+/// @param view The view to add to the app context.
+/// @return Returns APP_CONTEXT_OK on success, APP_CONTEXT_NOT_ENOUGH_VIEWS if the ID of
+//  the view exceeds the number of available views in the app context.
+AppContextStatus addViewToAppContext(struct AppContext_t** context, struct View_t* view);
 
 /// @brief Frees the app context entirely, cleaning it up from usage.
 /// @param context The app context to clean up.

+ 102 - 0
src/scenes/settings_scene.c

@@ -0,0 +1,102 @@
+#include <gui/modules/menu.h>
+#include <gui/modules/popup.h>
+
+#include "settings_scene.h"
+#include "../app_context.h"
+#include "../tone_gen.h"
+#include "../utils/linked_list.h"
+
+/** indices for menu items */
+typedef enum {
+    SettingsMenuOptions_WaveType,
+    SettingsMenuOptions_Amplitude,
+    SettingsMenuOptions_Period,
+} SettingsMenuOptions;
+
+/** main menu callback - sends a custom event to the scene manager based on the menu selection */
+void menu_callback_settings_scene(void* context, uint32_t index) {
+    UNUSED(context);
+    // struct AppContext_t* app = context;
+    switch(index) {
+    case SettingsMenuOptions_WaveType:
+        FURI_LOG_I(TAG, "selection one");
+        // scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_StartPlayback);
+        break;
+    case SettingsMenuOptions_Amplitude:
+        FURI_LOG_I(TAG, "selection two");
+        // scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_AdjustTone);
+        break;
+    case SettingsMenuOptions_Period:
+        FURI_LOG_I(TAG, "selection three");
+        // scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_AdjustTone);
+        break;
+    }
+}
+
+/** resets the menu, gives it content, callbacks and selection enums */
+void scene_on_enter_settings_scene(void* context) {
+    FURI_LOG_I(TAG, "scene_on_enter_settings_scene");
+    struct AppContext_t* app = (struct AppContext_t*)context;
+
+    // Setup our menu
+    FURI_LOG_D(TAG, "Adding view menu");
+    struct View_t* menuView = app->activeViews[ToneGenAppView_SharedMenu];
+
+    // Set the currently active view
+    menu_reset(menuView->viewData);
+
+    FURI_LOG_D(TAG, "Adding menu options for settings");
+    menu_add_item(
+        menuView->viewData,
+        "Wave Type",
+        NULL,
+        SettingsMenuOptions_WaveType,
+        menu_callback_settings_scene,
+        app);
+    menu_add_item(
+        menuView->viewData,
+        "Amplitude",
+        NULL,
+        SettingsMenuOptions_Amplitude,
+        menu_callback_settings_scene,
+        app);
+    menu_add_item(
+        menuView->viewData,
+        "Period",
+        NULL,
+        SettingsMenuOptions_Period,
+        menu_callback_settings_scene,
+        app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_SharedMenu);
+}
+
+/** main menu event handler - switches scene based on the event */
+bool scene_on_event_settings_scene(void* context, SceneManagerEvent event) {
+    FURI_LOG_I(TAG, "scene_on_event_settings_scene");
+    UNUSED(context);
+    // struct AppContext_t* app = context;
+    bool consumed = false;
+    switch(event.type) {
+    case SceneManagerEventTypeCustom:
+        // switch(event.event) {
+        // case ToneGenAppEvent_StartPlayback:
+        //     scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
+        //     consumed = true;
+        //     break;
+        // case ToneGenAppEvent_AdjustTone:
+        //     scene_manager_next_scene(app->scene_manager, ToneGenAppScene_AdjustTone);
+        //     consumed = true;
+        //     break;
+        // }
+        break;
+    default: // eg. SceneManagerEventTypeBack, SceneManagerEventTypeTick
+        consumed = false;
+        break;
+    }
+    return consumed;
+}
+
+void scene_on_exit_settings_scene(void* context) {
+    FURI_LOG_I(TAG, "scene_on_exit_settings_scene");
+    UNUSED(context);
+}

+ 11 - 0
src/scenes/settings_scene.h

@@ -0,0 +1,11 @@
+#ifndef _SETTINGS_SCENE_H_
+
+#define _SETTINGS_SCENE_H_
+
+#include <gui/scene_manager.h>
+
+void scene_on_enter_settings_scene(void* context);
+bool scene_on_event_settings_scene(void* context, SceneManagerEvent event);
+void scene_on_exit_settings_scene(void* context);
+
+#endif

+ 16 - 26
src/scenes/starting_scene.c

@@ -16,15 +16,14 @@ typedef enum {
 void menu_callback_starting_scene(void* context, uint32_t index) {
     FURI_LOG_I(TAG, "menu_callback_starting_scene");
     UNUSED(context);
-    // struct AppContext_t* app = context;
+    struct AppContext_t* app = context;
     switch(index) {
     case ToneGenAppMenuSelection_Play:
         FURI_LOG_I(TAG, "selection one");
         // scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_StartPlayback);
         break;
     case ToneGenAppMenuSelection_Adjust:
-        FURI_LOG_I(TAG, "selection two");
-        // scene_manager_handle_custom_event(app->scene_manager, ToneGenAppEvent_AdjustTone);
+        scene_manager_handle_custom_event(app->scene_manager, ToneGenAppMenuSelection_Adjust);
         break;
     }
 }
@@ -33,17 +32,9 @@ void menu_callback_starting_scene(void* context, uint32_t index) {
 void scene_on_enter_starting_scene(void* context) {
     FURI_LOG_I(TAG, "scene_on_enter_starting_scene");
     struct AppContext_t* app = (struct AppContext_t*)context;
-    // Setup our menu
-    FURI_LOG_D(TAG, "Adding view menu");
-    struct View_t* menuView = malloc(sizeof(struct View_t));
-    menuView->viewData = menu_alloc();
-    menuView->viewId = ToneGenAppView_Menu;
-    menuView->type = MENU;
-    view_dispatcher_add_view(
-        app->view_dispatcher, ToneGenAppView_Menu, menu_get_view(menuView->viewData));
+    struct View_t* menuView = app->activeViews[ToneGenAppView_SharedMenu];
 
     // Set the currently active view
-    addNode(&app->activeViews, menuView);
     menu_reset(menuView->viewData);
 
     // NB. icons are specified as NULL below, because:
@@ -64,27 +55,27 @@ void scene_on_enter_starting_scene(void* context) {
         ToneGenAppMenuSelection_Adjust,
         menu_callback_starting_scene,
         app);
-    view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_Menu);
+    view_dispatcher_switch_to_view(app->view_dispatcher, ToneGenAppView_SharedMenu);
 }
 
 /** main menu event handler - switches scene based on the event */
 bool scene_on_event_starting_scene(void* context, SceneManagerEvent event) {
     FURI_LOG_I(TAG, "scene_on_event_starting_scene");
     UNUSED(context);
-    // struct AppContext_t* app = context;
+    struct AppContext_t* app = context;
     bool consumed = false;
     switch(event.type) {
     case SceneManagerEventTypeCustom:
-        // switch(event.event) {
-        // case ToneGenAppEvent_StartPlayback:
-        //     scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
-        //     consumed = true;
-        //     break;
-        // case ToneGenAppEvent_AdjustTone:
-        //     scene_manager_next_scene(app->scene_manager, ToneGenAppScene_AdjustTone);
-        //     consumed = true;
-        //     break;
-        // }
+        switch(event.event) {
+        case ToneGenAppMenuSelection_Play:
+            // scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Playback);
+            // consumed = true;
+            break;
+        case ToneGenAppMenuSelection_Adjust:
+            scene_manager_next_scene(app->scene_manager, ToneGenAppScene_Settings);
+            consumed = true;
+            break;
+        }
         break;
     default: // eg. SceneManagerEventTypeBack, SceneManagerEventTypeTick
         consumed = false;
@@ -95,6 +86,5 @@ bool scene_on_event_starting_scene(void* context, SceneManagerEvent event) {
 
 void scene_on_exit_starting_scene(void* context) {
     FURI_LOG_I(TAG, "scene_on_exit_starting_scene");
-    struct AppContext_t* app = context;
-    freeAppContextViews(&app);
+    UNUSED(context);
 }

+ 39 - 9
src/tone_gen.c

@@ -7,20 +7,24 @@
 #include "tone_gen.h"
 
 #include "scenes/starting_scene.h"
+#include "scenes/settings_scene.h"
 
 /** collection of all scene on_enter handlers - in the same order as their enum */
 void (*const scene_on_enter_handlers[])(void*) = {
     scene_on_enter_starting_scene,
+    scene_on_enter_settings_scene,
 };
 
 /** collection of all scene on event handlers - in the same order as their enum */
 bool (*const scene_on_event_handlers[])(void*, SceneManagerEvent) = {
     scene_on_event_starting_scene,
+    scene_on_event_settings_scene,
 };
 
 /** collection of all scene on exit handlers - in the same order as their enum */
 void (*const scene_on_exit_handlers[])(void*) = {
     scene_on_exit_starting_scene,
+    scene_on_exit_settings_scene,
 };
 
 const SceneManagerHandlers scene_event_handlers = {
@@ -29,23 +33,49 @@ const SceneManagerHandlers scene_event_handlers = {
     .on_exit_handlers = scene_on_exit_handlers,
     .scene_num = ToneGenAppScene_count};
 
+int setupViews(struct AppContext_t** appContext) {
+    // Create views
+    struct View_t* sharedMenuView = malloc(sizeof(struct View_t));
+    sharedMenuView->viewData = menu_alloc();
+    sharedMenuView->viewId = ToneGenAppView_SharedMenu;
+    sharedMenuView->type = MENU;
+
+    // Add views to the app context for management later
+    AppContextStatus result = addViewToAppContext(appContext, sharedMenuView);
+    if(result != APP_CONTEXT_OK) {
+        FURI_LOG_E(TAG, "There was a problem adding the view %d!", sharedMenuView->viewId);
+        return -1;
+    }
+
+    // Add views to the view dispatcher for usage later
+    view_dispatcher_add_view(
+        (*appContext)->view_dispatcher,
+        sharedMenuView->viewId,
+        menu_get_view(sharedMenuView->viewData));
+    return 0;
+}
+
 int32_t tone_gen_app(void* p) {
     UNUSED(p);
 
     FURI_LOG_I(TAG, "Tone gen app starting...");
 
     struct AppContext_t* appContext;
-    AppContextStatus result = initializeAppContext(&appContext, &scene_event_handlers);
+    AppContextStatus result =
+        initializeAppContext(&appContext, ToneGenAppView_count, &scene_event_handlers);
 
     if(result == APP_CONTEXT_OK) {
-        // set the scene and launch the main loop
-        FURI_LOG_D(TAG, "Setting the scene");
-        Gui* gui = furi_record_open(RECORD_GUI);
-        view_dispatcher_attach_to_gui(
-            appContext->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
-        scene_manager_next_scene(appContext->scene_manager, ToneGenAppScene_MainMenu);
-        FURI_LOG_D(TAG, "Starting the view dispatcher");
-        view_dispatcher_run(appContext->view_dispatcher);
+        result = setupViews(&appContext);
+        if(result == 0) {
+            // set the scene and launch the main loop
+            FURI_LOG_D(TAG, "Setting the scene");
+            Gui* gui = furi_record_open(RECORD_GUI);
+            view_dispatcher_attach_to_gui(
+                appContext->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
+            scene_manager_next_scene(appContext->scene_manager, ToneGenAppScene_Starting);
+            FURI_LOG_D(TAG, "Starting the view dispatcher");
+            view_dispatcher_run(appContext->view_dispatcher);
+        }
 
         // free all memory
         FURI_LOG_D(TAG, "Ending the app");

+ 10 - 2
src/tone_gen.h

@@ -8,9 +8,17 @@
 #include <music_worker/music_worker.h>
 
 // ids for all scenes used by the app
-typedef enum { ToneGenAppScene_MainMenu, ToneGenAppScene_count } ToneGenAppScene;
+typedef enum {
+    ToneGenAppScene_Starting,
+    ToneGenAppScene_Settings,
+    ToneGenAppScene_count
+} ToneGenAppScene;
 
 // ids for the 2 types of view used by the app
-typedef enum { ToneGenAppView_Menu, ToneGenAppView_Popup } ToneGenAppView;
+typedef enum {
+    ToneGenAppView_SharedMenu,
+    ToneGenAppView_Popup,
+    ToneGenAppView_count
+} ToneGenAppView;
 
 #endif