[FL-1217] Menu refactoring (#726)
* menu: remove dead code * loader: change views from modules instead of menu service * dolphin: start main menu with loader API * applications: don't start menu service * loader: add debug tools menu * gui modules: introduce menu model * loader: remove calls to menu service API * gui modules: implement menu module * loader: add menu view * gui menu: add animation * applications: remove menu service * gui modules: rename icon_menu -> menu * loader: clean up code * menu module: add documentation, format code * menu: remove unused parameter * desktop: use loader to launch primary menu * Applications: cleaner makefile app declaration. Loader: application autostart * Gui: cleanup menu and submenu API. Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
1c4e6ec74d
commit
61c8f3325a
@ -9,7 +9,6 @@ extern int32_t dolphin_srv(void* p);
|
||||
extern int32_t gui_srv(void* p);
|
||||
extern int32_t input_srv(void* p);
|
||||
extern int32_t loader_srv(void* p);
|
||||
extern int32_t menu_srv(void* p);
|
||||
extern int32_t notification_srv(void* p);
|
||||
extern int32_t power_observer_srv(void* p);
|
||||
extern int32_t power_srv(void* p);
|
||||
@ -87,8 +86,7 @@ const FlipperApplication FLIPPER_SERVICES[] = {
|
||||
{.app = input_srv, .name = "Input", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_MENU
|
||||
{.app = menu_srv, .name = "Menu", .stack_size = 1024, .icon = NULL},
|
||||
#ifdef SRV_LOADER
|
||||
{.app = loader_srv, .name = "Loader", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
@ -107,43 +105,6 @@ const FlipperApplication FLIPPER_SERVICES[] = {
|
||||
#ifdef SRV_STORAGE
|
||||
{.app = storage_srv, .name = "Storage", .stack_size = 4096, .icon = NULL},
|
||||
#endif
|
||||
|
||||
/* Fake services (autorun) */
|
||||
#ifdef SRV_BLINK
|
||||
{.app = blink_test_app, .name = "Blink", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_LF_RFID
|
||||
{.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_IRDA
|
||||
{.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_MUSIC_PLAYER
|
||||
{.app = music_player_app, .name = "Music Player", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_IBUTTON
|
||||
{.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_GPIO_TEST
|
||||
{.app = gpio_test_app, .name = "GPIO Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_KEYPAD_TEST
|
||||
{.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_ACCESSOR
|
||||
{.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_STORAGE_TEST
|
||||
{.app = storage_test_app, .name = "Storage Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
};
|
||||
|
||||
const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
|
||||
@ -184,25 +145,35 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = {
|
||||
#ifdef SRV_CLI
|
||||
crypto_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_IRDA
|
||||
irda_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_NFC
|
||||
nfc_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_SUBGHZ
|
||||
subghz_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_LF_RFID
|
||||
lfrfid_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef APP_IBUTTON
|
||||
ibutton_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef SRV_BT
|
||||
bt_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef SRV_POWER
|
||||
power_cli_init,
|
||||
#endif
|
||||
|
||||
#ifdef SRV_STORAGE
|
||||
storage_cli_init,
|
||||
#endif
|
||||
|
@ -1,278 +1,248 @@
|
||||
APP_DIR = $(PROJECT_ROOT)/applications
|
||||
LIB_DIR = $(PROJECT_ROOT)/lib
|
||||
LIB_DIR = $(PROJECT_ROOT)/lib
|
||||
|
||||
CFLAGS += -I$(APP_DIR)
|
||||
C_SOURCES += $(shell find $(APP_DIR) -name *.c)
|
||||
CPP_SOURCES += $(shell find $(APP_DIR) -name *.cpp)
|
||||
C_SOURCES += $(shell find $(APP_DIR) -name *.c)
|
||||
CPP_SOURCES += $(shell find $(APP_DIR) -name *.cpp)
|
||||
|
||||
|
||||
# Use SRV_* for autostart app
|
||||
# Use APP_* for add app to build
|
||||
|
||||
APP_RELEASE ?= 1
|
||||
ifeq ($(APP_RELEASE), 1)
|
||||
# Services
|
||||
SRV_BT = 1
|
||||
SRV_CLI = 1
|
||||
SRV_DIALOGS = 1
|
||||
SRV_DOLPHIN = 1
|
||||
SRV_GUI = 1
|
||||
SRV_INPUT = 1
|
||||
SRV_MENU = 1
|
||||
SRV_BT = 1
|
||||
SRV_CLI = 1
|
||||
SRV_DIALOGS = 1
|
||||
SRV_DOLPHIN = 1
|
||||
SRV_GUI = 1
|
||||
SRV_INPUT = 1
|
||||
SRV_LOADER = 1
|
||||
SRV_NOTIFICATION = 1
|
||||
SRV_POWER = 1
|
||||
SRV_POWER = 1
|
||||
SRV_POWER_OBSERVER = 1
|
||||
SRV_STORAGE = 1
|
||||
SRV_STORAGE = 1
|
||||
|
||||
# Apps
|
||||
SRV_DESKTOP = 1
|
||||
APP_ARCHIVE = 1
|
||||
SRV_DESKTOP = 1
|
||||
APP_ARCHIVE = 1
|
||||
APP_GPIO_TEST = 1
|
||||
APP_IBUTTON = 1
|
||||
APP_IRDA = 1
|
||||
APP_LF_RFID = 1
|
||||
APP_NFC = 1
|
||||
APP_SUBGHZ = 1
|
||||
APP_ABOUT = 1
|
||||
APP_IBUTTON = 1
|
||||
APP_IRDA = 1
|
||||
APP_LF_RFID = 1
|
||||
APP_NFC = 1
|
||||
APP_SUBGHZ = 1
|
||||
APP_ABOUT = 1
|
||||
|
||||
# Plugins
|
||||
APP_MUSIC_PLAYER = 1
|
||||
|
||||
# Debug
|
||||
APP_ACCESSOR = 1
|
||||
APP_BLINK = 1
|
||||
APP_BLINK = 1
|
||||
APP_IRDA_MONITOR = 1
|
||||
APP_KEYPAD_TEST = 1
|
||||
APP_SD_TEST = 1
|
||||
APP_SD_TEST = 1
|
||||
APP_UNIT_TESTS = 0
|
||||
APP_VIBRO_DEMO = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_BT ?= 0
|
||||
ifeq ($(SRV_BT), 1)
|
||||
SRV_CLI = 1
|
||||
CFLAGS += -DSRV_BT
|
||||
endif
|
||||
# Applications
|
||||
# that will be shown in menu
|
||||
# Prefix with APP_*
|
||||
|
||||
SRV_DOLPHIN ?= 0
|
||||
ifeq ($(SRV_DOLPHIN), 1)
|
||||
SRV_MENU = 1
|
||||
CFLAGS += -DSRV_DOLPHIN
|
||||
endif
|
||||
|
||||
SRV_POWER ?= 0
|
||||
ifeq ($(SRV_POWER), 1)
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
CFLAGS += -DSRV_POWER
|
||||
endif
|
||||
|
||||
SRV_POWER_OBSERVER ?= 0
|
||||
ifeq ($(SRV_POWER_OBSERVER), 1)
|
||||
SRV_POWER = 1
|
||||
CFLAGS += -DSRV_POWER_OBSERVER
|
||||
endif
|
||||
|
||||
SRV_MENU ?= 0
|
||||
ifeq ($(SRV_MENU), 1)
|
||||
CFLAGS += -DSRV_MENU
|
||||
APP_MENU = 1
|
||||
endif
|
||||
APP_MENU ?= 0
|
||||
ifeq ($(APP_MENU), 1)
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
CFLAGS += -DAPP_MENU
|
||||
endif
|
||||
|
||||
APP_IRDA_MONITOR ?= 0
|
||||
ifeq ($(APP_IRDA_MONITOR), 1)
|
||||
CFLAGS += -DAPP_IRDA_MONITOR
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_UNIT_TESTS ?= 0
|
||||
ifeq ($(APP_UNIT_TESTS), 1)
|
||||
CFLAGS += -DAPP_UNIT_TESTS
|
||||
endif
|
||||
|
||||
SRV_DESKTOP ?= 0
|
||||
ifeq ($(SRV_DESKTOP), 1)
|
||||
CFLAGS += -DSRV_DESKTOP
|
||||
SRV_DESKTOP = 1
|
||||
endif
|
||||
|
||||
APP_ARCHIVE ?= 0
|
||||
ifeq ($(APP_NFC), 1)
|
||||
ifeq ($(APP_ARCHIVE), 1)
|
||||
CFLAGS += -DAPP_ARCHIVE
|
||||
APP_ARCHIVE = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_BLINK ?= 0
|
||||
ifeq ($(SRV_BLINK), 1)
|
||||
CFLAGS += -DSRV_BLINK
|
||||
APP_BLINK = 1
|
||||
endif
|
||||
|
||||
APP_BLINK ?= 0
|
||||
ifeq ($(APP_BLINK), 1)
|
||||
CFLAGS += -DAPP_BLINK
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_UART_WRITE ?= 0
|
||||
ifeq ($(SRV_UART_WRITE), 1)
|
||||
CFLAGS += -DSRV_UART_WRITE
|
||||
APP_UART_WRITE = 1
|
||||
endif
|
||||
APP_UART_WRITE ?= 0
|
||||
ifeq ($(APP_UART_WRITE), 1)
|
||||
CFLAGS += -DAPP_UART_WRITE
|
||||
endif
|
||||
|
||||
SRV_IPC ?= 0
|
||||
ifeq ($(SRV_IPC), 1)
|
||||
CFLAGS += -DSRV_IPC
|
||||
APP_IPC = 1
|
||||
endif
|
||||
APP_IPC ?= 0
|
||||
ifeq ($(APP_IPC), 1)
|
||||
CFLAGS += -DAPP_IPC
|
||||
endif
|
||||
|
||||
APP_SUBGHZ ?= 0
|
||||
ifeq ($(APP_SUBGHZ), 1)
|
||||
CFLAGS += -DAPP_SUBGHZ
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_ABOUT ?= 0
|
||||
ifeq ($(APP_ABOUT), 1)
|
||||
CFLAGS += -DAPP_ABOUT
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_LF_RFID ?= 0
|
||||
ifeq ($(SRV_LF_RFID), 1)
|
||||
CFLAGS += -DSRV_LF_RFID
|
||||
APP_LF_RFID = 1
|
||||
endif
|
||||
|
||||
APP_LF_RFID ?= 0
|
||||
ifeq ($(APP_LF_RFID), 1)
|
||||
CFLAGS += -DAPP_LF_RFID
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_NFC ?= 0
|
||||
ifeq ($(APP_NFC), 1)
|
||||
CFLAGS += -DAPP_NFC
|
||||
SRV_MENU = 1
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_IRDA ?= 0
|
||||
ifeq ($(SRV_IRDA), 1)
|
||||
CFLAGS += -DSRV_IRDA
|
||||
APP_IRDA = 1
|
||||
endif
|
||||
|
||||
APP_IRDA ?= 0
|
||||
ifeq ($(APP_IRDA), 1)
|
||||
CFLAGS += -DAPP_IRDA
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
APP_VIBRO_DEMO ?= 0
|
||||
ifeq ($(APP_VIBRO_DEMO), 1)
|
||||
CFLAGS += -DAPP_VIBRO_DEMO
|
||||
SRV_INPUT = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_KEYPAD_TEST ?= 0
|
||||
ifeq ($(SRV_KEYPAD_TEST), 1)
|
||||
CFLAGS += -DSRV_KEYPAD_TEST
|
||||
APP_KEYPAD_TEST = 1
|
||||
endif
|
||||
|
||||
APP_KEYPAD_TEST ?= 0
|
||||
ifeq ($(APP_KEYPAD_TEST), 1)
|
||||
CFLAGS += -DAPP_KEYPAD_TEST
|
||||
APP_KEYPAD_TEST = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_ACCESSOR ?= 0
|
||||
ifeq ($(SRV_ACCESSOR), 1)
|
||||
CFLAGS += -DSRV_ACCESSOR
|
||||
APP_ACCESSOR = 1
|
||||
endif
|
||||
|
||||
APP_ACCESSOR ?= 0
|
||||
ifeq ($(APP_ACCESSOR), 1)
|
||||
CFLAGS += -DAPP_ACCESSOR
|
||||
APP_ACCESSOR = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_GPIO_TEST ?= 0
|
||||
ifeq ($(SRV_GPIO_TEST), 1)
|
||||
CFLAGS += -DSRV_GPIO_TEST
|
||||
APP_GPIO_TEST = 1
|
||||
endif
|
||||
|
||||
APP_GPIO_TEST ?= 0
|
||||
ifeq ($(APP_GPIO_TEST), 1)
|
||||
CFLAGS += -DAPP_GPIO_TEST
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_MUSIC_PLAYER ?= 0
|
||||
ifeq ($(SRV_MUSIC_PLAYER), 1)
|
||||
CFLAGS += -DSRV_MUSIC_PLAYER
|
||||
APP_MUSIC_PLAYER = 1
|
||||
endif
|
||||
|
||||
APP_MUSIC_PLAYER ?= 0
|
||||
ifeq ($(APP_MUSIC_PLAYER), 1)
|
||||
CFLAGS += -DAPP_MUSIC_PLAYER
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
SRV_IBUTTON ?= 0
|
||||
ifeq ($(SRV_IBUTTON), 1)
|
||||
CFLAGS += -DSRV_IBUTTON
|
||||
APP_IBUTTON = 1
|
||||
endif
|
||||
|
||||
APP_IBUTTON ?= 0
|
||||
ifeq ($(APP_IBUTTON), 1)
|
||||
CFLAGS += -DAPP_IBUTTON
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
# Services
|
||||
# that will start with OS
|
||||
# Prefix with SRV_*
|
||||
|
||||
|
||||
SRV_BT ?= 0
|
||||
ifeq ($(SRV_BT), 1)
|
||||
CFLAGS += -DSRV_BT
|
||||
SRV_CLI = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_DESKTOP ?= 0
|
||||
ifeq ($(SRV_DESKTOP), 1)
|
||||
CFLAGS += -DSRV_DESKTOP
|
||||
SRV_LOADER = 1
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_DOLPHIN ?= 0
|
||||
ifeq ($(SRV_DOLPHIN), 1)
|
||||
CFLAGS += -DSRV_DOLPHIN
|
||||
endif
|
||||
|
||||
|
||||
SRV_POWER_OBSERVER ?= 0
|
||||
ifeq ($(SRV_POWER_OBSERVER), 1)
|
||||
CFLAGS += -DSRV_POWER_OBSERVER
|
||||
SRV_POWER = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_POWER ?= 0
|
||||
ifeq ($(SRV_POWER), 1)
|
||||
CFLAGS += -DSRV_POWER
|
||||
SRV_GUI = 1
|
||||
SRV_CLI = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_LOADER ?= 0
|
||||
ifeq ($(SRV_LOADER), 1)
|
||||
CFLAGS += -DSRV_LOADER
|
||||
SRV_GUI = 1
|
||||
# Loader autostart hook
|
||||
LOADER_AUTOSTART ?= ""
|
||||
ifneq ($(strip $(LOADER_AUTOSTART)),)
|
||||
CFLAGS += -DLOADER_AUTOSTART="\"$(LOADER_AUTOSTART)\""
|
||||
endif
|
||||
# Loader autostart hook END
|
||||
endif
|
||||
|
||||
|
||||
SRV_DIALOGS ?= 0
|
||||
ifeq ($(SRV_DIALOGS), 1)
|
||||
CFLAGS += -DSRV_DIALOGS
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
#
|
||||
# Essential services
|
||||
#
|
||||
|
||||
SRV_GUI ?= 0
|
||||
ifeq ($(SRV_GUI), 1)
|
||||
CFLAGS += -DSRV_GUI
|
||||
SRV_INPUT = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_INPUT ?= 0
|
||||
ifeq ($(SRV_INPUT), 1)
|
||||
CFLAGS += -DSRV_INPUT
|
||||
endif
|
||||
|
||||
|
||||
SRV_CLI ?= 0
|
||||
ifeq ($(SRV_CLI), 1)
|
||||
SRV_GUI = 1
|
||||
CFLAGS += -DSRV_CLI
|
||||
endif
|
||||
|
||||
|
||||
SRV_NOTIFICATION ?= 0
|
||||
ifeq ($(SRV_NOTIFICATION), 1)
|
||||
CFLAGS += -DSRV_NOTIFICATION
|
||||
endif
|
||||
|
||||
|
||||
SRV_STORAGE ?= 0
|
||||
ifeq ($(SRV_STORAGE), 1)
|
||||
CFLAGS += -DSRV_STORAGE
|
||||
endif
|
||||
|
||||
SRV_DIALOGS ?= 0
|
||||
ifeq ($(SRV_DIALOGS), 1)
|
||||
CFLAGS += -DSRV_DIALOGS
|
||||
endif
|
||||
|
@ -21,7 +21,6 @@ bool desktop_back_event_callback(void* context) {
|
||||
Desktop* desktop_alloc() {
|
||||
Desktop* desktop = furi_alloc(sizeof(Desktop));
|
||||
|
||||
desktop->menu_vm = furi_record_open("menu");
|
||||
desktop->gui = furi_record_open("gui");
|
||||
desktop->scene_thread = furi_thread_alloc();
|
||||
desktop->view_dispatcher = view_dispatcher_alloc();
|
||||
@ -101,7 +100,6 @@ void desktop_free(Desktop* desktop) {
|
||||
furi_thread_free(desktop->scene_thread);
|
||||
|
||||
furi_record_close("menu");
|
||||
desktop->menu_vm = NULL;
|
||||
|
||||
free(desktop);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
#include <menu/menu.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
@ -34,8 +33,6 @@ typedef enum {
|
||||
} DesktopViewEnum;
|
||||
|
||||
struct Desktop {
|
||||
// Menu
|
||||
ValueMutex* menu_vm;
|
||||
// Scene
|
||||
FuriThread* scene_thread;
|
||||
// GUI
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_main.h"
|
||||
#include "applications.h"
|
||||
#include <loader/loader.h>
|
||||
#define MAIN_VIEW_DEFAULT (0UL)
|
||||
|
||||
static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) {
|
||||
@ -48,8 +49,7 @@ const bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DesktopMainEventOpenMenu:
|
||||
with_value_mutex(
|
||||
desktop->menu_vm, (Menu * menu) { menu_ok(menu); });
|
||||
loader_show_menu();
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
|
199
applications/gui/modules/menu.c
Executable file
199
applications/gui/modules/menu.c
Executable file
@ -0,0 +1,199 @@
|
||||
#include "menu.h"
|
||||
|
||||
#include <m-array.h>
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
|
||||
struct Menu {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* label;
|
||||
IconAnimation* icon;
|
||||
uint32_t index;
|
||||
MenuItemCallback callback;
|
||||
void* callback_context;
|
||||
} MenuItem;
|
||||
|
||||
ARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST);
|
||||
|
||||
typedef struct {
|
||||
MenuItemArray_t items;
|
||||
uint8_t position;
|
||||
} MenuModel;
|
||||
|
||||
static void menu_process_up(Menu* menu);
|
||||
static void menu_process_down(Menu* menu);
|
||||
static void menu_process_ok(Menu* menu);
|
||||
|
||||
static void menu_draw_callback(Canvas* canvas, void* _model) {
|
||||
MenuModel* model = _model;
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
uint8_t position = model->position;
|
||||
size_t items_count = MenuItemArray_size(model->items);
|
||||
if(items_count) {
|
||||
MenuItem* item;
|
||||
size_t shift_position;
|
||||
// First line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (0 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 3, item->icon);
|
||||
icon_animation_stop(item->icon);
|
||||
}
|
||||
canvas_draw_str(canvas, 22, 14, item->label);
|
||||
// Second line main
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
shift_position = (1 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 25, item->icon);
|
||||
icon_animation_start(item->icon);
|
||||
}
|
||||
canvas_draw_str(canvas, 22, 36, item->label);
|
||||
// Third line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (2 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 47, item->icon);
|
||||
icon_animation_stop(item->icon);
|
||||
}
|
||||
canvas_draw_str(canvas, 22, 58, item->label);
|
||||
// Frame and scrollbar
|
||||
elements_frame(canvas, 0, 21, 128 - 5, 21);
|
||||
elements_scrollbar(canvas, position, items_count);
|
||||
} else {
|
||||
canvas_draw_str(canvas, 2, 32, "Empty");
|
||||
elements_scrollbar(canvas, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool menu_input_callback(InputEvent* event, void* context) {
|
||||
Menu* menu = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyUp) {
|
||||
consumed = true;
|
||||
menu_process_up(menu);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
consumed = true;
|
||||
menu_process_down(menu);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
consumed = true;
|
||||
menu_process_ok(menu);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
Menu* menu_alloc() {
|
||||
Menu* menu = furi_alloc(sizeof(Menu));
|
||||
menu->view = view_alloc(menu->view);
|
||||
view_set_context(menu->view, menu);
|
||||
view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel));
|
||||
view_set_draw_callback(menu->view, menu_draw_callback);
|
||||
view_set_input_callback(menu->view, menu_input_callback);
|
||||
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
MenuItemArray_init(model->items);
|
||||
model->position = 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
void menu_free(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
MenuItemArray_clear(model->items);
|
||||
return true;
|
||||
});
|
||||
view_free(menu->view);
|
||||
free(menu);
|
||||
}
|
||||
|
||||
View* menu_get_view(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
return (menu->view);
|
||||
}
|
||||
|
||||
void menu_add_item(
|
||||
Menu* menu,
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
uint32_t index,
|
||||
MenuItemCallback callback,
|
||||
void* context) {
|
||||
furi_assert(menu);
|
||||
furi_assert(label);
|
||||
|
||||
MenuItem* item = NULL;
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
item = MenuItemArray_push_new(model->items);
|
||||
item->label = label;
|
||||
item->icon = icon;
|
||||
item->index = index;
|
||||
item->callback = callback;
|
||||
item->callback_context = context;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void menu_clean(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
MenuItemArray_clean(model->items);
|
||||
model->position = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void menu_process_up(Menu* menu) {
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
if(model->position > 0) {
|
||||
model->position--;
|
||||
} else {
|
||||
model->position = MenuItemArray_size(model->items) - 1;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void menu_process_down(Menu* menu) {
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
if(model->position < MenuItemArray_size(model->items) - 1) {
|
||||
model->position++;
|
||||
} else {
|
||||
model->position = 0;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void menu_process_ok(Menu* menu) {
|
||||
MenuItem* item = NULL;
|
||||
with_view_model(
|
||||
menu->view, (MenuModel * model) {
|
||||
if(model->position < MenuItemArray_size(model->items)) {
|
||||
item = MenuItemArray_get(model->items, model->position);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(item && item->callback) {
|
||||
item->callback(item->callback_context, item->index);
|
||||
}
|
||||
}
|
52
applications/gui/modules/menu.h
Executable file
52
applications/gui/modules/menu.h
Executable file
@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Menu anonymous structure */
|
||||
typedef struct Menu Menu;
|
||||
typedef void (*MenuItemCallback)(void* context, uint32_t index);
|
||||
|
||||
/** Menu allocation and initialization
|
||||
* @return Menu instance
|
||||
*/
|
||||
Menu* menu_alloc();
|
||||
|
||||
/** Free menu
|
||||
* @param menu - Menu instance
|
||||
*/
|
||||
void menu_free(Menu* menu);
|
||||
|
||||
/** Get Menu view
|
||||
* @param menu - Menu instance
|
||||
* @return View instance
|
||||
*/
|
||||
View* menu_get_view(Menu* menu);
|
||||
|
||||
/** Add item to menu
|
||||
* @param menu - Menu instance
|
||||
* @param label - menu item string label
|
||||
* @param icon - IconAnimation instance
|
||||
* @param index - menu item index
|
||||
* @param callback - MenuItemCallback instance
|
||||
* @param context - pointer to context
|
||||
*/
|
||||
void menu_add_item(
|
||||
Menu* menu,
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
uint32_t index,
|
||||
MenuItemCallback callback,
|
||||
void* context);
|
||||
|
||||
/** Clean menu
|
||||
* Note: this function does not free menu instance
|
||||
* @param menu - Menu instance
|
||||
*/
|
||||
void menu_clean(Menu* menu);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,23 +1,22 @@
|
||||
#include "submenu.h"
|
||||
#include "gui/canvas.h"
|
||||
|
||||
#include <m-array.h>
|
||||
#include <furi.h>
|
||||
#include <gui/elements.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct SubmenuItem {
|
||||
const char* label;
|
||||
uint32_t index;
|
||||
SubmenuItemCallback callback;
|
||||
void* callback_context;
|
||||
};
|
||||
|
||||
ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST);
|
||||
#include <furi.h>
|
||||
|
||||
struct Submenu {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* label;
|
||||
uint32_t index;
|
||||
SubmenuItemCallback callback;
|
||||
void* callback_context;
|
||||
} SubmenuItem;
|
||||
|
||||
ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST);
|
||||
|
||||
typedef struct {
|
||||
SubmenuItemArray_t items;
|
||||
const char* header;
|
||||
@ -149,7 +148,7 @@ View* submenu_get_view(Submenu* submenu) {
|
||||
return submenu->view;
|
||||
}
|
||||
|
||||
SubmenuItem* submenu_add_item(
|
||||
void submenu_add_item(
|
||||
Submenu* submenu,
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
@ -168,8 +167,6 @@ SubmenuItem* submenu_add_item(
|
||||
item->callback_context = callback_context;
|
||||
return true;
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void submenu_clean(Submenu* submenu) {
|
||||
|
@ -7,7 +7,6 @@ extern "C" {
|
||||
|
||||
/* Submenu anonymous structure */
|
||||
typedef struct Submenu Submenu;
|
||||
typedef struct SubmenuItem SubmenuItem;
|
||||
typedef void (*SubmenuItemCallback)(void* context, uint32_t index);
|
||||
|
||||
/**
|
||||
@ -36,9 +35,8 @@ View* submenu_get_view(Submenu* submenu);
|
||||
* @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* submenu_add_item(
|
||||
void submenu_add_item(
|
||||
Submenu* submenu,
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
|
275
applications/loader/loader.c
Normal file → Executable file
275
applications/loader/loader.c
Normal file → Executable file
@ -1,8 +1,11 @@
|
||||
#include "loader_i.h"
|
||||
|
||||
#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0)
|
||||
#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU)
|
||||
|
||||
static Loader* loader_instance = NULL;
|
||||
|
||||
static void loader_menu_callback(void* _ctx) {
|
||||
static void loader_menu_callback(void* _ctx, uint32_t index) {
|
||||
const FlipperApplication* flipper_app = _ctx;
|
||||
|
||||
furi_assert(flipper_app->app);
|
||||
@ -27,6 +30,11 @@ static void loader_menu_callback(void* _ctx) {
|
||||
furi_thread_start(loader_instance->thread);
|
||||
}
|
||||
|
||||
static void loader_submenu_callback(void* context, uint32_t index) {
|
||||
uint32_t view_id = (uint32_t)context;
|
||||
view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id);
|
||||
}
|
||||
|
||||
static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) {
|
||||
furi_assert(_ctx);
|
||||
const FlipperApplication* flipper_app = (FlipperApplication*)_ctx;
|
||||
@ -60,6 +68,15 @@ bool loader_start(Loader* instance, const char* name, const char* args) {
|
||||
}
|
||||
}
|
||||
|
||||
if(!flipper_app) {
|
||||
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
||||
if(strcmp(FLIPPER_DEBUG_APPS[i].name, name) == 0) {
|
||||
flipper_app = &FLIPPER_DEBUG_APPS[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!flipper_app) {
|
||||
FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name);
|
||||
return false;
|
||||
@ -138,6 +155,14 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t loader_hide_menu(void* context) {
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
static uint32_t loader_back_to_primary_menu(void* context) {
|
||||
return LoaderMenuViewPrimary;
|
||||
}
|
||||
|
||||
static Loader* loader_alloc() {
|
||||
Loader* instance = furi_alloc(sizeof(Loader));
|
||||
|
||||
@ -150,10 +175,45 @@ static Loader* loader_alloc() {
|
||||
|
||||
instance->mutex = osMutexNew(NULL);
|
||||
|
||||
instance->menu_vm = furi_record_open("menu");
|
||||
|
||||
instance->cli = furi_record_open("cli");
|
||||
|
||||
instance->loader_thread = osThreadGetId();
|
||||
|
||||
// Gui
|
||||
instance->gui = furi_record_open("gui");
|
||||
instance->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_attach_to_gui(
|
||||
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
|
||||
// Primary menu
|
||||
instance->primary_menu = menu_alloc();
|
||||
view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu));
|
||||
// Plugins menu
|
||||
instance->plugins_menu = submenu_alloc();
|
||||
view_set_previous_callback(
|
||||
submenu_get_view(instance->plugins_menu), loader_back_to_primary_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher,
|
||||
LoaderMenuViewPlugins,
|
||||
submenu_get_view(instance->plugins_menu));
|
||||
// Debug menu
|
||||
instance->debug_menu = submenu_alloc();
|
||||
view_set_previous_callback(
|
||||
submenu_get_view(instance->debug_menu), loader_back_to_primary_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher, LoaderMenuViewDebug, submenu_get_view(instance->debug_menu));
|
||||
// Settings menu
|
||||
instance->settings_menu = submenu_alloc();
|
||||
view_set_previous_callback(
|
||||
submenu_get_view(instance->settings_menu), loader_back_to_primary_menu);
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher,
|
||||
LoaderMenuViewSettings,
|
||||
submenu_get_view(instance->settings_menu));
|
||||
|
||||
view_dispatcher_enable_queue(instance->view_dispatcher);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ -162,133 +222,111 @@ static void loader_free(Loader* instance) {
|
||||
|
||||
furi_record_close("cli");
|
||||
|
||||
furi_record_close("menu");
|
||||
|
||||
osMutexDelete(instance->mutex);
|
||||
|
||||
string_clear(instance->args);
|
||||
|
||||
furi_thread_free(instance->thread);
|
||||
|
||||
menu_free(loader_instance->primary_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary);
|
||||
submenu_free(loader_instance->plugins_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPlugins);
|
||||
submenu_free(loader_instance->debug_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewDebug);
|
||||
submenu_free(loader_instance->settings_menu);
|
||||
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings);
|
||||
view_dispatcher_free(loader_instance->view_dispatcher);
|
||||
|
||||
furi_record_close("gui");
|
||||
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
}
|
||||
|
||||
static void loader_add_cli_command(FlipperApplication* app) {
|
||||
string_t cli_name;
|
||||
string_init_printf(cli_name, "app_%s", app->name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
app);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
|
||||
static void loader_build_menu() {
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building main menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_add(
|
||||
menu,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_APPS[i].name,
|
||||
FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_APPS[i]));
|
||||
|
||||
// Add cli command
|
||||
string_t cli_name;
|
||||
string_init_set_str(cli_name, "app_");
|
||||
string_cat_str(cli_name, FLIPPER_APPS[i].name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
(void*)&FLIPPER_APPS[i]);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
});
|
||||
size_t i;
|
||||
for(i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_APPS[i]);
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
FLIPPER_APPS[i].name,
|
||||
FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_APPS[i]);
|
||||
}
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
"Plugins",
|
||||
icon_animation_alloc(&A_Plugins_14),
|
||||
i++,
|
||||
loader_submenu_callback,
|
||||
(void*)LoaderMenuViewPlugins);
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
"Debug tools",
|
||||
icon_animation_alloc(&A_Debug_14),
|
||||
i++,
|
||||
loader_submenu_callback,
|
||||
(void*)LoaderMenuViewDebug);
|
||||
menu_add_item(
|
||||
loader_instance->primary_menu,
|
||||
"Settings",
|
||||
icon_animation_alloc(&A_Settings_14),
|
||||
i++,
|
||||
loader_submenu_callback,
|
||||
(void*)LoaderMenuViewSettings);
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building plugins menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
MenuItem* menu_plugins =
|
||||
menu_item_alloc_menu("Plugins", icon_animation_alloc(&A_Plugins_14));
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_subitem_add(
|
||||
menu_plugins,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_PLUGINS[i].name,
|
||||
FLIPPER_PLUGINS[i].icon ? icon_animation_alloc(FLIPPER_PLUGINS[i].icon) :
|
||||
NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_PLUGINS[i]));
|
||||
|
||||
// Add cli command
|
||||
string_t cli_name;
|
||||
string_init_set_str(cli_name, "app_");
|
||||
string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
(void*)&FLIPPER_PLUGINS[i]);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
|
||||
menu_item_add(menu, menu_plugins);
|
||||
});
|
||||
for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
|
||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_PLUGINS[i]);
|
||||
submenu_add_item(
|
||||
loader_instance->plugins_menu,
|
||||
FLIPPER_PLUGINS[i].name,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_PLUGINS[i]);
|
||||
}
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
MenuItem* menu_debug =
|
||||
menu_item_alloc_menu("Debug tools", icon_animation_alloc(&A_Debug_14));
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_subitem_add(
|
||||
menu_debug,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_DEBUG_APPS[i].name,
|
||||
FLIPPER_DEBUG_APPS[i].icon ?
|
||||
icon_animation_alloc(FLIPPER_DEBUG_APPS[i].icon) :
|
||||
NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_DEBUG_APPS[i]));
|
||||
|
||||
// Add cli command
|
||||
string_t cli_name;
|
||||
string_init_set_str(cli_name, "app_");
|
||||
string_cat_str(cli_name, FLIPPER_DEBUG_APPS[i].name);
|
||||
cli_add_command(
|
||||
loader_instance->cli,
|
||||
string_get_cstr(cli_name),
|
||||
CliCommandFlagDefault,
|
||||
loader_cli_callback,
|
||||
(void*)&FLIPPER_DEBUG_APPS[i]);
|
||||
string_clear(cli_name);
|
||||
}
|
||||
|
||||
menu_item_add(menu, menu_debug);
|
||||
});
|
||||
for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
|
||||
loader_add_cli_command((FlipperApplication*)&FLIPPER_DEBUG_APPS[i]);
|
||||
submenu_add_item(
|
||||
loader_instance->debug_menu,
|
||||
FLIPPER_DEBUG_APPS[i].name,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_DEBUG_APPS[i]);
|
||||
}
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu");
|
||||
with_value_mutex(
|
||||
loader_instance->menu_vm, (Menu * menu) {
|
||||
MenuItem* menu_debug =
|
||||
menu_item_alloc_menu("Settings", icon_animation_alloc(&A_Settings_14));
|
||||
for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
|
||||
submenu_add_item(
|
||||
loader_instance->settings_menu,
|
||||
FLIPPER_SETTINGS_APPS[i].name,
|
||||
i,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_SETTINGS_APPS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
|
||||
// Add menu item
|
||||
menu_item_subitem_add(
|
||||
menu_debug,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_SETTINGS_APPS[i].name,
|
||||
FLIPPER_SETTINGS_APPS[i].icon ?
|
||||
icon_animation_alloc(FLIPPER_SETTINGS_APPS[i].icon) :
|
||||
NULL,
|
||||
loader_menu_callback,
|
||||
(void*)&FLIPPER_SETTINGS_APPS[i]));
|
||||
}
|
||||
|
||||
menu_item_add(menu, menu_debug);
|
||||
});
|
||||
void loader_show_menu() {
|
||||
furi_assert(loader_instance);
|
||||
osThreadFlagsSet(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU);
|
||||
}
|
||||
|
||||
int32_t loader_srv(void* p) {
|
||||
@ -300,15 +338,24 @@ int32_t loader_srv(void* p) {
|
||||
|
||||
// Call on start hooks
|
||||
for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
|
||||
(*FLIPPER_ON_SYSTEM_START[i])();
|
||||
FLIPPER_ON_SYSTEM_START[i]();
|
||||
}
|
||||
|
||||
FURI_LOG_I(LOADER_LOG_TAG, "Started");
|
||||
|
||||
furi_record_create("loader", loader_instance);
|
||||
|
||||
#ifdef LOADER_AUTOSTART
|
||||
loader_start(loader_instance, LOADER_AUTOSTART, NULL);
|
||||
#endif
|
||||
|
||||
while(1) {
|
||||
osThreadSuspend(osThreadGetId());
|
||||
uint32_t flags = osThreadFlagsWait(LOADER_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever);
|
||||
if(flags & LOADER_THREAD_FLAG_SHOW_MENU) {
|
||||
view_dispatcher_switch_to_view(
|
||||
loader_instance->view_dispatcher, LoaderMenuViewPrimary);
|
||||
view_dispatcher_run(loader_instance->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
loader_free(loader_instance);
|
||||
|
@ -18,3 +18,6 @@ bool loader_lock(Loader* instance);
|
||||
|
||||
/** Unlock application start */
|
||||
void loader_unlock(Loader* instance);
|
||||
|
||||
/** Show primary loader */
|
||||
void loader_show_menu();
|
||||
|
@ -3,20 +3,39 @@
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <menu/menu.h>
|
||||
#include <menu/menu_item.h>
|
||||
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
||||
#include <gui/modules/menu.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
#include <applications.h>
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define LOADER_LOG_TAG "loader"
|
||||
|
||||
struct Loader {
|
||||
osThreadId_t loader_thread;
|
||||
FuriThread* thread;
|
||||
const FlipperApplication* current_app;
|
||||
string_t args;
|
||||
Cli* cli;
|
||||
ValueMutex* menu_vm;
|
||||
Gui* gui;
|
||||
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Menu* primary_menu;
|
||||
Submenu* plugins_menu;
|
||||
Submenu* debug_menu;
|
||||
Submenu* settings_menu;
|
||||
|
||||
size_t free_heap_size;
|
||||
osMutexId_t mutex;
|
||||
volatile uint8_t lock_semaphore;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LoaderMenuViewPrimary,
|
||||
LoaderMenuViewPlugins,
|
||||
LoaderMenuViewDebug,
|
||||
LoaderMenuViewSettings,
|
||||
} LoaderMenuView;
|
||||
|
@ -1,337 +0,0 @@
|
||||
#include "menu.h"
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include "menu_event.h"
|
||||
#include "menu_item.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
struct Menu {
|
||||
MenuEvent* event;
|
||||
|
||||
// GUI
|
||||
Gui* gui;
|
||||
ViewPort* view_port;
|
||||
IconAnimation* icon;
|
||||
|
||||
// State
|
||||
MenuItem* root;
|
||||
MenuItem* settings;
|
||||
MenuItem* current;
|
||||
};
|
||||
|
||||
void menu_view_port_callback(Canvas* canvas, void* context);
|
||||
|
||||
ValueMutex* menu_init() {
|
||||
Menu* menu = furi_alloc(sizeof(Menu));
|
||||
|
||||
// Event dispatcher
|
||||
menu->event = menu_event_alloc();
|
||||
|
||||
ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
|
||||
if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
|
||||
printf("[menu_task] cannot create menu mutex\r\n");
|
||||
furi_crash(NULL);
|
||||
}
|
||||
|
||||
// OpenGui record
|
||||
menu->gui = furi_record_open("gui");
|
||||
|
||||
// Allocate and configure view_port
|
||||
menu->view_port = view_port_alloc();
|
||||
|
||||
// Open GUI and register fullscreen view_port
|
||||
gui_add_view_port(menu->gui, menu->view_port, GuiLayerFullscreen);
|
||||
|
||||
view_port_enabled_set(menu->view_port, false);
|
||||
view_port_draw_callback_set(menu->view_port, menu_view_port_callback, menu_mutex);
|
||||
view_port_input_callback_set(menu->view_port, menu_event_input_callback, menu->event);
|
||||
|
||||
return menu_mutex;
|
||||
}
|
||||
|
||||
void menu_build_main(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
// Root point
|
||||
menu->root = menu_item_alloc_menu(NULL, NULL);
|
||||
}
|
||||
|
||||
void menu_item_add(Menu* menu, MenuItem* item) {
|
||||
menu_item_subitem_add(menu->root, item);
|
||||
}
|
||||
|
||||
void menu_settings_item_add(Menu* menu, MenuItem* item) {
|
||||
menu_item_subitem_add(menu->settings, item);
|
||||
}
|
||||
|
||||
void menu_draw_primary(Menu* menu, Canvas* canvas) {
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
size_t items_count = MenuItemArray_size(*items);
|
||||
if(items_count) {
|
||||
MenuItem* item;
|
||||
size_t shift_position;
|
||||
// First line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (0 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas_draw_icon_animation(canvas, 4, 3, menu_item_get_icon(item));
|
||||
canvas_draw_str(canvas, 22, 14, menu_item_get_label(item));
|
||||
// Second line main
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
shift_position = (1 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas_draw_icon_animation(canvas, 4, 25, menu_item_get_icon(item));
|
||||
canvas_draw_str(canvas, 22, 36, menu_item_get_label(item));
|
||||
// Third line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (2 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas_draw_icon_animation(canvas, 4, 47, menu_item_get_icon(item));
|
||||
canvas_draw_str(canvas, 22, 58, menu_item_get_label(item));
|
||||
// Frame and scrollbar
|
||||
// elements_frame(canvas, 0, 0, 128 - 5, 21);
|
||||
elements_frame(canvas, 0, 21, 128 - 5, 21);
|
||||
// elements_frame(canvas, 0, 42, 128 - 5, 21);
|
||||
elements_scrollbar(canvas, position, items_count);
|
||||
} else {
|
||||
canvas_draw_str(canvas, 2, 32, "Empty");
|
||||
elements_scrollbar(canvas, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_draw_secondary(Menu* menu, Canvas* canvas) {
|
||||
size_t position = 0;
|
||||
size_t selected_position = menu_item_get_position(menu->current);
|
||||
size_t window_position = menu_item_get_window_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
const uint8_t items_on_screen = 4;
|
||||
const uint8_t item_height = 16;
|
||||
const uint8_t item_width = 123;
|
||||
size_t items_count = MenuItemArray_size(*items);
|
||||
MenuItemArray_it_t it;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
for(MenuItemArray_it(it, *items); !MenuItemArray_end_p(it); MenuItemArray_next(it)) {
|
||||
size_t item_position = position - window_position;
|
||||
|
||||
if(item_position < items_on_screen) {
|
||||
if(position == selected_position) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
elements_slightly_rounded_box(
|
||||
canvas, 0, (item_position * item_height) + 1, item_width, item_height - 2);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
6,
|
||||
(item_position * item_height) + item_height - 4,
|
||||
menu_item_get_label(*MenuItemArray_ref(it)));
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
elements_scrollbar(canvas, selected_position, items_count);
|
||||
}
|
||||
|
||||
void menu_view_port_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(context);
|
||||
|
||||
Menu* menu = acquire_mutex((ValueMutex*)context, 100); // wait 10 ms to get mutex
|
||||
if(menu == NULL) return; // redraw fail
|
||||
|
||||
furi_assert(menu->current);
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// if top level
|
||||
if(menu_item_get_parent(menu->current) == NULL) {
|
||||
menu_draw_primary(menu, canvas);
|
||||
} else {
|
||||
menu_draw_secondary(menu, canvas);
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)context, menu);
|
||||
}
|
||||
|
||||
void menu_set_icon(Menu* menu, IconAnimation* icon) {
|
||||
furi_assert(menu);
|
||||
|
||||
if(menu->icon) {
|
||||
icon_animation_stop(menu->icon);
|
||||
}
|
||||
|
||||
menu->icon = icon;
|
||||
|
||||
if(menu->icon) {
|
||||
icon_animation_start(menu->icon);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_update(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
|
||||
if(menu->current) {
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
size_t items_count = MenuItemArray_size(*items);
|
||||
if(items_count) {
|
||||
MenuItem* item = *MenuItemArray_get(*items, position);
|
||||
menu_set_icon(menu, menu_item_get_icon(item));
|
||||
}
|
||||
}
|
||||
|
||||
menu_event_activity_notify(menu->event);
|
||||
view_port_update(menu->view_port);
|
||||
}
|
||||
|
||||
void menu_up(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
size_t window_position = menu_item_get_window_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
|
||||
const uint8_t items_on_screen = 4;
|
||||
|
||||
if(position > 0) {
|
||||
position--;
|
||||
if(((position - window_position) < 1) && window_position > 0) {
|
||||
window_position--;
|
||||
}
|
||||
} else {
|
||||
position = MenuItemArray_size(*items) - 1;
|
||||
if(position > (items_on_screen - 1)) {
|
||||
window_position = position - (items_on_screen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
menu_item_set_position(menu->current, position);
|
||||
menu_item_set_window_position(menu->current, window_position);
|
||||
menu_update(menu);
|
||||
}
|
||||
|
||||
void menu_down(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
size_t window_position = menu_item_get_window_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
|
||||
const uint8_t items_on_screen = 4;
|
||||
if(position < (MenuItemArray_size(*items) - 1)) {
|
||||
position++;
|
||||
if((position - window_position) > (items_on_screen - 2) &&
|
||||
window_position < (MenuItemArray_size(*items) - items_on_screen)) {
|
||||
window_position++;
|
||||
}
|
||||
} else {
|
||||
position = 0;
|
||||
window_position = 0;
|
||||
}
|
||||
|
||||
menu_item_set_position(menu->current, position);
|
||||
menu_item_set_window_position(menu->current, window_position);
|
||||
menu_update(menu);
|
||||
}
|
||||
|
||||
void menu_ok(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
|
||||
if(!menu->current) {
|
||||
view_port_enabled_set(menu->view_port, true);
|
||||
menu->current = menu->root;
|
||||
menu_item_set_position(menu->current, 0);
|
||||
menu_item_set_window_position(menu->current, 0);
|
||||
menu_update(menu);
|
||||
return;
|
||||
}
|
||||
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
if(!items || MenuItemArray_size(*items) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
MenuItem* item = *MenuItemArray_get(*items, position);
|
||||
MenuItemType type = menu_item_get_type(item);
|
||||
|
||||
if(type == MenuItemTypeMenu) {
|
||||
menu->current = item;
|
||||
menu_item_set_position(menu->current, 0);
|
||||
menu_item_set_window_position(menu->current, 0);
|
||||
menu_update(menu);
|
||||
} else if(type == MenuItemTypeFunction) {
|
||||
menu_item_function_call(item);
|
||||
gui_send_view_port_back(menu->gui, menu->view_port);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_back(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
MenuItem* parent = menu_item_get_parent(menu->current);
|
||||
if(parent) {
|
||||
menu->current = parent;
|
||||
menu_update(menu);
|
||||
} else {
|
||||
menu_exit(menu);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_exit(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
view_port_enabled_set(menu->view_port, false);
|
||||
menu->current = NULL;
|
||||
menu_update(menu);
|
||||
}
|
||||
|
||||
int32_t menu_srv(void* p) {
|
||||
ValueMutex* menu_mutex = menu_init();
|
||||
|
||||
MenuEvent* menu_event = NULL;
|
||||
{
|
||||
Menu* menu = acquire_mutex_block(menu_mutex);
|
||||
furi_check(menu);
|
||||
|
||||
menu_build_main(menu);
|
||||
|
||||
// immutable thread-safe object
|
||||
menu_event = menu->event;
|
||||
|
||||
release_mutex(menu_mutex, menu);
|
||||
}
|
||||
|
||||
furi_record_create("menu", menu_mutex);
|
||||
|
||||
while(1) {
|
||||
MenuMessage m = menu_event_next(menu_event);
|
||||
|
||||
Menu* menu = acquire_mutex_block(menu_mutex);
|
||||
|
||||
if(!menu->current && m.type != MenuMessageTypeOk) {
|
||||
} else if(m.type == MenuMessageTypeUp) {
|
||||
menu_up(menu);
|
||||
} else if(m.type == MenuMessageTypeDown) {
|
||||
menu_down(menu);
|
||||
} else if(m.type == MenuMessageTypeOk) {
|
||||
menu_ok(menu);
|
||||
} else if(m.type == MenuMessageTypeBack) {
|
||||
menu_back(menu);
|
||||
} else if(m.type == MenuMessageTypeIdle) {
|
||||
menu_exit(menu);
|
||||
} else {
|
||||
// TODO: fail somehow?
|
||||
}
|
||||
|
||||
release_mutex(menu_mutex, menu);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "menu/menu_item.h"
|
||||
|
||||
typedef struct Menu Menu;
|
||||
typedef struct MenuItem MenuItem;
|
||||
|
||||
// Add menu item to root menu
|
||||
void menu_item_add(Menu* menu, MenuItem* item);
|
||||
|
||||
// Add menu item to settings menu
|
||||
void menu_settings_item_add(Menu* menu, MenuItem* item);
|
||||
|
||||
// Menu controls
|
||||
void menu_up(Menu* menu);
|
||||
void menu_down(Menu* menu);
|
||||
void menu_ok(Menu* menu);
|
||||
void menu_back(Menu* menu);
|
||||
void menu_exit(Menu* menu);
|
@ -1,65 +0,0 @@
|
||||
#include "menu_event.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define MENU_MESSAGE_MQUEUE_SIZE 8
|
||||
|
||||
struct MenuEvent {
|
||||
osMessageQueueId_t mqueue;
|
||||
};
|
||||
|
||||
void MenuEventimeout_callback(void* arg) {
|
||||
MenuEvent* menu_event = arg;
|
||||
MenuMessage message;
|
||||
message.type = MenuMessageTypeIdle;
|
||||
osMessageQueuePut(menu_event->mqueue, &message, 0, osWaitForever);
|
||||
}
|
||||
|
||||
MenuEvent* menu_event_alloc() {
|
||||
MenuEvent* menu_event = furi_alloc(sizeof(MenuEvent));
|
||||
menu_event->mqueue = osMessageQueueNew(MENU_MESSAGE_MQUEUE_SIZE, sizeof(MenuMessage), NULL);
|
||||
furi_check(menu_event->mqueue);
|
||||
return menu_event;
|
||||
}
|
||||
|
||||
void menu_event_free(MenuEvent* menu_event) {
|
||||
furi_assert(menu_event);
|
||||
furi_check(osMessageQueueDelete(menu_event->mqueue) == osOK);
|
||||
free(menu_event);
|
||||
}
|
||||
|
||||
void menu_event_activity_notify(MenuEvent* menu_event) {
|
||||
furi_assert(menu_event);
|
||||
}
|
||||
|
||||
MenuMessage menu_event_next(MenuEvent* menu_event) {
|
||||
furi_assert(menu_event);
|
||||
MenuMessage message;
|
||||
while(osMessageQueueGet(menu_event->mqueue, &message, NULL, osWaitForever) != osOK) {
|
||||
};
|
||||
return message;
|
||||
}
|
||||
|
||||
void menu_event_input_callback(InputEvent* input_event, void* context) {
|
||||
MenuEvent* menu_event = context;
|
||||
MenuMessage message;
|
||||
|
||||
if(input_event->type != InputTypeShort) return;
|
||||
|
||||
if(input_event->key == InputKeyUp) {
|
||||
message.type = MenuMessageTypeUp;
|
||||
} else if(input_event->key == InputKeyDown) {
|
||||
message.type = MenuMessageTypeDown;
|
||||
} else if(input_event->key == InputKeyOk) {
|
||||
message.type = MenuMessageTypeOk;
|
||||
} else if(input_event->key == InputKeyBack) {
|
||||
message.type = MenuMessageTypeBack;
|
||||
} else {
|
||||
message.type = MenuMessageTypeUnknown;
|
||||
}
|
||||
|
||||
osMessageQueuePut(menu_event->mqueue, &message, 0, osWaitForever);
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <input/input.h>
|
||||
|
||||
typedef enum {
|
||||
MenuMessageTypeUp = 0x00,
|
||||
MenuMessageTypeDown = 0x01,
|
||||
MenuMessageTypeLeft = 0x02,
|
||||
MenuMessageTypeRight = 0x03,
|
||||
MenuMessageTypeOk = 0x04,
|
||||
MenuMessageTypeBack = 0x05,
|
||||
MenuMessageTypeIdle = 0x06,
|
||||
MenuMessageTypeUnknown = 0xFF,
|
||||
} MenuMessageType;
|
||||
|
||||
typedef struct {
|
||||
MenuMessageType type;
|
||||
void* data;
|
||||
} MenuMessage;
|
||||
|
||||
typedef struct MenuEvent MenuEvent;
|
||||
|
||||
MenuEvent* menu_event_alloc();
|
||||
|
||||
void menu_event_free(MenuEvent* menu_event);
|
||||
|
||||
void menu_event_activity_notify(MenuEvent* menu_event);
|
||||
|
||||
MenuMessage menu_event_next(MenuEvent* menu_event);
|
||||
|
||||
void menu_event_input_callback(InputEvent* input_event, void* context);
|
@ -1,135 +0,0 @@
|
||||
#include "menu_item.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
|
||||
struct MenuItem {
|
||||
MenuItemType type;
|
||||
|
||||
const char* label;
|
||||
IconAnimation* icon;
|
||||
|
||||
size_t position;
|
||||
size_t window_position;
|
||||
MenuItem* parent;
|
||||
void* data;
|
||||
|
||||
// callback related
|
||||
MenuItemCallback callback;
|
||||
void* callback_context;
|
||||
};
|
||||
|
||||
MenuItem* menu_item_alloc() {
|
||||
MenuItem* menu_item = furi_alloc(sizeof(MenuItem));
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
MenuItem* menu_item_alloc_menu(const char* label, IconAnimation* icon) {
|
||||
MenuItem* menu_item = menu_item_alloc();
|
||||
|
||||
menu_item->type = MenuItemTypeMenu;
|
||||
menu_item->label = label;
|
||||
menu_item->icon = icon;
|
||||
|
||||
MenuItemArray_t* items = furi_alloc(sizeof(MenuItemArray_t));
|
||||
MenuItemArray_init(*items);
|
||||
menu_item->data = items;
|
||||
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
MenuItem* menu_item_alloc_function(
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
MenuItemCallback callback,
|
||||
void* context) {
|
||||
MenuItem* menu_item = menu_item_alloc();
|
||||
|
||||
menu_item->type = MenuItemTypeFunction;
|
||||
menu_item->label = label;
|
||||
menu_item->icon = icon;
|
||||
menu_item->callback = callback;
|
||||
menu_item->callback_context = context;
|
||||
menu_item->parent = NULL;
|
||||
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
void menu_item_release(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
if(menu_item->type == MenuItemTypeMenu) {
|
||||
//TODO: iterate and release
|
||||
free(menu_item->data);
|
||||
}
|
||||
free(menu_item);
|
||||
}
|
||||
|
||||
MenuItem* menu_item_get_parent(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->parent;
|
||||
}
|
||||
|
||||
void menu_item_subitem_add(MenuItem* menu_item, MenuItem* sub_item) {
|
||||
furi_assert(menu_item);
|
||||
furi_check(menu_item->type == MenuItemTypeMenu);
|
||||
MenuItemArray_t* items = menu_item->data;
|
||||
sub_item->parent = menu_item;
|
||||
MenuItemArray_push_back(*items, sub_item);
|
||||
}
|
||||
|
||||
MenuItemType menu_item_get_type(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->type;
|
||||
}
|
||||
|
||||
void menu_item_set_position(MenuItem* menu_item, size_t position) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->position = position;
|
||||
}
|
||||
|
||||
size_t menu_item_get_position(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->position;
|
||||
}
|
||||
|
||||
void menu_item_set_window_position(MenuItem* menu_item, size_t window_position) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->window_position = window_position;
|
||||
}
|
||||
|
||||
size_t menu_item_get_window_position(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->window_position;
|
||||
}
|
||||
|
||||
void menu_item_set_label(MenuItem* menu_item, const char* label) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->label = label;
|
||||
}
|
||||
|
||||
const char* menu_item_get_label(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->label;
|
||||
}
|
||||
|
||||
void menu_item_set_icon(MenuItem* menu_item, IconAnimation* icon) {
|
||||
furi_assert(menu_item);
|
||||
menu_item->icon = icon;
|
||||
}
|
||||
|
||||
IconAnimation* menu_item_get_icon(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
return menu_item->icon;
|
||||
}
|
||||
|
||||
MenuItemArray_t* menu_item_get_subitems(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
furi_check(menu_item->type == MenuItemTypeMenu);
|
||||
return menu_item->data;
|
||||
}
|
||||
|
||||
void menu_item_function_call(MenuItem* menu_item) {
|
||||
furi_assert(menu_item);
|
||||
furi_check(menu_item->type == MenuItemTypeFunction);
|
||||
if(menu_item->callback) menu_item->callback(menu_item->callback_context);
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <m-array.h>
|
||||
#include <gui/icon_animation.h>
|
||||
|
||||
typedef enum {
|
||||
MenuItemTypeMenu = 0x00,
|
||||
MenuItemTypeFunction = 0x01,
|
||||
} MenuItemType;
|
||||
|
||||
typedef struct MenuItem MenuItem;
|
||||
typedef void (*MenuItemCallback)(void* context);
|
||||
|
||||
ARRAY_DEF(MenuItemArray, MenuItem*, M_PTR_OPLIST);
|
||||
|
||||
MenuItem* menu_item_alloc_menu(const char* label, IconAnimation* icon);
|
||||
|
||||
MenuItem* menu_item_alloc_function(
|
||||
const char* label,
|
||||
IconAnimation* icon,
|
||||
MenuItemCallback callback,
|
||||
void* context);
|
||||
|
||||
void menu_item_release(MenuItem* menu_item);
|
||||
|
||||
MenuItem* menu_item_get_parent(MenuItem* menu_item);
|
||||
|
||||
void menu_item_subitem_add(MenuItem* menu_item, MenuItem* sub_item);
|
||||
|
||||
MenuItemType menu_item_get_type(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_position(MenuItem* menu_item, size_t position);
|
||||
size_t menu_item_get_position(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_window_position(MenuItem* menu_item, size_t window_position);
|
||||
size_t menu_item_get_window_position(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_label(MenuItem* menu_item, const char* label);
|
||||
const char* menu_item_get_label(MenuItem* menu_item);
|
||||
|
||||
void menu_item_set_icon(MenuItem* menu_item, IconAnimation* icon);
|
||||
IconAnimation* menu_item_get_icon(MenuItem* menu_item);
|
||||
|
||||
MenuItemArray_t* menu_item_get_subitems(MenuItem* menu_item);
|
||||
|
||||
void menu_item_function_call(MenuItem* menu_item);
|
@ -16,12 +16,12 @@ void SubmenuVM::clean() {
|
||||
submenu_clean(submenu);
|
||||
}
|
||||
|
||||
SubmenuItem* SubmenuVM::add_item(
|
||||
void SubmenuVM::add_item(
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
SubmenuItemCallback callback,
|
||||
void* callback_context) {
|
||||
return submenu_add_item(submenu, label, index, callback, callback_context);
|
||||
submenu_add_item(submenu, label, index, callback, callback_context);
|
||||
}
|
||||
|
||||
void SubmenuVM::set_selected_item(uint32_t index) {
|
||||
|
@ -16,9 +16,8 @@ public:
|
||||
* @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(
|
||||
void add_item(
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
SubmenuItemCallback callback,
|
||||
|
Loading…
Reference in New Issue
Block a user