[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 gui_srv(void* p); | ||||||
| extern int32_t input_srv(void* p); | extern int32_t input_srv(void* p); | ||||||
| extern int32_t loader_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 notification_srv(void* p); | ||||||
| extern int32_t power_observer_srv(void* p); | extern int32_t power_observer_srv(void* p); | ||||||
| extern int32_t power_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}, |     {.app = input_srv, .name = "Input", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_MENU | #ifdef SRV_LOADER | ||||||
|     {.app = menu_srv, .name = "Menu", .stack_size = 1024, .icon = NULL}, |  | ||||||
|     {.app = loader_srv, .name = "Loader", .stack_size = 1024, .icon = NULL}, |     {.app = loader_srv, .name = "Loader", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| @ -107,43 +105,6 @@ const FlipperApplication FLIPPER_SERVICES[] = { | |||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     {.app = storage_srv, .name = "Storage", .stack_size = 4096, .icon = NULL}, |     {.app = storage_srv, .name = "Storage", .stack_size = 4096, .icon = NULL}, | ||||||
| #endif | #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); | const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication); | ||||||
| @ -184,25 +145,35 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | |||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     crypto_cli_init, |     crypto_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef APP_IRDA | ||||||
|     irda_cli_init, |     irda_cli_init, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_NFC | #ifdef APP_NFC | ||||||
|     nfc_cli_init, |     nfc_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_SUBGHZ | #ifdef APP_SUBGHZ | ||||||
|     subghz_cli_init, |     subghz_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_LF_RFID | #ifdef APP_LF_RFID | ||||||
|     lfrfid_cli_init, |     lfrfid_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_IBUTTON | #ifdef APP_IBUTTON | ||||||
|     ibutton_cli_init, |     ibutton_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
| #ifdef SRV_BT | #ifdef SRV_BT | ||||||
|     bt_cli_init, |     bt_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
| #ifdef SRV_POWER | #ifdef SRV_POWER | ||||||
|     power_cli_init, |     power_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     storage_cli_init, |     storage_cli_init, | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -6,9 +6,6 @@ C_SOURCES   += $(shell find $(APP_DIR) -name *.c) | |||||||
| CPP_SOURCES	+= $(shell find $(APP_DIR) -name *.cpp) | CPP_SOURCES	+= $(shell find $(APP_DIR) -name *.cpp) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Use SRV_* for autostart app
 |  | ||||||
| # Use APP_* for add app to build
 |  | ||||||
| 
 |  | ||||||
| APP_RELEASE ?= 1 | APP_RELEASE ?= 1 | ||||||
| ifeq ($(APP_RELEASE), 1) | ifeq ($(APP_RELEASE), 1) | ||||||
| # Services
 | # Services
 | ||||||
| @ -18,7 +15,7 @@ SRV_DIALOGS = 1 | |||||||
| SRV_DOLPHIN	= 1 | SRV_DOLPHIN	= 1 | ||||||
| SRV_GUI		= 1 | SRV_GUI		= 1 | ||||||
| SRV_INPUT	= 1 | SRV_INPUT	= 1 | ||||||
| SRV_MENU = 1 | SRV_LOADER	= 1 | ||||||
| SRV_NOTIFICATION = 1 | SRV_NOTIFICATION = 1 | ||||||
| SRV_POWER	= 1 | SRV_POWER	= 1 | ||||||
| SRV_POWER_OBSERVER = 1 | SRV_POWER_OBSERVER = 1 | ||||||
| @ -49,230 +46,203 @@ APP_VIBRO_DEMO = 1 | |||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| SRV_BT ?= 0 | # Applications
 | ||||||
| ifeq ($(SRV_BT), 1) | # that will be shown in menu
 | ||||||
| SRV_CLI		= 1 | # Prefix with APP_*
 | ||||||
| CFLAGS		+= -DSRV_BT |  | ||||||
| endif |  | ||||||
| 
 | 
 | ||||||
| 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 | APP_IRDA_MONITOR	?= 0 | ||||||
| ifeq ($(APP_IRDA_MONITOR), 1) | ifeq ($(APP_IRDA_MONITOR), 1) | ||||||
| CFLAGS		+= -DAPP_IRDA_MONITOR | CFLAGS		+= -DAPP_IRDA_MONITOR | ||||||
|  | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| APP_UNIT_TESTS	?= 0 | APP_UNIT_TESTS	?= 0 | ||||||
| ifeq ($(APP_UNIT_TESTS), 1) | ifeq ($(APP_UNIT_TESTS), 1) | ||||||
| CFLAGS		+= -DAPP_UNIT_TESTS | CFLAGS		+= -DAPP_UNIT_TESTS | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_DESKTOP ?= 0 |  | ||||||
| ifeq ($(SRV_DESKTOP), 1) |  | ||||||
| CFLAGS		+= -DSRV_DESKTOP |  | ||||||
| SRV_DESKTOP = 1 |  | ||||||
| endif |  | ||||||
| 
 | 
 | ||||||
| APP_ARCHIVE ?= 0 | APP_ARCHIVE ?= 0 | ||||||
| ifeq ($(APP_NFC), 1) | ifeq ($(APP_ARCHIVE), 1) | ||||||
| CFLAGS		+= -DAPP_ARCHIVE | CFLAGS		+= -DAPP_ARCHIVE | ||||||
| APP_ARCHIVE = 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_BLINK ?= 0 | 
 | ||||||
| ifeq ($(SRV_BLINK), 1) |  | ||||||
| CFLAGS		+= -DSRV_BLINK |  | ||||||
| APP_BLINK = 1 |  | ||||||
| endif |  | ||||||
| APP_BLINK ?= 0 | APP_BLINK ?= 0 | ||||||
| ifeq ($(APP_BLINK), 1) | ifeq ($(APP_BLINK), 1) | ||||||
| CFLAGS		+= -DAPP_BLINK | CFLAGS		+= -DAPP_BLINK | ||||||
| SRV_INPUT = 1 | SRV_GUI		= 1 | ||||||
| endif | 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) | ifeq ($(APP_SUBGHZ), 1) | ||||||
| CFLAGS		+= -DAPP_SUBGHZ | CFLAGS		+= -DAPP_SUBGHZ | ||||||
| SRV_INPUT = 1 |  | ||||||
| SRV_GUI		= 1 | SRV_GUI		= 1 | ||||||
| SRV_CLI		= 1 | SRV_CLI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | APP_ABOUT ?= 0 | ||||||
| ifeq ($(APP_ABOUT), 1) | ifeq ($(APP_ABOUT), 1) | ||||||
| CFLAGS		+= -DAPP_ABOUT | CFLAGS		+= -DAPP_ABOUT | ||||||
| SRV_INPUT = 1 |  | ||||||
| SRV_GUI		= 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_LF_RFID ?= 0 | 
 | ||||||
| ifeq ($(SRV_LF_RFID), 1) |  | ||||||
| CFLAGS		+= -DSRV_LF_RFID |  | ||||||
| APP_LF_RFID = 1 |  | ||||||
| endif |  | ||||||
| APP_LF_RFID ?= 0 | APP_LF_RFID ?= 0 | ||||||
| ifeq ($(APP_LF_RFID), 1) | ifeq ($(APP_LF_RFID), 1) | ||||||
| CFLAGS		+= -DAPP_LF_RFID | CFLAGS		+= -DAPP_LF_RFID | ||||||
| SRV_INPUT = 1 |  | ||||||
| SRV_GUI		= 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| APP_NFC ?= 0 | APP_NFC ?= 0 | ||||||
| ifeq ($(APP_NFC), 1) | ifeq ($(APP_NFC), 1) | ||||||
| CFLAGS		+= -DAPP_NFC | CFLAGS		+= -DAPP_NFC | ||||||
| SRV_MENU = 1 |  | ||||||
| SRV_INPUT = 1 |  | ||||||
| SRV_GUI		= 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_IRDA ?= 0 | 
 | ||||||
| ifeq ($(SRV_IRDA), 1) |  | ||||||
| CFLAGS		+= -DSRV_IRDA |  | ||||||
| APP_IRDA = 1 |  | ||||||
| endif |  | ||||||
| APP_IRDA ?= 0 | APP_IRDA ?= 0 | ||||||
| ifeq ($(APP_IRDA), 1) | ifeq ($(APP_IRDA), 1) | ||||||
| CFLAGS		+= -DAPP_IRDA | CFLAGS		+= -DAPP_IRDA | ||||||
| SRV_INPUT = 1 |  | ||||||
| SRV_GUI		= 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| APP_VIBRO_DEMO ?= 0 | APP_VIBRO_DEMO ?= 0 | ||||||
| ifeq ($(APP_VIBRO_DEMO), 1) | ifeq ($(APP_VIBRO_DEMO), 1) | ||||||
| CFLAGS		+= -DAPP_VIBRO_DEMO | CFLAGS		+= -DAPP_VIBRO_DEMO | ||||||
| SRV_INPUT = 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_KEYPAD_TEST ?= 0 | 
 | ||||||
| ifeq ($(SRV_KEYPAD_TEST), 1) |  | ||||||
| CFLAGS		+= -DSRV_KEYPAD_TEST |  | ||||||
| APP_KEYPAD_TEST = 1 |  | ||||||
| endif |  | ||||||
| APP_KEYPAD_TEST ?= 0 | APP_KEYPAD_TEST ?= 0 | ||||||
| ifeq ($(APP_KEYPAD_TEST), 1) | ifeq ($(APP_KEYPAD_TEST), 1) | ||||||
| CFLAGS		+= -DAPP_KEYPAD_TEST | CFLAGS		+= -DAPP_KEYPAD_TEST | ||||||
| APP_KEYPAD_TEST = 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_ACCESSOR ?= 0 | 
 | ||||||
| ifeq ($(SRV_ACCESSOR), 1) |  | ||||||
| CFLAGS		+= -DSRV_ACCESSOR |  | ||||||
| APP_ACCESSOR = 1 |  | ||||||
| endif |  | ||||||
| APP_ACCESSOR ?= 0 | APP_ACCESSOR ?= 0 | ||||||
| ifeq ($(APP_ACCESSOR), 1) | ifeq ($(APP_ACCESSOR), 1) | ||||||
| CFLAGS		+= -DAPP_ACCESSOR | CFLAGS		+= -DAPP_ACCESSOR | ||||||
| APP_ACCESSOR = 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_GPIO_TEST ?= 0 | 
 | ||||||
| ifeq ($(SRV_GPIO_TEST), 1) |  | ||||||
| CFLAGS		+= -DSRV_GPIO_TEST |  | ||||||
| APP_GPIO_TEST = 1 |  | ||||||
| endif |  | ||||||
| APP_GPIO_TEST ?= 0 | APP_GPIO_TEST ?= 0 | ||||||
| ifeq ($(APP_GPIO_TEST), 1) | ifeq ($(APP_GPIO_TEST), 1) | ||||||
| CFLAGS		+= -DAPP_GPIO_TEST | CFLAGS		+= -DAPP_GPIO_TEST | ||||||
|  | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_MUSIC_PLAYER ?= 0 | 
 | ||||||
| ifeq ($(SRV_MUSIC_PLAYER), 1) |  | ||||||
| CFLAGS		+= -DSRV_MUSIC_PLAYER |  | ||||||
| APP_MUSIC_PLAYER = 1 |  | ||||||
| endif |  | ||||||
| APP_MUSIC_PLAYER ?= 0 | APP_MUSIC_PLAYER ?= 0 | ||||||
| ifeq ($(APP_MUSIC_PLAYER), 1) | ifeq ($(APP_MUSIC_PLAYER), 1) | ||||||
| CFLAGS		+= -DAPP_MUSIC_PLAYER | CFLAGS		+= -DAPP_MUSIC_PLAYER | ||||||
|  | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| SRV_IBUTTON ?= 0 | 
 | ||||||
| ifeq ($(SRV_IBUTTON), 1) |  | ||||||
| CFLAGS		+= -DSRV_IBUTTON |  | ||||||
| APP_IBUTTON = 1 |  | ||||||
| endif |  | ||||||
| APP_IBUTTON ?= 0 | APP_IBUTTON ?= 0 | ||||||
| ifeq ($(APP_IBUTTON), 1) | ifeq ($(APP_IBUTTON), 1) | ||||||
| CFLAGS		+= -DAPP_IBUTTON | 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 | endif | ||||||
| 
 | 
 | ||||||
| #
 |  | ||||||
| # Essential services
 |  | ||||||
| #
 |  | ||||||
| 
 | 
 | ||||||
| SRV_GUI	?= 0 | SRV_GUI	?= 0 | ||||||
| ifeq ($(SRV_GUI), 1) | ifeq ($(SRV_GUI), 1) | ||||||
| CFLAGS		+= -DSRV_GUI | CFLAGS		+= -DSRV_GUI | ||||||
|  | SRV_INPUT	= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| SRV_INPUT	?= 0 | SRV_INPUT	?= 0 | ||||||
| ifeq ($(SRV_INPUT), 1) | ifeq ($(SRV_INPUT), 1) | ||||||
| CFLAGS		+= -DSRV_INPUT | CFLAGS		+= -DSRV_INPUT | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| SRV_CLI ?= 0 | SRV_CLI ?= 0 | ||||||
| ifeq ($(SRV_CLI), 1) | ifeq ($(SRV_CLI), 1) | ||||||
| SRV_GUI		= 1 |  | ||||||
| CFLAGS		+= -DSRV_CLI | CFLAGS		+= -DSRV_CLI | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| SRV_NOTIFICATION ?= 0 | SRV_NOTIFICATION ?= 0 | ||||||
| ifeq ($(SRV_NOTIFICATION), 1) | ifeq ($(SRV_NOTIFICATION), 1) | ||||||
| CFLAGS		+= -DSRV_NOTIFICATION | CFLAGS		+= -DSRV_NOTIFICATION | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| SRV_STORAGE ?= 0 | SRV_STORAGE ?= 0 | ||||||
| ifeq ($(SRV_STORAGE), 1) | ifeq ($(SRV_STORAGE), 1) | ||||||
| CFLAGS		+= -DSRV_STORAGE | CFLAGS		+= -DSRV_STORAGE | ||||||
| endif | 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_alloc() { | ||||||
|     Desktop* desktop = furi_alloc(sizeof(Desktop)); |     Desktop* desktop = furi_alloc(sizeof(Desktop)); | ||||||
| 
 | 
 | ||||||
|     desktop->menu_vm = furi_record_open("menu"); |  | ||||||
|     desktop->gui = furi_record_open("gui"); |     desktop->gui = furi_record_open("gui"); | ||||||
|     desktop->scene_thread = furi_thread_alloc(); |     desktop->scene_thread = furi_thread_alloc(); | ||||||
|     desktop->view_dispatcher = view_dispatcher_alloc(); |     desktop->view_dispatcher = view_dispatcher_alloc(); | ||||||
| @ -101,7 +100,6 @@ void desktop_free(Desktop* desktop) { | |||||||
|     furi_thread_free(desktop->scene_thread); |     furi_thread_free(desktop->scene_thread); | ||||||
| 
 | 
 | ||||||
|     furi_record_close("menu"); |     furi_record_close("menu"); | ||||||
|     desktop->menu_vm = NULL; |  | ||||||
| 
 | 
 | ||||||
|     free(desktop); |     free(desktop); | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| #include <menu/menu.h> |  | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| @ -34,8 +33,6 @@ typedef enum { | |||||||
| } DesktopViewEnum; | } DesktopViewEnum; | ||||||
| 
 | 
 | ||||||
| struct Desktop { | struct Desktop { | ||||||
|     // Menu
 |  | ||||||
|     ValueMutex* menu_vm; |  | ||||||
|     // Scene
 |     // Scene
 | ||||||
|     FuriThread* scene_thread; |     FuriThread* scene_thread; | ||||||
|     // GUI
 |     // GUI
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "../views/desktop_main.h" | #include "../views/desktop_main.h" | ||||||
| #include "applications.h" | #include "applications.h" | ||||||
|  | #include <loader/loader.h> | ||||||
| #define MAIN_VIEW_DEFAULT (0UL) | #define MAIN_VIEW_DEFAULT (0UL) | ||||||
| 
 | 
 | ||||||
| static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { | 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) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopMainEventOpenMenu: |         case DesktopMainEventOpenMenu: | ||||||
|             with_value_mutex( |             loader_show_menu(); | ||||||
|                 desktop->menu_vm, (Menu * menu) { menu_ok(menu); }); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             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 "submenu.h" | ||||||
| #include "gui/canvas.h" | 
 | ||||||
| #include <m-array.h> | #include <m-array.h> | ||||||
| #include <furi.h> |  | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <stdint.h> | #include <furi.h> | ||||||
| 
 |  | ||||||
| struct SubmenuItem { |  | ||||||
|     const char* label; |  | ||||||
|     uint32_t index; |  | ||||||
|     SubmenuItemCallback callback; |  | ||||||
|     void* callback_context; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST); |  | ||||||
| 
 | 
 | ||||||
| struct Submenu { | struct Submenu { | ||||||
|     View* view; |     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 { | typedef struct { | ||||||
|     SubmenuItemArray_t items; |     SubmenuItemArray_t items; | ||||||
|     const char* header; |     const char* header; | ||||||
| @ -149,7 +148,7 @@ View* submenu_get_view(Submenu* submenu) { | |||||||
|     return submenu->view; |     return submenu->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubmenuItem* submenu_add_item( | void submenu_add_item( | ||||||
|     Submenu* submenu, |     Submenu* submenu, | ||||||
|     const char* label, |     const char* label, | ||||||
|     uint32_t index, |     uint32_t index, | ||||||
| @ -168,8 +167,6 @@ SubmenuItem* submenu_add_item( | |||||||
|             item->callback_context = callback_context; |             item->callback_context = callback_context; | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| 
 |  | ||||||
|     return item; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void submenu_clean(Submenu* submenu) { | void submenu_clean(Submenu* submenu) { | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| /* Submenu anonymous structure */ | /* Submenu anonymous structure */ | ||||||
| typedef struct Submenu Submenu; | typedef struct Submenu Submenu; | ||||||
| typedef struct SubmenuItem SubmenuItem; |  | ||||||
| typedef void (*SubmenuItemCallback)(void* context, uint32_t index); | 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 index - menu item index, used for callback, may be the same with other items | ||||||
|  * @param callback - menu item callback |  * @param callback - menu item callback | ||||||
|  * @param callback_context - menu item callback context |  * @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, |     Submenu* submenu, | ||||||
|     const char* label, |     const char* label, | ||||||
|     uint32_t index, |     uint32_t index, | ||||||
|  | |||||||
							
								
								
									
										243
									
								
								applications/loader/loader.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										243
									
								
								applications/loader/loader.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -1,8 +1,11 @@ | |||||||
| #include "loader_i.h" | #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 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; |     const FlipperApplication* flipper_app = _ctx; | ||||||
| 
 | 
 | ||||||
|     furi_assert(flipper_app->app); |     furi_assert(flipper_app->app); | ||||||
| @ -27,6 +30,11 @@ static void loader_menu_callback(void* _ctx) { | |||||||
|     furi_thread_start(loader_instance->thread); |     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) { | static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) { | ||||||
|     furi_assert(_ctx); |     furi_assert(_ctx); | ||||||
|     const FlipperApplication* flipper_app = (FlipperApplication*)_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) { |     if(!flipper_app) { | ||||||
|         FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name); |         FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name); | ||||||
|         return false; |         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() { | static Loader* loader_alloc() { | ||||||
|     Loader* instance = furi_alloc(sizeof(Loader)); |     Loader* instance = furi_alloc(sizeof(Loader)); | ||||||
| 
 | 
 | ||||||
| @ -150,10 +175,45 @@ static Loader* loader_alloc() { | |||||||
| 
 | 
 | ||||||
|     instance->mutex = osMutexNew(NULL); |     instance->mutex = osMutexNew(NULL); | ||||||
| 
 | 
 | ||||||
|     instance->menu_vm = furi_record_open("menu"); |  | ||||||
| 
 |  | ||||||
|     instance->cli = furi_record_open("cli"); |     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; |     return instance; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -162,133 +222,111 @@ static void loader_free(Loader* instance) { | |||||||
| 
 | 
 | ||||||
|     furi_record_close("cli"); |     furi_record_close("cli"); | ||||||
| 
 | 
 | ||||||
|     furi_record_close("menu"); |  | ||||||
| 
 |  | ||||||
|     osMutexDelete(instance->mutex); |     osMutexDelete(instance->mutex); | ||||||
| 
 | 
 | ||||||
|     string_clear(instance->args); |     string_clear(instance->args); | ||||||
| 
 | 
 | ||||||
|     furi_thread_free(instance->thread); |     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); |     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() { | static void loader_build_menu() { | ||||||
|     FURI_LOG_I(LOADER_LOG_TAG, "Building main menu"); |     FURI_LOG_I(LOADER_LOG_TAG, "Building main menu"); | ||||||
|     with_value_mutex( |     size_t i; | ||||||
|         loader_instance->menu_vm, (Menu * menu) { |     for(i = 0; i < FLIPPER_APPS_COUNT; i++) { | ||||||
|             for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { |         loader_add_cli_command((FlipperApplication*)&FLIPPER_APPS[i]); | ||||||
|                 // Add menu item
 |         menu_add_item( | ||||||
|                 menu_item_add( |             loader_instance->primary_menu, | ||||||
|                     menu, |  | ||||||
|                     menu_item_alloc_function( |  | ||||||
|             FLIPPER_APPS[i].name, |             FLIPPER_APPS[i].name, | ||||||
|             FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL, |             FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL, | ||||||
|  |             i, | ||||||
|             loader_menu_callback, |             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]); |             (void*)&FLIPPER_APPS[i]); | ||||||
|                 string_clear(cli_name); |  | ||||||
|     } |     } | ||||||
|         }); |     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"); |     FURI_LOG_I(LOADER_LOG_TAG, "Building plugins menu"); | ||||||
|     with_value_mutex( |     for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { | ||||||
|         loader_instance->menu_vm, (Menu * menu) { |         loader_add_cli_command((FlipperApplication*)&FLIPPER_PLUGINS[i]); | ||||||
|             MenuItem* menu_plugins = |         submenu_add_item( | ||||||
|                 menu_item_alloc_menu("Plugins", icon_animation_alloc(&A_Plugins_14)); |             loader_instance->plugins_menu, | ||||||
| 
 |  | ||||||
|             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].name, | ||||||
|                         FLIPPER_PLUGINS[i].icon ? icon_animation_alloc(FLIPPER_PLUGINS[i].icon) : |             i, | ||||||
|                                                   NULL, |  | ||||||
|             loader_menu_callback, |             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]); |             (void*)&FLIPPER_PLUGINS[i]); | ||||||
|                 string_clear(cli_name); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|             menu_item_add(menu, menu_plugins); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu"); |     FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu"); | ||||||
|     with_value_mutex( |     for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { | ||||||
|         loader_instance->menu_vm, (Menu * menu) { |         loader_add_cli_command((FlipperApplication*)&FLIPPER_DEBUG_APPS[i]); | ||||||
|             MenuItem* menu_debug = |         submenu_add_item( | ||||||
|                 menu_item_alloc_menu("Debug tools", icon_animation_alloc(&A_Debug_14)); |             loader_instance->debug_menu, | ||||||
| 
 |  | ||||||
|             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].name, | ||||||
|                         FLIPPER_DEBUG_APPS[i].icon ? |             i, | ||||||
|                             icon_animation_alloc(FLIPPER_DEBUG_APPS[i].icon) : |  | ||||||
|                             NULL, |  | ||||||
|             loader_menu_callback, |             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]); |             (void*)&FLIPPER_DEBUG_APPS[i]); | ||||||
|                 string_clear(cli_name); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|             menu_item_add(menu, menu_debug); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu"); |     FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu"); | ||||||
|     with_value_mutex( |     for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { | ||||||
|         loader_instance->menu_vm, (Menu * menu) { |         submenu_add_item( | ||||||
|             MenuItem* menu_debug = |             loader_instance->settings_menu, | ||||||
|                 menu_item_alloc_menu("Settings", icon_animation_alloc(&A_Settings_14)); |  | ||||||
| 
 |  | ||||||
|             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].name, | ||||||
|                         FLIPPER_SETTINGS_APPS[i].icon ? |             i, | ||||||
|                             icon_animation_alloc(FLIPPER_SETTINGS_APPS[i].icon) : |  | ||||||
|                             NULL, |  | ||||||
|             loader_menu_callback, |             loader_menu_callback, | ||||||
|                         (void*)&FLIPPER_SETTINGS_APPS[i])); |             (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) { | int32_t loader_srv(void* p) { | ||||||
| @ -300,15 +338,24 @@ int32_t loader_srv(void* p) { | |||||||
| 
 | 
 | ||||||
|     // Call on start hooks
 |     // Call on start hooks
 | ||||||
|     for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { |     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_LOG_I(LOADER_LOG_TAG, "Started"); | ||||||
| 
 | 
 | ||||||
|     furi_record_create("loader", loader_instance); |     furi_record_create("loader", loader_instance); | ||||||
| 
 | 
 | ||||||
|  | #ifdef LOADER_AUTOSTART | ||||||
|  |     loader_start(loader_instance, LOADER_AUTOSTART, NULL); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|     while(1) { |     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); |     loader_free(loader_instance); | ||||||
|  | |||||||
| @ -18,3 +18,6 @@ bool loader_lock(Loader* instance); | |||||||
| 
 | 
 | ||||||
| /** Unlock application start */ | /** Unlock application start */ | ||||||
| void loader_unlock(Loader* instance); | void loader_unlock(Loader* instance); | ||||||
|  | 
 | ||||||
|  | /** Show primary loader */ | ||||||
|  | void loader_show_menu(); | ||||||
|  | |||||||
| @ -3,20 +3,39 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| #include <cli/cli.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 <applications.h> | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
| 
 | 
 | ||||||
| #define LOADER_LOG_TAG "loader" | #define LOADER_LOG_TAG "loader" | ||||||
| 
 | 
 | ||||||
| struct Loader { | struct Loader { | ||||||
|  |     osThreadId_t loader_thread; | ||||||
|     FuriThread* thread; |     FuriThread* thread; | ||||||
|     const FlipperApplication* current_app; |     const FlipperApplication* current_app; | ||||||
|     string_t args; |     string_t args; | ||||||
|     Cli* cli; |     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; |     size_t free_heap_size; | ||||||
|     osMutexId_t mutex; |     osMutexId_t mutex; | ||||||
|     volatile uint8_t lock_semaphore; |     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); |     submenu_clean(submenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubmenuItem* SubmenuVM::add_item( | void SubmenuVM::add_item( | ||||||
|     const char* label, |     const char* label, | ||||||
|     uint32_t index, |     uint32_t index, | ||||||
|     SubmenuItemCallback callback, |     SubmenuItemCallback callback, | ||||||
|     void* callback_context) { |     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) { | 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 index - menu item index, used for callback, may be the same with other items | ||||||
|      * @param callback - menu item callback |      * @param callback - menu item callback | ||||||
|      * @param callback_context - menu item callback context |      * @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, |         const char* label, | ||||||
|         uint32_t index, |         uint32_t index, | ||||||
|         SubmenuItemCallback callback, |         SubmenuItemCallback callback, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich