[FL-976] Removing lambdas (#1849)
* Removing lambdas... * Wake the fk up, Gordon! We have a citadel to burn! * Here comes the Nihilanth * Lambda documentation Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									981f7ff8b0
								
							
						
					
					
						commit
						31c0346adc
					
				| @ -154,7 +154,9 @@ static bool bt_test_input_callback(InputEvent* event, void* context) { | ||||
| 
 | ||||
| void bt_test_process_up(BtTest* bt_test) { | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             uint8_t params_on_screen = 3; | ||||
|             if(model->position > 0) { | ||||
|                 model->position--; | ||||
| @ -168,13 +170,15 @@ void bt_test_process_up(BtTest* bt_test) { | ||||
|                     model->window_position = model->position - (params_on_screen - 1); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void bt_test_process_down(BtTest* bt_test) { | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             uint8_t params_on_screen = 3; | ||||
|             if(model->position < (BtTestParamArray_size(model->params) - 1)) { | ||||
|                 model->position++; | ||||
| @ -187,8 +191,8 @@ void bt_test_process_down(BtTest* bt_test) { | ||||
|                 model->position = 0; | ||||
|                 model->window_position = 0; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| BtTestParam* bt_test_get_selected_param(BtTestModel* model) { | ||||
| @ -213,7 +217,9 @@ BtTestParam* bt_test_get_selected_param(BtTestModel* model) { | ||||
| void bt_test_process_left(BtTest* bt_test) { | ||||
|     BtTestParam* param; | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             param = bt_test_get_selected_param(model); | ||||
|             if(param->current_value_index > 0) { | ||||
|                 param->current_value_index--; | ||||
| @ -225,8 +231,8 @@ void bt_test_process_left(BtTest* bt_test) { | ||||
|                     model->packets_num_tx = 0; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     if(param->change_callback) { | ||||
|         param->change_callback(param); | ||||
|     } | ||||
| @ -235,7 +241,9 @@ void bt_test_process_left(BtTest* bt_test) { | ||||
| void bt_test_process_right(BtTest* bt_test) { | ||||
|     BtTestParam* param; | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             param = bt_test_get_selected_param(model); | ||||
|             if(param->current_value_index < (param->values_count - 1)) { | ||||
|                 param->current_value_index++; | ||||
| @ -247,8 +255,8 @@ void bt_test_process_right(BtTest* bt_test) { | ||||
|                     model->packets_num_tx = 0; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     if(param->change_callback) { | ||||
|         param->change_callback(param); | ||||
|     } | ||||
| @ -257,7 +265,9 @@ void bt_test_process_right(BtTest* bt_test) { | ||||
| void bt_test_process_ok(BtTest* bt_test) { | ||||
|     BtTestState state; | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             if(model->state == BtTestStateStarted) { | ||||
|                 model->state = BtTestStateStopped; | ||||
|                 model->message = BT_TEST_START_MESSAGE; | ||||
| @ -269,8 +279,8 @@ void bt_test_process_ok(BtTest* bt_test) { | ||||
|                 model->message = BT_TEST_STOP_MESSAGE; | ||||
|             } | ||||
|             state = model->state; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     if(bt_test->change_state_callback) { | ||||
|         bt_test->change_state_callback(state, bt_test->context); | ||||
|     } | ||||
| @ -278,13 +288,15 @@ void bt_test_process_ok(BtTest* bt_test) { | ||||
| 
 | ||||
| void bt_test_process_back(BtTest* bt_test) { | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             model->state = BtTestStateStopped; | ||||
|             model->rssi = 0.0f; | ||||
|             model->packets_num_rx = 0; | ||||
|             model->packets_num_tx = 0; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     if(bt_test->back_callback) { | ||||
|         bt_test->back_callback(bt_test->context); | ||||
|     } | ||||
| @ -299,7 +311,9 @@ BtTest* bt_test_alloc() { | ||||
|     view_set_input_callback(bt_test->view, bt_test_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             model->state = BtTestStateStopped; | ||||
|             model->message = "Ok - Start"; | ||||
|             BtTestParamArray_init(model->params); | ||||
| @ -308,8 +322,8 @@ BtTest* bt_test_alloc() { | ||||
|             model->rssi = 0.0f; | ||||
|             model->packets_num_tx = 0; | ||||
|             model->packets_num_rx = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return bt_test; | ||||
| } | ||||
| @ -318,15 +332,17 @@ void bt_test_free(BtTest* bt_test) { | ||||
|     furi_assert(bt_test); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             BtTestParamArray_it_t it; | ||||
|             for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it); | ||||
|                 BtTestParamArray_next(it)) { | ||||
|                 furi_string_free(BtTestParamArray_ref(it)->current_value_text); | ||||
|             } | ||||
|             BtTestParamArray_clear(model->params); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     view_free(bt_test->view); | ||||
|     free(bt_test); | ||||
| } | ||||
| @ -347,7 +363,9 @@ BtTestParam* bt_test_param_add( | ||||
|     furi_assert(bt_test); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|             param = BtTestParamArray_push_new(model->params); | ||||
|             param->label = label; | ||||
|             param->values_count = values_count; | ||||
| @ -355,8 +373,8 @@ BtTestParam* bt_test_param_add( | ||||
|             param->context = context; | ||||
|             param->current_value_index = 0; | ||||
|             param->current_value_text = furi_string_alloc(); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return param; | ||||
| } | ||||
| @ -364,28 +382,19 @@ BtTestParam* bt_test_param_add( | ||||
| void bt_test_set_rssi(BtTest* bt_test, float rssi) { | ||||
|     furi_assert(bt_test); | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|             model->rssi = rssi; | ||||
|             return true; | ||||
|         }); | ||||
|         bt_test->view, BtTestModel * model, { model->rssi = rssi; }, true); | ||||
| } | ||||
| 
 | ||||
| void bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num) { | ||||
|     furi_assert(bt_test); | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|             model->packets_num_tx = packets_num; | ||||
|             return true; | ||||
|         }); | ||||
|         bt_test->view, BtTestModel * model, { model->packets_num_tx = packets_num; }, true); | ||||
| } | ||||
| 
 | ||||
| void bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num) { | ||||
|     furi_assert(bt_test); | ||||
|     with_view_model( | ||||
|         bt_test->view, (BtTestModel * model) { | ||||
|             model->packets_num_rx = packets_num; | ||||
|             return true; | ||||
|         }); | ||||
|         bt_test->view, BtTestModel * model, { model->packets_num_rx = packets_num; }, true); | ||||
| } | ||||
| 
 | ||||
| void bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback) { | ||||
|  | ||||
| @ -110,7 +110,9 @@ static bool view_display_test_input_callback(InputEvent* event, void* context) { | ||||
|     bool consumed = false; | ||||
|     if(event->type == InputTypeShort || event->type == InputTypeRepeat) { | ||||
|         with_view_model( | ||||
|             instance->view, (ViewDisplayTestModel * model) { | ||||
|             instance->view, | ||||
|             ViewDisplayTestModel * model, | ||||
|             { | ||||
|                 if(event->key == InputKeyLeft && model->test > 0) { | ||||
|                     model->test--; | ||||
|                     consumed = true; | ||||
| @ -129,8 +131,8 @@ static bool view_display_test_input_callback(InputEvent* event, void* context) { | ||||
|                     model->flip_flop = !model->flip_flop; | ||||
|                     consumed = true; | ||||
|                 } | ||||
|                 return consumed; | ||||
|             }); | ||||
|             }, | ||||
|             consumed); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| @ -149,10 +151,7 @@ static void view_display_test_exit(void* context) { | ||||
| static void view_display_test_timer_callback(void* context) { | ||||
|     ViewDisplayTest* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, (ViewDisplayTestModel * model) { | ||||
|             model->counter++; | ||||
|             return true; | ||||
|         }); | ||||
|         instance->view, ViewDisplayTestModel * model, { model->counter++; }, true); | ||||
| } | ||||
| 
 | ||||
| ViewDisplayTest* view_display_test_alloc() { | ||||
|  | ||||
| @ -52,23 +52,29 @@ static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) { | ||||
| 
 | ||||
| static void lfrfid_debug_view_tune_button_up(LfRfidTuneView* tune_view) { | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|         tune_view->view, | ||||
|         LfRfidTuneViewModel * model, | ||||
|         { | ||||
|             if(model->pos > 0) model->pos--; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void lfrfid_debug_view_tune_button_down(LfRfidTuneView* tune_view) { | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|         tune_view->view, | ||||
|         LfRfidTuneViewModel * model, | ||||
|         { | ||||
|             if(model->pos < 1) model->pos++; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) { | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|         tune_view->view, | ||||
|         LfRfidTuneViewModel * model, | ||||
|         { | ||||
|             if(model->pos == 0) { | ||||
|                 if(model->fine) { | ||||
|                     model->ARR -= 1; | ||||
| @ -84,13 +90,15 @@ static void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) { | ||||
|             } | ||||
| 
 | ||||
|             model->dirty = true; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) { | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|         tune_view->view, | ||||
|         LfRfidTuneViewModel * model, | ||||
|         { | ||||
|             if(model->pos == 0) { | ||||
|                 if(model->fine) { | ||||
|                     model->ARR += 1; | ||||
| @ -106,16 +114,13 @@ static void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) { | ||||
|             } | ||||
| 
 | ||||
|             model->dirty = true; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void lfrfid_debug_view_tune_button_ok(LfRfidTuneView* tune_view) { | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|             model->fine = !model->fine; | ||||
|             return true; | ||||
|         }); | ||||
|         tune_view->view, LfRfidTuneViewModel * model, { model->fine = !model->fine; }, true); | ||||
| } | ||||
| 
 | ||||
| static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* context) { | ||||
| @ -158,14 +163,16 @@ LfRfidTuneView* lfrfid_debug_view_tune_alloc() { | ||||
|     view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel)); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|         tune_view->view, | ||||
|         LfRfidTuneViewModel * model, | ||||
|         { | ||||
|             model->dirty = true; | ||||
|             model->fine = false; | ||||
|             model->ARR = 511; | ||||
|             model->CCR = 255; | ||||
|             model->pos = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback); | ||||
|     view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback); | ||||
| @ -184,24 +191,28 @@ View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view) { | ||||
| 
 | ||||
| void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) { | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|         tune_view->view, | ||||
|         LfRfidTuneViewModel * model, | ||||
|         { | ||||
|             model->dirty = true; | ||||
|             model->fine = false; | ||||
|             model->ARR = 511; | ||||
|             model->CCR = 255; | ||||
|             model->pos = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) { | ||||
|     bool result = false; | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|         tune_view->view, | ||||
|         LfRfidTuneViewModel * model, | ||||
|         { | ||||
|             result = model->dirty; | ||||
|             model->dirty = false; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| @ -209,10 +220,7 @@ bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) { | ||||
| uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) { | ||||
|     uint32_t result = false; | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|             result = model->ARR; | ||||
|             return false; | ||||
|         }); | ||||
|         tune_view->view, LfRfidTuneViewModel * model, { result = model->ARR; }, false); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| @ -220,10 +228,7 @@ uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) { | ||||
| uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) { | ||||
|     uint32_t result = false; | ||||
|     with_view_model( | ||||
|         tune_view->view, (LfRfidTuneViewModel * model) { | ||||
|             result = model->CCR; | ||||
|             return false; | ||||
|         }); | ||||
|         tune_view->view, LfRfidTuneViewModel * model, { result = model->CCR; }, false); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| @ -159,21 +159,20 @@ static int32_t uart_echo_worker(void* context) { | ||||
|                 if(length > 0) { | ||||
|                     furi_hal_uart_tx(FuriHalUartIdUSART1, data, length); | ||||
|                     with_view_model( | ||||
|                         app->view, (UartDumpModel * model) { | ||||
|                         app->view, | ||||
|                         UartDumpModel * model, | ||||
|                         { | ||||
|                             for(size_t i = 0; i < length; i++) { | ||||
|                                 uart_echo_push_to_list(model, data[i]); | ||||
|                             } | ||||
|                             return false; | ||||
|                         }); | ||||
|                         }, | ||||
|                         false); | ||||
|                 } | ||||
|             } while(length > 0); | ||||
| 
 | ||||
|             notification_message(app->notification, &sequence_notification); | ||||
|             with_view_model( | ||||
|                 app->view, (UartDumpModel * model) { | ||||
|                     UNUSED(model); | ||||
|                     return true; | ||||
|                 }); | ||||
|                 app->view, UartDumpModel * model, { UNUSED(model); }, true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -200,15 +199,17 @@ static UartEchoApp* uart_echo_app_alloc() { | ||||
|     view_set_input_callback(app->view, uart_echo_view_input_callback); | ||||
|     view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel)); | ||||
|     with_view_model( | ||||
|         app->view, (UartDumpModel * model) { | ||||
|         app->view, | ||||
|         UartDumpModel * model, | ||||
|         { | ||||
|             for(size_t i = 0; i < LINES_ON_SCREEN; i++) { | ||||
|                 model->line = 0; | ||||
|                 model->escape = false; | ||||
|                 model->list[i] = malloc(sizeof(ListElement)); | ||||
|                 model->list[i]->text = furi_string_alloc(); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     view_set_previous_callback(app->view, uart_echo_exit); | ||||
|     view_dispatcher_add_view(app->view_dispatcher, 0, app->view); | ||||
| @ -242,13 +243,15 @@ static void uart_echo_app_free(UartEchoApp* app) { | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, 0); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         app->view, (UartDumpModel * model) { | ||||
|         app->view, | ||||
|         UartDumpModel * model, | ||||
|         { | ||||
|             for(size_t i = 0; i < LINES_ON_SCREEN; i++) { | ||||
|                 furi_string_free(model->list[i]->text); | ||||
|                 free(model->list[i]); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     view_free(app->view); | ||||
|     view_dispatcher_free(app->view_dispatcher); | ||||
| 
 | ||||
|  | ||||
| @ -21,7 +21,9 @@ static void | ||||
|         archive_switch_tab(browser, browser->last_tab_switch_dir); | ||||
|     } else if(!furi_string_start_with_str(browser->path, "/app:")) { | ||||
|         with_view_model( | ||||
|             browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             browser->view, | ||||
|             ArchiveBrowserViewModel * model, | ||||
|             { | ||||
|                 files_array_reset(model->files); | ||||
|                 model->item_cnt = item_cnt; | ||||
|                 model->item_idx = (file_idx > 0) ? file_idx : 0; | ||||
| @ -31,8 +33,8 @@ static void | ||||
|                 model->list_offset = 0; | ||||
|                 model->list_loading = true; | ||||
|                 model->folder_loading = false; | ||||
|                 return false; | ||||
|             }); | ||||
|             }, | ||||
|             false); | ||||
|         archive_update_offset(browser); | ||||
| 
 | ||||
|         file_browser_worker_load(browser->worker, load_offset, FILE_LIST_BUF_LEN); | ||||
| @ -44,11 +46,13 @@ static void archive_list_load_cb(void* context, uint32_t list_load_offset) { | ||||
|     ArchiveBrowserView* browser = (ArchiveBrowserView*)context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             files_array_reset(model->files); | ||||
|             model->array_offset = list_load_offset; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| @ -60,10 +64,7 @@ static void | ||||
|         archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path)); | ||||
|     } else { | ||||
|         with_view_model( | ||||
|             browser->view, (ArchiveBrowserViewModel * model) { | ||||
|                 model->list_loading = false; | ||||
|                 return true; | ||||
|             }); | ||||
|             browser->view, ArchiveBrowserViewModel * model, { model->list_loading = false; }, true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -72,10 +73,7 @@ static void archive_long_load_cb(void* context) { | ||||
|     ArchiveBrowserView* browser = (ArchiveBrowserView*)context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             model->folder_loading = true; | ||||
|             return true; | ||||
|         }); | ||||
|         browser->view, ArchiveBrowserViewModel * model, { model->folder_loading = true; }, true); | ||||
| } | ||||
| 
 | ||||
| static void archive_file_browser_set_path( | ||||
| @ -113,7 +111,9 @@ void archive_update_offset(ArchiveBrowserView* browser) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt; | ||||
| 
 | ||||
|             if((model->item_cnt > 3u) && (model->item_idx >= ((int32_t)model->item_cnt - 1))) { | ||||
| @ -125,9 +125,8 @@ void archive_update_offset(ArchiveBrowserView* browser) { | ||||
|                 model->list_offset = | ||||
|                     CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void archive_update_focus(ArchiveBrowserView* browser, const char* target) { | ||||
| @ -140,7 +139,9 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { | ||||
|         archive_switch_tab(browser, TAB_RIGHT); | ||||
|     } else { | ||||
|         with_view_model( | ||||
|             browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             browser->view, | ||||
|             ArchiveBrowserViewModel * model, | ||||
|             { | ||||
|                 uint16_t idx = 0; | ||||
|                 while(idx < files_array_size(model->files)) { | ||||
|                     ArchiveFile_t* current = files_array_get(model->files, idx); | ||||
| @ -150,8 +151,8 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { | ||||
|                     } | ||||
|                     ++idx; | ||||
|                 } | ||||
|                 return false; | ||||
|             }); | ||||
|             }, | ||||
|             false); | ||||
| 
 | ||||
|         archive_update_offset(browser); | ||||
|     } | ||||
| @ -162,10 +163,10 @@ size_t archive_file_get_array_size(ArchiveBrowserView* browser) { | ||||
| 
 | ||||
|     uint16_t size = 0; | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             size = files_array_size(model->files); | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { size = files_array_size(model->files); }, | ||||
|         false); | ||||
|     return size; | ||||
| } | ||||
| 
 | ||||
| @ -173,11 +174,13 @@ void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             model->item_cnt = count; | ||||
|             model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     archive_update_offset(browser); | ||||
| } | ||||
| 
 | ||||
| @ -186,7 +189,9 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) { | ||||
|     uint32_t items_cnt = 0; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             files_array_remove_v( | ||||
|                 model->files, | ||||
|                 model->item_idx - model->array_offset, | ||||
| @ -194,8 +199,8 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) { | ||||
|             model->item_cnt--; | ||||
|             model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); | ||||
|             items_cnt = model->item_cnt; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     if((items_cnt == 0) && (archive_is_home(browser))) { | ||||
|         archive_switch_tab(browser, TAB_RIGHT); | ||||
| @ -208,7 +213,9 @@ void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             ArchiveFile_t temp; | ||||
|             size_t array_size = files_array_size(model->files) - 1; | ||||
|             uint8_t swap_idx = CLAMP((size_t)(model->item_idx + dir), array_size, 0u); | ||||
| @ -226,18 +233,18 @@ void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) { | ||||
|             } else { | ||||
|                 files_array_swap_at(model->files, model->item_idx, swap_idx); | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void archive_file_array_rm_all(ArchiveBrowserView* browser) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             files_array_reset(model->files); | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { files_array_reset(model->files); }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { | ||||
| @ -246,7 +253,9 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { | ||||
|     int32_t offset_new = 0; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             if(model->item_cnt > FILE_LIST_BUF_LEN) { | ||||
|                 if(dir < 0) { | ||||
|                     offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 3; | ||||
| @ -262,8 +271,8 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { | ||||
|                     offset_new = 0; | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     file_browser_worker_load(browser->worker, offset_new, FILE_LIST_BUF_LEN); | ||||
| } | ||||
| @ -273,12 +282,14 @@ ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { | ||||
| 
 | ||||
|     ArchiveFile_t* selected = NULL; | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             selected = files_array_size(model->files) ? | ||||
|                            files_array_get(model->files, model->item_idx - model->array_offset) : | ||||
|                            NULL; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     return selected; | ||||
| } | ||||
| 
 | ||||
| @ -288,11 +299,13 @@ ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) { | ||||
|     ArchiveFile_t* selected = NULL; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             idx = CLAMP(idx - model->array_offset, files_array_size(model->files), 0u); | ||||
|             selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     return selected; | ||||
| } | ||||
| 
 | ||||
| @ -301,10 +314,7 @@ ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { | ||||
| 
 | ||||
|     ArchiveTabEnum tab_id = 0; | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             tab_id = model->tab_idx; | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, ArchiveBrowserViewModel * model, { tab_id = model->tab_idx; }, false); | ||||
|     return tab_id; | ||||
| } | ||||
| 
 | ||||
| @ -328,10 +338,7 @@ void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             model->tab_idx = tab; | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, ArchiveBrowserViewModel * model, { model->tab_idx = tab; }, false); | ||||
| } | ||||
| 
 | ||||
| void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { | ||||
| @ -344,11 +351,13 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { | ||||
|     archive_set_file_type(&item, name, false, true); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             files_array_push_back(model->files, item); | ||||
|             model->item_cnt = files_array_size(model->files); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     ArchiveFile_t_clear(&item); | ||||
| } | ||||
| 
 | ||||
| @ -379,17 +388,19 @@ void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const ch | ||||
|         } | ||||
|     } | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             files_array_push_back(model->files, item); | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { files_array_push_back(model->files, item); }, | ||||
|         false); | ||||
|     ArchiveFile_t_clear(&item); | ||||
| } | ||||
| 
 | ||||
| void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { | ||||
|     furi_assert(browser); | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             if(show) { | ||||
|                 if(archive_is_item_in_array(model, model->item_idx)) { | ||||
|                     model->menu = true; | ||||
| @ -403,19 +414,15 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { | ||||
|                 model->menu = false; | ||||
|                 model->menu_idx = 0; | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             model->move_fav = active; | ||||
|             return true; | ||||
|         }); | ||||
|         browser->view, ArchiveBrowserViewModel * model, { model->move_fav = active; }, true); | ||||
| } | ||||
| 
 | ||||
| static bool archive_is_dir_exists(FuriString* path) { | ||||
| @ -476,11 +483,13 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { | ||||
|         archive_switch_tab(browser, key); | ||||
|     } else { | ||||
|         with_view_model( | ||||
|             browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             browser->view, | ||||
|             ArchiveBrowserViewModel * model, | ||||
|             { | ||||
|                 model->item_idx = 0; | ||||
|                 model->array_offset = 0; | ||||
|                 return false; | ||||
|             }); | ||||
|             }, | ||||
|             false); | ||||
|         archive_get_items(browser, furi_string_get_cstr(browser->path)); | ||||
|         archive_update_offset(browser); | ||||
|     } | ||||
| @ -493,10 +502,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) { | ||||
|     int32_t idx_temp = 0; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             idx_temp = model->item_idx; | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); | ||||
| 
 | ||||
|     furi_string_set(browser->path, path); | ||||
|     file_browser_worker_folder_enter(browser->worker, path, idx_temp); | ||||
| @ -514,9 +520,6 @@ void archive_refresh_dir(ArchiveBrowserView* browser) { | ||||
|     int32_t idx_temp = 0; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             idx_temp = model->item_idx; | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); | ||||
|     file_browser_worker_folder_refresh(browser->worker, idx_temp); | ||||
| } | ||||
|  | ||||
| @ -263,33 +263,37 @@ static bool archive_view_input(InputEvent* event, void* context) { | ||||
|     bool in_menu; | ||||
|     bool move_fav_mode; | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             in_menu = model->menu; | ||||
|             move_fav_mode = model->move_fav; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     if(in_menu) { | ||||
|         if(event->type == InputTypeShort) { | ||||
|             if(event->key == InputKeyUp || event->key == InputKeyDown) { | ||||
|                 with_view_model( | ||||
|                     browser->view, (ArchiveBrowserViewModel * model) { | ||||
|                     browser->view, | ||||
|                     ArchiveBrowserViewModel * model, | ||||
|                     { | ||||
|                         if(event->key == InputKeyUp) { | ||||
|                             model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; | ||||
|                         } else if(event->key == InputKeyDown) { | ||||
|                             model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; | ||||
|                         } | ||||
|                         return true; | ||||
|                     }); | ||||
|                     }, | ||||
|                     true); | ||||
|             } | ||||
| 
 | ||||
|             if(event->key == InputKeyOk) { | ||||
|                 uint8_t idx; | ||||
|                 with_view_model( | ||||
|                     browser->view, (ArchiveBrowserViewModel * model) { | ||||
|                         idx = model->menu_idx; | ||||
|                         return false; | ||||
|                     }); | ||||
|                     browser->view, | ||||
|                     ArchiveBrowserViewModel * model, | ||||
|                     { idx = model->menu_idx; }, | ||||
|                     false); | ||||
|                 browser->callback(file_menu_actions[idx], browser->context); | ||||
|             } else if(event->key == InputKeyBack) { | ||||
|                 browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); | ||||
| @ -313,7 +317,9 @@ static bool archive_view_input(InputEvent* event, void* context) { | ||||
|         if((event->key == InputKeyUp || event->key == InputKeyDown) && | ||||
|            (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||
|             with_view_model( | ||||
|                 browser->view, (ArchiveBrowserViewModel * model) { | ||||
|                 browser->view, | ||||
|                 ArchiveBrowserViewModel * model, | ||||
|                 { | ||||
|                     if(event->key == InputKeyUp) { | ||||
|                         model->item_idx = | ||||
|                             ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; | ||||
| @ -334,9 +340,8 @@ static bool archive_view_input(InputEvent* event, void* context) { | ||||
|                             browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     return true; | ||||
|                 }); | ||||
|                 }, | ||||
|                 true); | ||||
|             archive_update_offset(browser); | ||||
|         } | ||||
| 
 | ||||
| @ -384,11 +389,13 @@ ArchiveBrowserView* browser_alloc() { | ||||
|     browser->path = furi_string_alloc_set(archive_get_default_path(TAB_DEFAULT)); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { | ||||
|             files_array_init(model->files); | ||||
|             model->tab_idx = TAB_DEFAULT; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return browser; | ||||
| } | ||||
| @ -401,10 +408,10 @@ void browser_free(ArchiveBrowserView* browser) { | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             files_array_clear(model->files); | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, | ||||
|         ArchiveBrowserViewModel * model, | ||||
|         { files_array_clear(model->files); }, | ||||
|         false); | ||||
| 
 | ||||
|     furi_string_free(browser->path); | ||||
| 
 | ||||
|  | ||||
| @ -145,29 +145,33 @@ void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* c | ||||
|     furi_assert(bad_usb); | ||||
|     furi_assert(callback); | ||||
|     with_view_model( | ||||
|         bad_usb->view, (BadUsbModel * model) { | ||||
|         bad_usb->view, | ||||
|         BadUsbModel * model, | ||||
|         { | ||||
|             UNUSED(model); | ||||
|             bad_usb->callback = callback; | ||||
|             bad_usb->context = context; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { | ||||
|     furi_assert(name); | ||||
|     with_view_model( | ||||
|         bad_usb->view, (BadUsbModel * model) { | ||||
|             strlcpy(model->file_name, name, MAX_NAME_LEN); | ||||
|             return true; | ||||
|         }); | ||||
|         bad_usb->view, | ||||
|         BadUsbModel * model, | ||||
|         { strlcpy(model->file_name, name, MAX_NAME_LEN); }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { | ||||
|     furi_assert(st); | ||||
|     with_view_model( | ||||
|         bad_usb->view, (BadUsbModel * model) { | ||||
|         bad_usb->view, | ||||
|         BadUsbModel * model, | ||||
|         { | ||||
|             memcpy(&(model->state), st, sizeof(BadUsbState)); | ||||
|             model->anim_frame ^= 1; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -48,23 +48,27 @@ static bool gpio_test_input_callback(InputEvent* event, void* context) { | ||||
| 
 | ||||
| static bool gpio_test_process_left(GpioTest* gpio_test) { | ||||
|     with_view_model( | ||||
|         gpio_test->view, (GpioTestModel * model) { | ||||
|         gpio_test->view, | ||||
|         GpioTestModel * model, | ||||
|         { | ||||
|             if(model->pin_idx) { | ||||
|                 model->pin_idx--; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool gpio_test_process_right(GpioTest* gpio_test) { | ||||
|     with_view_model( | ||||
|         gpio_test->view, (GpioTestModel * model) { | ||||
|         gpio_test->view, | ||||
|         GpioTestModel * model, | ||||
|         { | ||||
|             if(model->pin_idx < GPIO_ITEM_COUNT) { | ||||
|                 model->pin_idx++; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| @ -72,7 +76,9 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         gpio_test->view, (GpioTestModel * model) { | ||||
|         gpio_test->view, | ||||
|         GpioTestModel * model, | ||||
|         { | ||||
|             if(event->type == InputTypePress) { | ||||
|                 if(model->pin_idx < GPIO_ITEM_COUNT) { | ||||
|                     gpio_item_set_pin(model->pin_idx, true); | ||||
| @ -89,8 +95,8 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { | ||||
|                 consumed = true; | ||||
|             } | ||||
|             gpio_test->callback(event->type, gpio_test->context); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| @ -122,10 +128,12 @@ void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, | ||||
|     furi_assert(gpio_test); | ||||
|     furi_assert(callback); | ||||
|     with_view_model( | ||||
|         gpio_test->view, (GpioTestModel * model) { | ||||
|         gpio_test->view, | ||||
|         GpioTestModel * model, | ||||
|         { | ||||
|             UNUSED(model); | ||||
|             gpio_test->callback = callback; | ||||
|             gpio_test->context = context; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
|  | ||||
| @ -129,12 +129,14 @@ void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callb | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         usb_uart->view, (GpioUsbUartModel * model) { | ||||
|         usb_uart->view, | ||||
|         GpioUsbUartModel * model, | ||||
|         { | ||||
|             UNUSED(model); | ||||
|             usb_uart->callback = callback; | ||||
|             usb_uart->context = context; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st) { | ||||
| @ -143,7 +145,9 @@ void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUa | ||||
|     furi_assert(st); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (GpioUsbUartModel * model) { | ||||
|         instance->view, | ||||
|         GpioUsbUartModel * model, | ||||
|         { | ||||
|             model->baudrate = st->baudrate_cur; | ||||
|             model->vcp_port = cfg->vcp_ch; | ||||
|             model->tx_pin = (cfg->uart_ch == 0) ? (13) : (15); | ||||
| @ -152,6 +156,6 @@ void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUa | ||||
|             model->rx_active = (model->rx_cnt != st->rx_cnt); | ||||
|             model->tx_cnt = st->tx_cnt; | ||||
|             model->rx_cnt = st->rx_cnt; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -56,19 +56,13 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { | ||||
| void lfrfid_view_read_enter(void* context) { | ||||
|     LfRfidReadView* read_view = context; | ||||
|     with_view_model( | ||||
|         read_view->view, (LfRfidReadViewModel * model) { | ||||
|             icon_animation_start(model->icon); | ||||
|             return true; | ||||
|         }); | ||||
|         read_view->view, LfRfidReadViewModel * model, { icon_animation_start(model->icon); }, true); | ||||
| } | ||||
| 
 | ||||
| void lfrfid_view_read_exit(void* context) { | ||||
|     LfRfidReadView* read_view = context; | ||||
|     with_view_model( | ||||
|         read_view->view, (LfRfidReadViewModel * model) { | ||||
|             icon_animation_stop(model->icon); | ||||
|             return false; | ||||
|         }); | ||||
|         read_view->view, LfRfidReadViewModel * model, { icon_animation_stop(model->icon); }, false); | ||||
| } | ||||
| 
 | ||||
| LfRfidReadView* lfrfid_view_read_alloc() { | ||||
| @ -78,11 +72,13 @@ LfRfidReadView* lfrfid_view_read_alloc() { | ||||
|     view_allocate_model(read_view->view, ViewModelTypeLocking, sizeof(LfRfidReadViewModel)); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         read_view->view, (LfRfidReadViewModel * model) { | ||||
|         read_view->view, | ||||
|         LfRfidReadViewModel * model, | ||||
|         { | ||||
|             model->icon = icon_animation_alloc(&A_Round_loader_8x8); | ||||
|             view_tie_icon_animation(read_view->view, model->icon); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     view_set_draw_callback(read_view->view, lfrfid_view_read_draw_callback); | ||||
|     view_set_enter_callback(read_view->view, lfrfid_view_read_enter); | ||||
| @ -93,10 +89,7 @@ LfRfidReadView* lfrfid_view_read_alloc() { | ||||
| 
 | ||||
| void lfrfid_view_read_free(LfRfidReadView* read_view) { | ||||
|     with_view_model( | ||||
|         read_view->view, (LfRfidReadViewModel * model) { | ||||
|             icon_animation_free(model->icon); | ||||
|             return false; | ||||
|         }); | ||||
|         read_view->view, LfRfidReadViewModel * model, { icon_animation_free(model->icon); }, false); | ||||
| 
 | ||||
|     view_free(read_view->view); | ||||
|     free(read_view); | ||||
| @ -108,10 +101,12 @@ View* lfrfid_view_read_get_view(LfRfidReadView* read_view) { | ||||
| 
 | ||||
| void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode) { | ||||
|     with_view_model( | ||||
|         read_view->view, (LfRfidReadViewModel * model) { | ||||
|         read_view->view, | ||||
|         LfRfidReadViewModel * model, | ||||
|         { | ||||
|             icon_animation_stop(model->icon); | ||||
|             icon_animation_start(model->icon); | ||||
|             model->read_mode = mode; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -64,10 +64,7 @@ static bool detect_reader_input_callback(InputEvent* event, void* context) { | ||||
| 
 | ||||
|     uint8_t nonces = 0; | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|             nonces = model->nonces; | ||||
|             return false; | ||||
|         }); | ||||
|         detect_reader->view, DetectReaderViewModel * model, { nonces = model->nonces; }, false); | ||||
| 
 | ||||
|     if(event->type == InputTypeShort) { | ||||
|         if(event->key == InputKeyOk) { | ||||
| @ -103,12 +100,14 @@ void detect_reader_reset(DetectReader* detect_reader) { | ||||
|     furi_assert(detect_reader); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|         detect_reader->view, | ||||
|         DetectReaderViewModel * model, | ||||
|         { | ||||
|             model->nonces = 0; | ||||
|             model->nonces_max = 0; | ||||
|             model->state = DetectReaderStateStart; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| View* detect_reader_get_view(DetectReader* detect_reader) { | ||||
| @ -132,27 +131,24 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m | ||||
|     furi_assert(detect_reader); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|             model->nonces_max = nonces_max; | ||||
|             return false; | ||||
|         }); | ||||
|         detect_reader->view, | ||||
|         DetectReaderViewModel * model, | ||||
|         { model->nonces_max = nonces_max; }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected) { | ||||
|     furi_assert(detect_reader); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|             model->nonces = nonces_collected; | ||||
|             return false; | ||||
|         }); | ||||
|         detect_reader->view, | ||||
|         DetectReaderViewModel * model, | ||||
|         { model->nonces = nonces_collected; }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state) { | ||||
|     furi_assert(detect_reader); | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|             model->state = state; | ||||
|             return true; | ||||
|         }); | ||||
|         detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true); | ||||
| } | ||||
|  | ||||
| @ -80,20 +80,20 @@ DictAttack* dict_attack_alloc() { | ||||
|     view_set_input_callback(dict_attack->view, dict_attack_input_callback); | ||||
|     view_set_context(dict_attack->view, dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->header = furi_string_alloc(); | ||||
|             return false; | ||||
|         }); | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { model->header = furi_string_alloc(); }, | ||||
|         false); | ||||
|     return dict_attack; | ||||
| } | ||||
| 
 | ||||
| void dict_attack_free(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             furi_string_free(model->header); | ||||
|             return false; | ||||
|         }); | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { furi_string_free(model->header); }, | ||||
|         false); | ||||
|     view_free(dict_attack->view); | ||||
|     free(dict_attack); | ||||
| } | ||||
| @ -101,7 +101,9 @@ void dict_attack_free(DictAttack* dict_attack) { | ||||
| void dict_attack_reset(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { | ||||
|             model->state = DictAttackStateRead; | ||||
|             model->type = MfClassicType1k; | ||||
|             model->sectors_total = 0; | ||||
| @ -112,8 +114,8 @@ void dict_attack_reset(DictAttack* dict_attack) { | ||||
|             model->dict_keys_total = 0; | ||||
|             model->dict_keys_current = 0; | ||||
|             furi_string_reset(model->header); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| View* dict_attack_get_view(DictAttack* dict_attack) { | ||||
| @ -133,99 +135,103 @@ void dict_attack_set_header(DictAttack* dict_attack, const char* header) { | ||||
|     furi_assert(header); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             furi_string_set(model->header, header); | ||||
|             return true; | ||||
|         }); | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { furi_string_set(model->header, header); }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { | ||||
|             model->state = DictAttackStateRead; | ||||
|             model->sectors_total = mf_classic_get_total_sectors_num(type); | ||||
|             model->keys_total = model->sectors_total * 2; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_card_removed(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->state = DictAttackStateCardRemoved; | ||||
|             return true; | ||||
|         }); | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { model->state = DictAttackStateCardRemoved; }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->sectors_read = sec_read; | ||||
|             return true; | ||||
|         }); | ||||
|         dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->keys_found = keys_found; | ||||
|             return true; | ||||
|         }); | ||||
|         dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { | ||||
|             model->sector_current = curr_sec; | ||||
|             model->dict_keys_current = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_inc_current_sector(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { | ||||
|             if(model->sector_current < model->sectors_total) { | ||||
|                 model->sector_current++; | ||||
|                 model->dict_keys_current = 0; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_inc_keys_found(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { | ||||
|             if(model->keys_found < model->keys_total) { | ||||
|                 model->keys_found++; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->dict_keys_total = dict_keys_total; | ||||
|             return true; | ||||
|         }); | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { model->dict_keys_total = dict_keys_total; }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|         dict_attack->view, | ||||
|         DictAttackViewModel * model, | ||||
|         { | ||||
|             if(model->dict_keys_current + keys_tried < model->dict_keys_total) { | ||||
|                 model->dict_keys_current += keys_tried; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -67,17 +67,17 @@ void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLo | ||||
|     if(lock == SubGhzLockOn) { | ||||
|         subghz_receiver->lock = lock; | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|                 model->bar_show = SubGhzViewReceiverBarShowLock; | ||||
|                 return true; | ||||
|             }); | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
|             { model->bar_show = SubGhzViewReceiverBarShowLock; }, | ||||
|             true); | ||||
|         furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000)); | ||||
|     } else { | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|                 model->bar_show = SubGhzViewReceiverBarShowDefault; | ||||
|                 return true; | ||||
|             }); | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
|             { model->bar_show = SubGhzViewReceiverBarShowDefault; }, | ||||
|             true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -95,7 +95,9 @@ static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiv | ||||
|     furi_assert(subghz_receiver); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             size_t history_item = model->history_item; | ||||
|             uint16_t bounds = history_item > 3 ? 2 : history_item; | ||||
| 
 | ||||
| @ -107,8 +109,8 @@ static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiv | ||||
|             } else if(model->list_offset > model->idx - bounds) { | ||||
|                 model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void subghz_view_receiver_add_item_to_menu( | ||||
| @ -117,7 +119,9 @@ void subghz_view_receiver_add_item_to_menu( | ||||
|     uint8_t type) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             SubGhzReceiverMenuItem* item_menu = | ||||
|                 SubGhzReceiverMenuItemArray_push_raw(model->history->data); | ||||
|             item_menu->item_str = furi_string_alloc_set(name); | ||||
| @ -128,9 +132,8 @@ void subghz_view_receiver_add_item_to_menu( | ||||
|             } else { | ||||
|                 model->history_item++; | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     subghz_view_receiver_update_offset(subghz_receiver); | ||||
| } | ||||
| 
 | ||||
| @ -141,12 +144,14 @@ void subghz_view_receiver_add_data_statusbar( | ||||
|     const char* history_stat_str) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             furi_string_set(model->frequency_str, frequency_str); | ||||
|             furi_string_set(model->preset_str, preset_str); | ||||
|             furi_string_set(model->history_stat_str, history_stat_str); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { | ||||
| @ -240,10 +245,10 @@ static void subghz_view_receiver_timer_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhzViewReceiver* subghz_receiver = context; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|             model->bar_show = SubGhzViewReceiverBarShowDefault; | ||||
|             return true; | ||||
|         }); | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { model->bar_show = SubGhzViewReceiverBarShowDefault; }, | ||||
|         true); | ||||
|     if(subghz_receiver->lock_count < UNLOCK_CNT) { | ||||
|         subghz_receiver->callback( | ||||
|             SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); | ||||
| @ -260,10 +265,10 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | ||||
| 
 | ||||
|     if(subghz_receiver->lock == SubGhzLockOn) { | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|                 model->bar_show = SubGhzViewReceiverBarShowToUnlockPress; | ||||
|                 return true; | ||||
|             }); | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
|             { model->bar_show = SubGhzViewReceiverBarShowToUnlockPress; }, | ||||
|             true); | ||||
|         if(subghz_receiver->lock_count == 0) { | ||||
|             furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000)); | ||||
|         } | ||||
| @ -274,10 +279,10 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | ||||
|             // subghz_receiver->callback(
 | ||||
|             //     SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
 | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|                     model->bar_show = SubGhzViewReceiverBarShowUnlock; | ||||
|                     return true; | ||||
|                 }); | ||||
|                 subghz_receiver->view, | ||||
|                 SubGhzViewReceiverModel * model, | ||||
|                 { model->bar_show = SubGhzViewReceiverBarShowUnlock; }, | ||||
|                 true); | ||||
|             //subghz_receiver->lock = SubGhzLockOff;
 | ||||
|             furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650)); | ||||
|         } | ||||
| @ -291,29 +296,35 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | ||||
|         event->key == InputKeyUp && | ||||
|         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
|             { | ||||
|                 if(model->idx != 0) model->idx--; | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|     } else if( | ||||
|         event->key == InputKeyDown && | ||||
|         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
|             { | ||||
|                 if(model->idx != model->history_item - 1) model->idx++; | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { | ||||
|         subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context); | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
|             { | ||||
|                 if(model->history_item != 0) { | ||||
|                     subghz_receiver->callback( | ||||
|                         SubGhzCustomEventViewReceiverOK, subghz_receiver->context); | ||||
|                 } | ||||
|                 return false; | ||||
|             }); | ||||
|             }, | ||||
|             false); | ||||
|     } | ||||
| 
 | ||||
|     subghz_view_receiver_update_offset(subghz_receiver); | ||||
| @ -329,7 +340,9 @@ void subghz_view_receiver_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhzViewReceiver* subghz_receiver = context; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             furi_string_reset(model->frequency_str); | ||||
|             furi_string_reset(model->preset_str); | ||||
|             furi_string_reset(model->history_stat_str); | ||||
| @ -342,8 +355,8 @@ void subghz_view_receiver_exit(void* context) { | ||||
|                 model->idx = 0; | ||||
|                 model->list_offset = 0; | ||||
|                 model->history_item = 0; | ||||
|                 return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     furi_timer_stop(subghz_receiver->timer); | ||||
| } | ||||
| 
 | ||||
| @ -364,15 +377,17 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { | ||||
|     view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             model->frequency_str = furi_string_alloc(); | ||||
|             model->preset_str = furi_string_alloc(); | ||||
|             model->history_stat_str = furi_string_alloc(); | ||||
|             model->bar_show = SubGhzViewReceiverBarShowDefault; | ||||
|             model->history = malloc(sizeof(SubGhzReceiverHistory)); | ||||
|             SubGhzReceiverMenuItemArray_init(model->history->data); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     subghz_receiver->timer = | ||||
|         furi_timer_alloc(subghz_view_receiver_timer_callback, FuriTimerTypeOnce, subghz_receiver); | ||||
|     return subghz_receiver; | ||||
| @ -382,7 +397,9 @@ void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { | ||||
|     furi_assert(subghz_receiver); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             furi_string_free(model->frequency_str); | ||||
|             furi_string_free(model->preset_str); | ||||
|             furi_string_free(model->history_stat_str); | ||||
| @ -393,8 +410,8 @@ void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { | ||||
|                     } | ||||
|                 SubGhzReceiverMenuItemArray_clear(model->history->data); | ||||
|                 free(model->history); | ||||
|                 return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     furi_timer_free(subghz_receiver->timer); | ||||
|     view_free(subghz_receiver->view); | ||||
|     free(subghz_receiver); | ||||
| @ -409,20 +426,19 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) | ||||
|     furi_assert(subghz_receiver); | ||||
|     uint32_t idx = 0; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|             idx = model->idx; | ||||
|             return false; | ||||
|         }); | ||||
|         subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false); | ||||
|     return idx; | ||||
| } | ||||
| 
 | ||||
| void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||
|         subghz_receiver->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             model->idx = idx; | ||||
|             if(model->idx > 2) model->list_offset = idx - 2; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     subghz_view_receiver_update_offset(subghz_receiver); | ||||
| } | ||||
|  | ||||
| @ -132,12 +132,14 @@ void subghz_frequency_analyzer_pair_callback( | ||||
|         } | ||||
|         //update history
 | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzFrequencyAnalyzerModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzFrequencyAnalyzerModel * model, | ||||
|             { | ||||
|                 model->history_frequency[2] = model->history_frequency[1]; | ||||
|                 model->history_frequency[1] = model->history_frequency[0]; | ||||
|                 model->history_frequency[0] = model->frequency; | ||||
|                 return false; | ||||
|             }); | ||||
|             }, | ||||
|             false); | ||||
|     } else if((rssi != 0.f) && (!instance->locked)) { | ||||
|         if(instance->callback) { | ||||
|             instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); | ||||
| @ -146,12 +148,14 @@ void subghz_frequency_analyzer_pair_callback( | ||||
| 
 | ||||
|     instance->locked = (rssi != 0.f); | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzFrequencyAnalyzerModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzFrequencyAnalyzerModel * model, | ||||
|         { | ||||
|             model->rssi = rssi; | ||||
|             model->frequency = frequency; | ||||
|             model->signal = signal; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void subghz_frequency_analyzer_enter(void* context) { | ||||
| @ -169,14 +173,16 @@ void subghz_frequency_analyzer_enter(void* context) { | ||||
|     subghz_frequency_analyzer_worker_start(instance->worker); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzFrequencyAnalyzerModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzFrequencyAnalyzerModel * model, | ||||
|         { | ||||
|             model->rssi = 0; | ||||
|             model->frequency = 0; | ||||
|             model->history_frequency[2] = 0; | ||||
|             model->history_frequency[1] = 0; | ||||
|             model->history_frequency[0] = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void subghz_frequency_analyzer_exit(void* context) { | ||||
| @ -190,10 +196,7 @@ void subghz_frequency_analyzer_exit(void* context) { | ||||
|     subghz_frequency_analyzer_worker_free(instance->worker); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzFrequencyAnalyzerModel * model) { | ||||
|             model->rssi = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         instance->view, SubGhzFrequencyAnalyzerModel * model, { model->rssi = 0; }, true); | ||||
| } | ||||
| 
 | ||||
| SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { | ||||
| @ -210,10 +213,7 @@ SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { | ||||
|     view_set_exit_callback(instance->view, subghz_frequency_analyzer_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzFrequencyAnalyzerModel * model) { | ||||
|             model->rssi = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         instance->view, SubGhzFrequencyAnalyzerModel * model, { model->rssi = 0; }, true); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
|  | ||||
| @ -45,11 +45,13 @@ void subghz_read_raw_add_data_statusbar( | ||||
|     const char* preset_str) { | ||||
|     furi_assert(instance); | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { | ||||
|             furi_string_set(model->frequency_str, frequency_str); | ||||
|             furi_string_set(model->preset_str, preset_str); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi) { | ||||
| @ -63,31 +65,35 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi) { | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { | ||||
|             model->rssi_history[model->ind_write++] = u_rssi; | ||||
|             if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) { | ||||
|                 model->rssi_history_end = true; | ||||
|                 model->ind_write = 0; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|             furi_string_printf(model->sample_write, "%d spl.", sample); | ||||
|             return false; | ||||
|         }); | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { furi_string_printf(model->sample_write, "%d spl.", sample); }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void subghz_read_raw_stop_send(SubGhzReadRAW* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { | ||||
|             switch(model->status) { | ||||
|             case SubGhzReadRAWStatusTXRepeat: | ||||
|             case SubGhzReadRAWStatusLoadKeyTXRepeat: | ||||
| @ -105,19 +111,21 @@ void subghz_read_raw_stop_send(SubGhzReadRAW* instance) { | ||||
|                 model->status = SubGhzReadRAWStatusIDLE; | ||||
|                 break; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void subghz_read_raw_update_sin(SubGhzReadRAW* instance) { | ||||
|     furi_assert(instance); | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { | ||||
|             if(model->ind_sin++ > 62) { | ||||
|                 model->ind_sin = 0; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static int8_t subghz_read_raw_tab_sin(uint8_t x) { | ||||
| @ -286,9 +294,11 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | ||||
|         //further check of events is not needed, we exit
 | ||||
|         return false; | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypePress) { | ||||
|         uint8_t ret = false; | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|                 uint8_t ret = false; | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 switch(model->status) { | ||||
|                 case SubGhzReadRAWStatusIDLE: | ||||
|                     // Start TX
 | ||||
| @ -314,11 +324,13 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|                 return ret; | ||||
|             }); | ||||
|             }, | ||||
|             ret); | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeRelease) { | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 if(model->status == SubGhzReadRAWStatusTXRepeat) { | ||||
|                     // Stop repeat TX
 | ||||
|                     model->status = SubGhzReadRAWStatusTX; | ||||
| @ -326,11 +338,13 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | ||||
|                     // Stop repeat TX
 | ||||
|                     model->status = SubGhzReadRAWStatusLoadKeyTX; | ||||
|                 } | ||||
|                 return false; | ||||
|             }); | ||||
|             }, | ||||
|             false); | ||||
|     } else if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 switch(model->status) { | ||||
|                 case SubGhzReadRAWStatusREC: | ||||
|                     //Stop REC
 | ||||
| @ -357,11 +371,13 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | ||||
|                     instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context); | ||||
|                     break; | ||||
|                 } | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 if(model->status == SubGhzReadRAWStatusStart) { | ||||
|                     //Config
 | ||||
|                     instance->callback(SubGhzCustomEventViewReadRAWConfig, instance->context); | ||||
| @ -376,11 +392,13 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | ||||
|                     furi_string_reset(model->file_name); | ||||
|                     instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context); | ||||
|                 } | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|     } else if(event->key == InputKeyRight && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 if(model->status == SubGhzReadRAWStatusIDLE) { | ||||
|                     //Save
 | ||||
|                     instance->callback(SubGhzCustomEventViewReadRAWSave, instance->context); | ||||
| @ -388,11 +406,13 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | ||||
|                     //More
 | ||||
|                     instance->callback(SubGhzCustomEventViewReadRAWMore, instance->context); | ||||
|                 } | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 if(model->status == SubGhzReadRAWStatusStart) { | ||||
|                     //Record
 | ||||
|                     instance->callback(SubGhzCustomEventViewReadRAWREC, instance->context); | ||||
| @ -404,8 +424,8 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | ||||
|                     instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); | ||||
|                     model->status = SubGhzReadRAWStatusIDLE; | ||||
|                 } | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| @ -419,36 +439,42 @@ void subghz_read_raw_set_status( | ||||
|     switch(status) { | ||||
|     case SubGhzReadRAWStatusStart: | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 model->status = SubGhzReadRAWStatusStart; | ||||
|                 model->rssi_history_end = false; | ||||
|                 model->ind_write = 0; | ||||
|                 furi_string_reset(model->file_name); | ||||
|                 furi_string_set(model->sample_write, "0 spl."); | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|         break; | ||||
|     case SubGhzReadRAWStatusIDLE: | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|                 model->status = SubGhzReadRAWStatusIDLE; | ||||
|                 return true; | ||||
|             }); | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { model->status = SubGhzReadRAWStatusIDLE; }, | ||||
|             true); | ||||
|         break; | ||||
|     case SubGhzReadRAWStatusLoadKeyTX: | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 model->status = SubGhzReadRAWStatusLoadKeyIDLE; | ||||
|                 model->rssi_history_end = false; | ||||
|                 model->ind_write = 0; | ||||
|                 furi_string_set(model->file_name, file_name); | ||||
|                 furi_string_set(model->sample_write, "RAW"); | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|         break; | ||||
|     case SubGhzReadRAWStatusSaveKey: | ||||
|         with_view_model( | ||||
|             instance->view, (SubGhzReadRAWModel * model) { | ||||
|             instance->view, | ||||
|             SubGhzReadRAWModel * model, | ||||
|             { | ||||
|                 model->status = SubGhzReadRAWStatusLoadKeyIDLE; | ||||
|                 if(!model->ind_write) { | ||||
|                     furi_string_set(model->file_name, file_name); | ||||
| @ -456,8 +482,8 @@ void subghz_read_raw_set_status( | ||||
|                 } else { | ||||
|                     furi_string_reset(model->file_name); | ||||
|                 } | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
| @ -476,15 +502,17 @@ void subghz_read_raw_exit(void* context) { | ||||
|     SubGhzReadRAW* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { | ||||
|             if(model->status != SubGhzReadRAWStatusIDLE && | ||||
|                model->status != SubGhzReadRAWStatusStart && | ||||
|                model->status != SubGhzReadRAWStatusLoadKeyIDLE) { | ||||
|                 instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); | ||||
|                 model->status = SubGhzReadRAWStatusStart; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| SubGhzReadRAW* subghz_read_raw_alloc() { | ||||
| @ -500,14 +528,16 @@ SubGhzReadRAW* subghz_read_raw_alloc() { | ||||
|     view_set_exit_callback(instance->view, subghz_read_raw_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { | ||||
|             model->frequency_str = furi_string_alloc(); | ||||
|             model->preset_str = furi_string_alloc(); | ||||
|             model->sample_write = furi_string_alloc(); | ||||
|             model->file_name = furi_string_alloc(); | ||||
|             model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t)); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| @ -516,14 +546,16 @@ void subghz_read_raw_free(SubGhzReadRAW* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzReadRAWModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzReadRAWModel * model, | ||||
|         { | ||||
|             furi_string_free(model->frequency_str); | ||||
|             furi_string_free(model->preset_str); | ||||
|             furi_string_free(model->sample_write); | ||||
|             furi_string_free(model->file_name); | ||||
|             free(model->rssi_history); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
|  | ||||
| @ -89,7 +89,9 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_test_carrier->view, (SubGhzTestCarrierModel * model) { | ||||
|         subghz_test_carrier->view, | ||||
|         SubGhzTestCarrierModel * model, | ||||
|         { | ||||
|             furi_hal_subghz_idle(); | ||||
| 
 | ||||
|             if(event->key == InputKeyLeft) { | ||||
| @ -125,9 +127,8 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { | ||||
|                         SubGhzTestCarrierEventOnlyRx, subghz_test_carrier->context); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| @ -142,15 +143,17 @@ void subghz_test_carrier_enter(void* context) { | ||||
|     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_test_carrier->view, (SubGhzTestCarrierModel * model) { | ||||
|         subghz_test_carrier->view, | ||||
|         SubGhzTestCarrierModel * model, | ||||
|         { | ||||
|             model->frequency = subghz_frequencies_433_92_testing; // 433
 | ||||
|             model->real_frequency = | ||||
|                 furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]); | ||||
|             model->path = FuriHalSubGhzPathIsolate; // isolate
 | ||||
|             model->rssi = 0.0f; | ||||
|             model->status = SubGhzTestCarrierModelStatusRx; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     furi_hal_subghz_rx(); | ||||
| 
 | ||||
| @ -172,13 +175,14 @@ void subghz_test_carrier_rssi_timer_callback(void* context) { | ||||
|     SubGhzTestCarrier* subghz_test_carrier = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_test_carrier->view, (SubGhzTestCarrierModel * model) { | ||||
|         subghz_test_carrier->view, | ||||
|         SubGhzTestCarrierModel * model, | ||||
|         { | ||||
|             if(model->status == SubGhzTestCarrierModelStatusRx) { | ||||
|                 model->rssi = furi_hal_subghz_get_rssi(); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| SubGhzTestCarrier* subghz_test_carrier_alloc() { | ||||
|  | ||||
| @ -68,7 +68,9 @@ static void subghz_test_packet_rssi_timer_callback(void* context) { | ||||
|     SubGhzTestPacket* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzTestPacketModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzTestPacketModel * model, | ||||
|         { | ||||
|             if(model->status == SubGhzTestPacketModelStatusRx) { | ||||
|                 model->rssi = furi_hal_subghz_get_rssi(); | ||||
|                 model->packets = instance->packet_rx; | ||||
| @ -77,8 +79,8 @@ static void subghz_test_packet_rssi_timer_callback(void* context) { | ||||
|                     SUBGHZ_TEST_PACKET_COUNT - | ||||
|                     subghz_encoder_princeton_for_testing_get_repeat_left(instance->encoder); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void subghz_test_packet_draw(Canvas* canvas, SubGhzTestPacketModel* model) { | ||||
| @ -137,7 +139,9 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzTestPacketModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzTestPacketModel * model, | ||||
|         { | ||||
|             if(model->status == SubGhzTestPacketModelStatusRx) { | ||||
|                 furi_hal_subghz_stop_async_rx(); | ||||
|             } else if(model->status == SubGhzTestPacketModelStatusTx) { | ||||
| @ -179,9 +183,8 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { | ||||
|                     instance->callback(SubGhzTestPacketEventOnlyRx, instance->context); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| @ -194,15 +197,17 @@ void subghz_test_packet_enter(void* context) { | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzTestPacketModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzTestPacketModel * model, | ||||
|         { | ||||
|             model->frequency = subghz_frequencies_433_92_testing; | ||||
|             model->real_frequency = | ||||
|                 furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]); | ||||
|             model->path = FuriHalSubGhzPathIsolate; // isolate
 | ||||
|             model->rssi = 0.0f; | ||||
|             model->status = SubGhzTestPacketModelStatusRx; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance); | ||||
| 
 | ||||
| @ -217,15 +222,17 @@ void subghz_test_packet_exit(void* context) { | ||||
| 
 | ||||
|     // Reinitialize IC to default state
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzTestPacketModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzTestPacketModel * model, | ||||
|         { | ||||
|             if(model->status == SubGhzTestPacketModelStatusRx) { | ||||
|                 furi_hal_subghz_stop_async_rx(); | ||||
|             } else if(model->status == SubGhzTestPacketModelStatusTx) { | ||||
|                 subghz_encoder_princeton_for_testing_stop(instance->encoder, furi_get_tick()); | ||||
|                 furi_hal_subghz_stop_async_tx(); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     furi_hal_subghz_sleep(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -77,7 +77,9 @@ bool subghz_test_static_input(InputEvent* event, void* context) { | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzTestStaticModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzTestStaticModel * model, | ||||
|         { | ||||
|             if(event->type == InputTypeShort) { | ||||
|                 if(event->key == InputKeyLeft) { | ||||
|                     if(model->frequency > 0) model->frequency--; | ||||
| @ -128,9 +130,8 @@ bool subghz_test_static_input(InputEvent* event, void* context) { | ||||
|                 } | ||||
|                 furi_record_close(RECORD_NOTIFICATION); | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| @ -147,13 +148,14 @@ void subghz_test_static_enter(void* context) { | ||||
|     instance->status_tx = SubGhzTestStaticStatusIDLE; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubGhzTestStaticModel * model) { | ||||
|         instance->view, | ||||
|         SubGhzTestStaticModel * model, | ||||
|         { | ||||
|             model->frequency = subghz_frequencies_433_92_testing; | ||||
|             model->real_frequency = subghz_frequencies_testing[model->frequency]; | ||||
|             model->button = 0; | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void subghz_test_static_exit(void* context) { | ||||
|  | ||||
| @ -35,13 +35,15 @@ void subghz_view_transmitter_add_data_to_show( | ||||
|     uint8_t show_button) { | ||||
|     furi_assert(subghz_transmitter); | ||||
|     with_view_model( | ||||
|         subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { | ||||
|         subghz_transmitter->view, | ||||
|         SubGhzViewTransmitterModel * model, | ||||
|         { | ||||
|             furi_string_set(model->key_str, key_str); | ||||
|             furi_string_set(model->frequency_str, frequency_str); | ||||
|             furi_string_set(model->preset_str, preset_str); | ||||
|             model->show_button = show_button; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) { | ||||
| @ -95,23 +97,27 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { | ||||
| 
 | ||||
|     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { | ||||
|             subghz_transmitter->view, | ||||
|             SubGhzViewTransmitterModel * model, | ||||
|             { | ||||
|                 furi_string_reset(model->frequency_str); | ||||
|                 furi_string_reset(model->preset_str); | ||||
|                 furi_string_reset(model->key_str); | ||||
|                 model->show_button = 0; | ||||
|                 return false; | ||||
|             }); | ||||
|             }, | ||||
|             false); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { | ||||
|         subghz_transmitter->view, | ||||
|         SubGhzViewTransmitterModel * model, | ||||
|         { | ||||
|             if(model->show_button) { | ||||
|                 can_be_sent = true; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { | ||||
|         subghz_transmitter->callback( | ||||
| @ -149,12 +155,14 @@ SubGhzViewTransmitter* subghz_view_transmitter_alloc() { | ||||
|     view_set_exit_callback(subghz_transmitter->view, subghz_view_transmitter_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { | ||||
|         subghz_transmitter->view, | ||||
|         SubGhzViewTransmitterModel * model, | ||||
|         { | ||||
|             model->frequency_str = furi_string_alloc(); | ||||
|             model->preset_str = furi_string_alloc(); | ||||
|             model->key_str = furi_string_alloc(); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     return subghz_transmitter; | ||||
| } | ||||
| 
 | ||||
| @ -162,12 +170,14 @@ void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) { | ||||
|     furi_assert(subghz_transmitter); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { | ||||
|         subghz_transmitter->view, | ||||
|         SubGhzViewTransmitterModel * model, | ||||
|         { | ||||
|             furi_string_free(model->frequency_str); | ||||
|             furi_string_free(model->preset_str); | ||||
|             furi_string_free(model->key_str); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     view_free(subghz_transmitter->view); | ||||
|     free(subghz_transmitter); | ||||
| } | ||||
|  | ||||
| @ -85,18 +85,17 @@ void u2f_view_set_ok_callback(U2fView* u2f, U2fOkCallback callback, void* contex | ||||
|     furi_assert(u2f); | ||||
|     furi_assert(callback); | ||||
|     with_view_model( | ||||
|         u2f->view, (U2fModel * model) { | ||||
|         u2f->view, | ||||
|         U2fModel * model, | ||||
|         { | ||||
|             UNUSED(model); | ||||
|             u2f->callback = callback; | ||||
|             u2f->context = context; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void u2f_view_set_state(U2fView* u2f, U2fViewMsg msg) { | ||||
|     with_view_model( | ||||
|         u2f->view, (U2fModel * model) { | ||||
|             model->display_msg = msg; | ||||
|             return true; | ||||
|         }); | ||||
|         u2f->view, U2fModel * model, { model->display_msg = msg; }, true); | ||||
| } | ||||
|  | ||||
| @ -277,7 +277,9 @@ static void bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeybo | ||||
| 
 | ||||
| static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) { | ||||
|     with_view_model( | ||||
|         bt_hid_keyboard->view, (BtHidKeyboardModel * model) { | ||||
|         bt_hid_keyboard->view, | ||||
|         BtHidKeyboardModel * model, | ||||
|         { | ||||
|             if(event->key == InputKeyOk) { | ||||
|                 if(event->type == InputTypePress) { | ||||
|                     model->ok_pressed = true; | ||||
| @ -338,8 +340,8 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* | ||||
|                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0}); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) { | ||||
| @ -382,8 +384,5 @@ View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) { | ||||
| void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) { | ||||
|     furi_assert(bt_hid_keyboard); | ||||
|     with_view_model( | ||||
|         bt_hid_keyboard->view, (BtHidKeyboardModel * model) { | ||||
|             model->connected = connected; | ||||
|             return true; | ||||
|         }); | ||||
|         bt_hid_keyboard->view, BtHidKeyboardModel * model, { model->connected = connected; }, true); | ||||
| } | ||||
|  | ||||
| @ -106,7 +106,9 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | ||||
| 
 | ||||
| static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { | ||||
|     with_view_model( | ||||
|         bt_hid_keynote->view, (BtHidKeynoteModel * model) { | ||||
|         bt_hid_keynote->view, | ||||
|         BtHidKeynoteModel * model, | ||||
|         { | ||||
|             if(event->type == InputTypePress) { | ||||
|                 if(event->key == InputKeyUp) { | ||||
|                     model->up_pressed = true; | ||||
| @ -153,8 +155,8 @@ static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* eve | ||||
|                     furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) { | ||||
| @ -197,8 +199,5 @@ View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) { | ||||
| void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) { | ||||
|     furi_assert(bt_hid_keynote); | ||||
|     with_view_model( | ||||
|         bt_hid_keynote->view, (BtHidKeynoteModel * model) { | ||||
|             model->connected = connected; | ||||
|             return true; | ||||
|         }); | ||||
|         bt_hid_keynote->view, BtHidKeynoteModel * model, { model->connected = connected; }, true); | ||||
| } | ||||
|  | ||||
| @ -107,7 +107,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { | ||||
| 
 | ||||
| static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) { | ||||
|     with_view_model( | ||||
|         bt_hid_media->view, (BtHidMediaModel * model) { | ||||
|         bt_hid_media->view, | ||||
|         BtHidMediaModel * model, | ||||
|         { | ||||
|             if(event->key == InputKeyUp) { | ||||
|                 model->up_pressed = true; | ||||
|                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); | ||||
| @ -124,13 +126,15 @@ static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* eve | ||||
|                 model->ok_pressed = true; | ||||
|                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* event) { | ||||
|     with_view_model( | ||||
|         bt_hid_media->view, (BtHidMediaModel * model) { | ||||
|         bt_hid_media->view, | ||||
|         BtHidMediaModel * model, | ||||
|         { | ||||
|             if(event->key == InputKeyUp) { | ||||
|                 model->up_pressed = false; | ||||
|                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); | ||||
| @ -147,8 +151,8 @@ static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* e | ||||
|                 model->ok_pressed = false; | ||||
|                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static bool bt_hid_media_input_callback(InputEvent* event, void* context) { | ||||
| @ -196,8 +200,5 @@ View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) { | ||||
| void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) { | ||||
|     furi_assert(bt_hid_media); | ||||
|     with_view_model( | ||||
|         bt_hid_media->view, (BtHidMediaModel * model) { | ||||
|             model->connected = connected; | ||||
|             return true; | ||||
|         }); | ||||
|         bt_hid_media->view, BtHidMediaModel * model, { model->connected = connected; }, true); | ||||
| } | ||||
|  | ||||
| @ -103,7 +103,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { | ||||
| 
 | ||||
| static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { | ||||
|     with_view_model( | ||||
|         bt_hid_mouse->view, (BtHidMouseModel * model) { | ||||
|         bt_hid_mouse->view, | ||||
|         BtHidMouseModel * model, | ||||
|         { | ||||
|             if(event->key == InputKeyBack) { | ||||
|                 if(event->type == InputTypeShort) { | ||||
|                     furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); | ||||
| @ -167,8 +169,8 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { | ||||
|                     model->up_pressed = false; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static bool bt_hid_mouse_input_callback(InputEvent* event, void* context) { | ||||
| @ -211,8 +213,5 @@ View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse) { | ||||
| void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected) { | ||||
|     furi_assert(bt_hid_mouse); | ||||
|     with_view_model( | ||||
|         bt_hid_mouse->view, (BtHidMouseModel * model) { | ||||
|             model->connected = connected; | ||||
|             return true; | ||||
|         }); | ||||
|         bt_hid_mouse->view, BtHidMouseModel * model, { model->connected = connected; }, true); | ||||
| } | ||||
|  | ||||
| @ -44,12 +44,14 @@ static void pwm_set_config(SignalGenPwm* pwm) { | ||||
|     uint8_t duty; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         pwm->view, (SignalGenPwmViewModel * model) { | ||||
|         pwm->view, | ||||
|         SignalGenPwmViewModel * model, | ||||
|         { | ||||
|             channel = model->channel_id; | ||||
|             freq = model->freq; | ||||
|             duty = model->duty; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     furi_assert(pwm->callback); | ||||
|     pwm->callback(channel, freq, duty, pwm->context); | ||||
| @ -188,7 +190,9 @@ static bool signal_gen_pwm_input_callback(InputEvent* event, void* context) { | ||||
|     bool need_update = false; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         pwm->view, (SignalGenPwmViewModel * model) { | ||||
|         pwm->view, | ||||
|         SignalGenPwmViewModel * model, | ||||
|         { | ||||
|             if(model->edit_mode == false) { | ||||
|                 if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { | ||||
|                     if(event->key == InputKeyUp) { | ||||
| @ -238,8 +242,8 @@ static bool signal_gen_pwm_input_callback(InputEvent* event, void* context) { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     if(need_update) { | ||||
|         pwm_set_config(pwm); | ||||
| @ -279,22 +283,26 @@ void signal_gen_pwm_set_callback( | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         pwm->view, (SignalGenPwmViewModel * model) { | ||||
|         pwm->view, | ||||
|         SignalGenPwmViewModel * model, | ||||
|         { | ||||
|             UNUSED(model); | ||||
|             pwm->callback = callback; | ||||
|             pwm->context = context; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void signal_gen_pwm_set_params(SignalGenPwm* pwm, uint8_t channel_id, uint32_t freq, uint8_t duty) { | ||||
|     with_view_model( | ||||
|         pwm->view, (SignalGenPwmViewModel * model) { | ||||
|         pwm->view, | ||||
|         SignalGenPwmViewModel * model, | ||||
|         { | ||||
|             model->channel_id = channel_id; | ||||
|             model->freq = freq; | ||||
|             model->duty = duty; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     furi_assert(pwm->callback); | ||||
|     pwm->callback(channel_id, freq, duty, pwm->context); | ||||
|  | ||||
| @ -124,16 +124,17 @@ bool desktop_debug_input(InputEvent* event, void* context) { | ||||
| 
 | ||||
|     DesktopViewStatsScreens current = 0; | ||||
|     with_view_model( | ||||
|         debug_view->view, (DesktopDebugViewModel * model) { | ||||
| 
 | ||||
|         debug_view->view, | ||||
|         DesktopDebugViewModel * model, | ||||
|         { | ||||
| #ifdef SRV_DOLPHIN_STATE_DEBUG | ||||
|             if((event->key == InputKeyDown) || (event->key == InputKeyUp)) { | ||||
|                 model->screen = !model->screen; | ||||
|             } | ||||
| #endif | ||||
|             current = model->screen; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     size_t count = (event->type == InputTypeRepeat) ? 10 : 1; | ||||
|     if(current == DesktopViewStatsMeta) { | ||||
| @ -181,20 +182,19 @@ void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { | ||||
|     Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); | ||||
|     DolphinStats stats = dolphin_stats(dolphin); | ||||
|     with_view_model( | ||||
|         debug_view->view, (DesktopDebugViewModel * model) { | ||||
|         debug_view->view, | ||||
|         DesktopDebugViewModel * model, | ||||
|         { | ||||
|             model->icounter = stats.icounter; | ||||
|             model->butthurt = stats.butthurt; | ||||
|             model->timestamp = stats.timestamp; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     furi_record_close(RECORD_DOLPHIN); | ||||
| } | ||||
| 
 | ||||
| void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view) { | ||||
|     with_view_model( | ||||
|         debug_view->view, (DesktopDebugViewModel * model) { | ||||
|             model->screen = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         debug_view->view, DesktopDebugViewModel * model, { model->screen = 0; }, true); | ||||
| } | ||||
|  | ||||
| @ -24,27 +24,24 @@ void desktop_lock_menu_set_callback( | ||||
| 
 | ||||
| void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set) { | ||||
|     with_view_model( | ||||
|         lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||
|             model->pin_is_set = pin_is_set; | ||||
|             return true; | ||||
|         }); | ||||
|         lock_menu->view, | ||||
|         DesktopLockMenuViewModel * model, | ||||
|         { model->pin_is_set = pin_is_set; }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) { | ||||
|     with_view_model( | ||||
|         lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||
|             model->dummy_mode = dummy_mode; | ||||
|             return true; | ||||
|         }); | ||||
|         lock_menu->view, | ||||
|         DesktopLockMenuViewModel * model, | ||||
|         { model->dummy_mode = dummy_mode; }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx) { | ||||
|     furi_assert(idx < DesktopLockMenuIndexTotalCount); | ||||
|     with_view_model( | ||||
|         lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||
|             model->idx = idx; | ||||
|             return true; | ||||
|         }); | ||||
|         lock_menu->view, DesktopLockMenuViewModel * model, { model->idx = idx; }, true); | ||||
| } | ||||
| 
 | ||||
| void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { | ||||
| @ -95,10 +92,12 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { | ||||
|     uint8_t idx = 0; | ||||
|     bool consumed = false; | ||||
|     bool dummy_mode = false; | ||||
|     bool update = false; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||
|             bool ret = false; | ||||
|         lock_menu->view, | ||||
|         DesktopLockMenuViewModel * model, | ||||
|         { | ||||
|             if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { | ||||
|                 if(event->key == InputKeyUp) { | ||||
|                     if(model->idx == 0) { | ||||
| @ -106,7 +105,7 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { | ||||
|                     } else { | ||||
|                         model->idx = CLAMP(model->idx - 1, DesktopLockMenuIndexTotalCount - 1, 0); | ||||
|                     } | ||||
|                     ret = true; | ||||
|                     update = true; | ||||
|                     consumed = true; | ||||
|                 } else if(event->key == InputKeyDown) { | ||||
|                     if(model->idx == DesktopLockMenuIndexTotalCount - 1) { | ||||
| @ -114,14 +113,14 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { | ||||
|                     } else { | ||||
|                         model->idx = CLAMP(model->idx + 1, DesktopLockMenuIndexTotalCount - 1, 0); | ||||
|                     } | ||||
|                     ret = true; | ||||
|                     update = true; | ||||
|                     consumed = true; | ||||
|                 } | ||||
|             } | ||||
|             idx = model->idx; | ||||
|             dummy_mode = model->dummy_mode; | ||||
|             return ret; | ||||
|         }); | ||||
|         }, | ||||
|         update); | ||||
| 
 | ||||
|     if(event->key == InputKeyOk) { | ||||
|         if((idx == DesktopLockMenuIndexLock) && (event->type == InputTypeShort)) { | ||||
|  | ||||
| @ -148,28 +148,32 @@ static void button_menu_process_up(ButtonMenu* button_menu) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { | ||||
|             if(model->position > 0) { | ||||
|                 model->position--; | ||||
|             } else { | ||||
|                 model->position = ButtonMenuItemArray_size(model->items) - 1; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void button_menu_process_down(ButtonMenu* button_menu) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { | ||||
|             if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) { | ||||
|                 model->position++; | ||||
|             } else { | ||||
|                 model->position = 0; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { | ||||
| @ -178,12 +182,14 @@ static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { | ||||
|     ButtonMenuItem* item = NULL; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { | ||||
|             if(model->position < (ButtonMenuItemArray_size(model->items))) { | ||||
|                 item = ButtonMenuItemArray_get(model->items, model->position); | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     if(item) { | ||||
|         if(item->type == ButtonMenuItemTypeControl) { | ||||
| @ -248,22 +254,21 @@ void button_menu_reset(ButtonMenu* button_menu) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { | ||||
|             ButtonMenuItemArray_reset(model->items); | ||||
|             model->position = 0; | ||||
|             model->header = NULL; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void button_menu_set_header(ButtonMenu* button_menu, const char* header) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|             model->header = header; | ||||
|             return true; | ||||
|         }); | ||||
|         button_menu->view, ButtonMenuModel * model, { model->header = header; }, true); | ||||
| } | ||||
| 
 | ||||
| ButtonMenuItem* button_menu_add_item( | ||||
| @ -278,15 +283,17 @@ ButtonMenuItem* button_menu_add_item( | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { | ||||
|             item = ButtonMenuItemArray_push_new(model->items); | ||||
|             item->label = label; | ||||
|             item->index = index; | ||||
|             item->type = type; | ||||
|             item->callback = callback; | ||||
|             item->callback_context = callback_context; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return item; | ||||
| } | ||||
| @ -301,12 +308,14 @@ ButtonMenu* button_menu_alloc(void) { | ||||
|     view_set_input_callback(button_menu->view, button_menu_view_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { | ||||
|             ButtonMenuItemArray_init(model->items); | ||||
|             model->position = 0; | ||||
|             model->header = NULL; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     button_menu->freeze_input = false; | ||||
|     return button_menu; | ||||
| @ -316,10 +325,10 @@ void button_menu_free(ButtonMenu* button_menu) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|             ButtonMenuItemArray_clear(model->items); | ||||
|             return true; | ||||
|         }); | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { ButtonMenuItemArray_clear(model->items); }, | ||||
|         true); | ||||
|     view_free(button_menu->view); | ||||
|     free(button_menu); | ||||
| } | ||||
| @ -328,7 +337,9 @@ void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_menu->view, (ButtonMenuModel * model) { | ||||
|         button_menu->view, | ||||
|         ButtonMenuModel * model, | ||||
|         { | ||||
|             uint8_t item_position = 0; | ||||
|             ButtonMenuItemArray_it_t it; | ||||
|             for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); | ||||
| @ -338,6 +349,6 @@ void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -70,15 +70,17 @@ ButtonPanel* button_panel_alloc() { | ||||
|     view_set_input_callback(button_panel->view, button_panel_view_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             model->reserve_x = 0; | ||||
|             model->reserve_y = 0; | ||||
|             model->selected_item_x = 0; | ||||
|             model->selected_item_y = 0; | ||||
|             ButtonMatrix_init(model->button_matrix); | ||||
|             LabelList_init(model->labels); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return button_panel; | ||||
| } | ||||
| @ -88,7 +90,9 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re | ||||
|     furi_check(reserve_y > 0); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             model->reserve_x = reserve_x; | ||||
|             model->reserve_y = reserve_y; | ||||
|             ButtonMatrix_reserve(model->button_matrix, model->reserve_y); | ||||
| @ -99,8 +103,8 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re | ||||
|                 // TODO: do we need to clear allocated memory of ptr-s to ButtonItem ??
 | ||||
|             } | ||||
|             LabelList_init(model->labels); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void button_panel_free(ButtonPanel* button_panel) { | ||||
| @ -109,11 +113,13 @@ void button_panel_free(ButtonPanel* button_panel) { | ||||
|     button_panel_reset(button_panel); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             LabelList_clear(model->labels); | ||||
|             ButtonMatrix_clear(model->button_matrix); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     view_free(button_panel->view); | ||||
|     free(button_panel); | ||||
| @ -123,7 +129,9 @@ void button_panel_reset(ButtonPanel* button_panel) { | ||||
|     furi_assert(button_panel); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             for(size_t x = 0; x < model->reserve_x; ++x) { | ||||
|                 for(size_t y = 0; y < model->reserve_y; ++y) { | ||||
|                     ButtonItem** button_item = button_panel_get_item(model, x, y); | ||||
| @ -137,8 +145,8 @@ void button_panel_reset(ButtonPanel* button_panel) { | ||||
|             model->selected_item_y = 0; | ||||
|             LabelList_reset(model->labels); | ||||
|             ButtonMatrix_reset(model->button_matrix); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y) { | ||||
| @ -165,7 +173,9 @@ void button_panel_add_item( | ||||
|     furi_assert(button_panel); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             ButtonItem** button_item_ptr = | ||||
|                 button_panel_get_item(model, matrix_place_x, matrix_place_y); | ||||
|             furi_check(*button_item_ptr == NULL); | ||||
| @ -178,8 +188,8 @@ void button_panel_add_item( | ||||
|             button_item->icon.name = icon_name; | ||||
|             button_item->icon.name_selected = icon_name_selected; | ||||
|             button_item->index = index; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| View* button_panel_get_view(ButtonPanel* button_panel) { | ||||
| @ -216,114 +226,123 @@ static void button_panel_view_draw_callback(Canvas* canvas, void* _model) { | ||||
| 
 | ||||
| static void button_panel_process_down(ButtonPanel* button_panel) { | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             uint16_t new_selected_item_x = model->selected_item_x; | ||||
|             uint16_t new_selected_item_y = model->selected_item_y; | ||||
|             size_t i; | ||||
| 
 | ||||
|             if(new_selected_item_y >= (model->reserve_y - 1)) return false; | ||||
|             if(new_selected_item_y < (model->reserve_y - 1)) { | ||||
|                 ++new_selected_item_y; | ||||
| 
 | ||||
|             ++new_selected_item_y; | ||||
| 
 | ||||
|             for(i = 0; i < model->reserve_x; ++i) { | ||||
|                 new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; | ||||
|                 if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                     break; | ||||
|                 for(i = 0; i < model->reserve_x; ++i) { | ||||
|                     new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; | ||||
|                     if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if(i != model->reserve_x) { | ||||
|                     model->selected_item_x = new_selected_item_x; | ||||
|                     model->selected_item_y = new_selected_item_y; | ||||
|                 } | ||||
|             } | ||||
|             if(i == model->reserve_x) return false; | ||||
| 
 | ||||
|             model->selected_item_x = new_selected_item_x; | ||||
|             model->selected_item_y = new_selected_item_y; | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void button_panel_process_up(ButtonPanel* button_panel) { | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             size_t new_selected_item_x = model->selected_item_x; | ||||
|             size_t new_selected_item_y = model->selected_item_y; | ||||
|             size_t i; | ||||
| 
 | ||||
|             if(new_selected_item_y <= 0) return false; | ||||
|             if(new_selected_item_y > 0) { | ||||
|                 --new_selected_item_y; | ||||
| 
 | ||||
|             --new_selected_item_y; | ||||
| 
 | ||||
|             for(i = 0; i < model->reserve_x; ++i) { | ||||
|                 new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; | ||||
|                 if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                     break; | ||||
|                 for(i = 0; i < model->reserve_x; ++i) { | ||||
|                     new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; | ||||
|                     if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if(i != model->reserve_x) { | ||||
|                     model->selected_item_x = new_selected_item_x; | ||||
|                     model->selected_item_y = new_selected_item_y; | ||||
|                 } | ||||
|             } | ||||
|             if(i == model->reserve_x) return false; | ||||
| 
 | ||||
|             model->selected_item_x = new_selected_item_x; | ||||
|             model->selected_item_y = new_selected_item_y; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void button_panel_process_left(ButtonPanel* button_panel) { | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             size_t new_selected_item_x = model->selected_item_x; | ||||
|             size_t new_selected_item_y = model->selected_item_y; | ||||
|             size_t i; | ||||
| 
 | ||||
|             if(new_selected_item_x <= 0) return false; | ||||
|             if(new_selected_item_x > 0) { | ||||
|                 --new_selected_item_x; | ||||
| 
 | ||||
|             --new_selected_item_x; | ||||
| 
 | ||||
|             for(i = 0; i < model->reserve_y; ++i) { | ||||
|                 new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; | ||||
|                 if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                     break; | ||||
|                 for(i = 0; i < model->reserve_y; ++i) { | ||||
|                     new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; | ||||
|                     if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if(i != model->reserve_y) { | ||||
|                     model->selected_item_x = new_selected_item_x; | ||||
|                     model->selected_item_y = new_selected_item_y; | ||||
|                 } | ||||
|             } | ||||
|             if(i == model->reserve_y) return false; | ||||
| 
 | ||||
|             model->selected_item_x = new_selected_item_x; | ||||
|             model->selected_item_y = new_selected_item_y; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void button_panel_process_right(ButtonPanel* button_panel) { | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             uint16_t new_selected_item_x = model->selected_item_x; | ||||
|             uint16_t new_selected_item_y = model->selected_item_y; | ||||
|             size_t i; | ||||
| 
 | ||||
|             if(new_selected_item_x >= (model->reserve_x - 1)) return false; | ||||
|             if(new_selected_item_x < (model->reserve_x - 1)) { | ||||
|                 ++new_selected_item_x; | ||||
| 
 | ||||
|             ++new_selected_item_x; | ||||
| 
 | ||||
|             for(i = 0; i < model->reserve_y; ++i) { | ||||
|                 new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; | ||||
|                 if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                     break; | ||||
|                 for(i = 0; i < model->reserve_y; ++i) { | ||||
|                     new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; | ||||
|                     if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if(i != model->reserve_y) { | ||||
|                     model->selected_item_x = new_selected_item_x; | ||||
|                     model->selected_item_y = new_selected_item_y; | ||||
|                 } | ||||
|             } | ||||
|             if(i == model->reserve_y) return false; | ||||
| 
 | ||||
|             model->selected_item_x = new_selected_item_x; | ||||
|             model->selected_item_y = new_selected_item_y; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void button_panel_process_ok(ButtonPanel* button_panel) { | ||||
|     ButtonItem* button_item = NULL; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             button_item = | ||||
|                 *button_panel_get_item(model, model->selected_item_x, model->selected_item_y); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     if(button_item && button_item->callback) { | ||||
|         button_item->callback(button_item->callback_context, button_item->index); | ||||
| @ -374,12 +393,14 @@ void button_panel_add_label( | ||||
|     furi_assert(button_panel); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         button_panel->view, (ButtonPanelModel * model) { | ||||
|         button_panel->view, | ||||
|         ButtonPanelModel * model, | ||||
|         { | ||||
|             LabelElement* label = LabelList_push_raw(model->labels); | ||||
|             label->x = x; | ||||
|             label->y = y; | ||||
|             label->font = font; | ||||
|             label->str = label_str; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -618,42 +618,30 @@ static bool byte_input_view_input_callback(InputEvent* event, void* context) { | ||||
|         switch(event->key) { | ||||
|         case InputKeyLeft: | ||||
|             with_view_model( | ||||
|                 byte_input->view, (ByteInputModel * model) { | ||||
|                     byte_input_handle_left(model); | ||||
|                     return true; | ||||
|                 }); | ||||
|                 byte_input->view, ByteInputModel * model, { byte_input_handle_left(model); }, true); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case InputKeyRight: | ||||
|             with_view_model( | ||||
|                 byte_input->view, (ByteInputModel * model) { | ||||
|                     byte_input_handle_right(model); | ||||
|                     return true; | ||||
|                 }); | ||||
|                 byte_input->view, | ||||
|                 ByteInputModel * model, | ||||
|                 { byte_input_handle_right(model); }, | ||||
|                 true); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case InputKeyUp: | ||||
|             with_view_model( | ||||
|                 byte_input->view, (ByteInputModel * model) { | ||||
|                     byte_input_handle_up(model); | ||||
|                     return true; | ||||
|                 }); | ||||
|                 byte_input->view, ByteInputModel * model, { byte_input_handle_up(model); }, true); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case InputKeyDown: | ||||
|             with_view_model( | ||||
|                 byte_input->view, (ByteInputModel * model) { | ||||
|                     byte_input_handle_down(model); | ||||
|                     return true; | ||||
|                 }); | ||||
|                 byte_input->view, ByteInputModel * model, { byte_input_handle_down(model); }, true); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case InputKeyOk: | ||||
|             with_view_model( | ||||
|                 byte_input->view, (ByteInputModel * model) { | ||||
|                     byte_input_handle_ok(model); | ||||
|                     return true; | ||||
|                 }); | ||||
|                 byte_input->view, ByteInputModel * model, { byte_input_handle_ok(model); }, true); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
| @ -664,10 +652,10 @@ static bool byte_input_view_input_callback(InputEvent* event, void* context) { | ||||
|     if((event->type == InputTypeLong || event->type == InputTypeRepeat) && | ||||
|        event->key == InputKeyBack) { | ||||
|         with_view_model( | ||||
|             byte_input->view, (ByteInputModel * model) { | ||||
|                 byte_input_clear_selected_byte(model); | ||||
|                 return true; | ||||
|             }); | ||||
|             byte_input->view, | ||||
|             ByteInputModel * model, | ||||
|             { byte_input_clear_selected_byte(model); }, | ||||
|             true); | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
| @ -703,14 +691,16 @@ ByteInput* byte_input_alloc() { | ||||
|     view_set_input_callback(byte_input->view, byte_input_view_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         byte_input->view, (ByteInputModel * model) { | ||||
|         byte_input->view, | ||||
|         ByteInputModel * model, | ||||
|         { | ||||
|             model->header = ""; | ||||
|             model->input_callback = NULL; | ||||
|             model->changed_callback = NULL; | ||||
|             model->callback_context = NULL; | ||||
|             byte_input_reset_model_input_data(model); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return byte_input; | ||||
| } | ||||
| @ -755,15 +745,17 @@ void byte_input_set_result_callback( | ||||
|     uint8_t* bytes, | ||||
|     uint8_t bytes_count) { | ||||
|     with_view_model( | ||||
|         byte_input->view, (ByteInputModel * model) { | ||||
|         byte_input->view, | ||||
|         ByteInputModel * model, | ||||
|         { | ||||
|             byte_input_reset_model_input_data(model); | ||||
|             model->input_callback = input_callback; | ||||
|             model->changed_callback = changed_callback; | ||||
|             model->callback_context = callback_context; | ||||
|             model->bytes = bytes; | ||||
|             model->bytes_count = bytes_count; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| @ -774,8 +766,5 @@ void byte_input_set_result_callback( | ||||
|  */ | ||||
| void byte_input_set_header_text(ByteInput* byte_input, const char* text) { | ||||
|     with_view_model( | ||||
|         byte_input->view, (ByteInputModel * model) { | ||||
|             model->header = text; | ||||
|             return true; | ||||
|         }); | ||||
|         byte_input->view, ByteInputModel * model, { model->header = text; }, true); | ||||
| } | ||||
|  | ||||
| @ -90,12 +90,14 @@ static bool dialog_ex_view_input_callback(InputEvent* event, void* context) { | ||||
|     const char* right_text = NULL; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|         dialog_ex->view, | ||||
|         DialogExModel * model, | ||||
|         { | ||||
|             left_text = model->left_text; | ||||
|             center_text = model->center_text; | ||||
|             right_text = model->right_text; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     if(dialog_ex->callback) { | ||||
|         if(event->type == InputTypeShort) { | ||||
| @ -149,7 +151,9 @@ DialogEx* dialog_ex_alloc() { | ||||
|     view_set_draw_callback(dialog_ex->view, dialog_ex_view_draw_callback); | ||||
|     view_set_input_callback(dialog_ex->view, dialog_ex_view_input_callback); | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|         dialog_ex->view, | ||||
|         DialogExModel * model, | ||||
|         { | ||||
|             model->header.text = NULL; | ||||
|             model->header.x = 0; | ||||
|             model->header.y = 0; | ||||
| @ -169,9 +173,8 @@ DialogEx* dialog_ex_alloc() { | ||||
|             model->left_text = NULL; | ||||
|             model->center_text = NULL; | ||||
|             model->right_text = NULL; | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     dialog_ex->enable_extended_events = false; | ||||
|     return dialog_ex; | ||||
| } | ||||
| @ -206,14 +209,16 @@ void dialog_ex_set_header( | ||||
|     Align vertical) { | ||||
|     furi_assert(dialog_ex); | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|         dialog_ex->view, | ||||
|         DialogExModel * model, | ||||
|         { | ||||
|             model->header.text = text; | ||||
|             model->header.x = x; | ||||
|             model->header.y = y; | ||||
|             model->header.horizontal = horizontal; | ||||
|             model->header.vertical = vertical; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dialog_ex_set_text( | ||||
| @ -225,52 +230,47 @@ void dialog_ex_set_text( | ||||
|     Align vertical) { | ||||
|     furi_assert(dialog_ex); | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|         dialog_ex->view, | ||||
|         DialogExModel * model, | ||||
|         { | ||||
|             model->text.text = text; | ||||
|             model->text.x = x; | ||||
|             model->text.y = y; | ||||
|             model->text.horizontal = horizontal; | ||||
|             model->text.vertical = vertical; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon) { | ||||
|     furi_assert(dialog_ex); | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|         dialog_ex->view, | ||||
|         DialogExModel * model, | ||||
|         { | ||||
|             model->icon.x = x; | ||||
|             model->icon.y = y; | ||||
|             model->icon.icon = icon; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) { | ||||
|     furi_assert(dialog_ex); | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|             model->left_text = text; | ||||
|             return true; | ||||
|         }); | ||||
|         dialog_ex->view, DialogExModel * model, { model->left_text = text; }, true); | ||||
| } | ||||
| 
 | ||||
| void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) { | ||||
|     furi_assert(dialog_ex); | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|             model->center_text = text; | ||||
|             return true; | ||||
|         }); | ||||
|         dialog_ex->view, DialogExModel * model, { model->center_text = text; }, true); | ||||
| } | ||||
| 
 | ||||
| void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) { | ||||
|     furi_assert(dialog_ex); | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|             model->right_text = text; | ||||
|             return true; | ||||
|         }); | ||||
|         dialog_ex->view, DialogExModel * model, { model->right_text = text; }, true); | ||||
| } | ||||
| 
 | ||||
| void dialog_ex_reset(DialogEx* dialog_ex) { | ||||
| @ -279,15 +279,17 @@ void dialog_ex_reset(DialogEx* dialog_ex) { | ||||
|         .text = NULL, .x = 0, .y = 0, .horizontal = AlignLeft, .vertical = AlignLeft}; | ||||
|     IconElement clean_icon_el = {.icon = NULL, .x = 0, .y = 0}; | ||||
|     with_view_model( | ||||
|         dialog_ex->view, (DialogExModel * model) { | ||||
|         dialog_ex->view, | ||||
|         DialogExModel * model, | ||||
|         { | ||||
|             model->header = clean_text_el; | ||||
|             model->text = clean_text_el; | ||||
|             model->icon = clean_icon_el; | ||||
|             model->left_text = NULL; | ||||
|             model->center_text = NULL; | ||||
|             model->right_text = NULL; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     dialog_ex->context = NULL; | ||||
|     dialog_ex->callback = NULL; | ||||
| } | ||||
|  | ||||
| @ -139,10 +139,7 @@ FileBrowser* file_browser_alloc(FuriString* result_path) { | ||||
|     browser->result_path = result_path; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|             items_array_init(model->items); | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, FileBrowserModel * model, { items_array_init(model->items); }, false); | ||||
| 
 | ||||
|     return browser; | ||||
| } | ||||
| @ -151,10 +148,7 @@ void file_browser_free(FileBrowser* browser) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|             items_array_clear(model->items); | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, FileBrowserModel * model, { items_array_clear(model->items); }, false); | ||||
| 
 | ||||
|     view_free(browser->view); | ||||
|     free(browser); | ||||
| @ -178,11 +172,13 @@ void file_browser_configure( | ||||
|     browser->hide_ext = hide_ext; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|         browser->view, | ||||
|         FileBrowserModel * model, | ||||
|         { | ||||
|             model->file_icon = file_icon; | ||||
|             model->hide_ext = hide_ext; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void file_browser_start(FileBrowser* browser, FuriString* path) { | ||||
| @ -199,14 +195,16 @@ void file_browser_stop(FileBrowser* browser) { | ||||
|     furi_assert(browser); | ||||
|     file_browser_worker_free(browser->worker); | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|         browser->view, | ||||
|         FileBrowserModel * model, | ||||
|         { | ||||
|             items_array_reset(model->items); | ||||
|             model->item_cnt = 0; | ||||
|             model->item_idx = 0; | ||||
|             model->array_offset = 0; | ||||
|             model->list_offset = 0; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context) { | ||||
| @ -257,7 +255,9 @@ static void browser_update_offset(FileBrowser* browser) { | ||||
|     furi_assert(browser); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|         browser->view, | ||||
|         FileBrowserModel * model, | ||||
|         { | ||||
|             uint16_t bounds = model->item_cnt > (LIST_ITEMS - 1) ? 2 : model->item_cnt; | ||||
| 
 | ||||
|             if((model->item_cnt > (LIST_ITEMS - 1)) && | ||||
| @ -272,9 +272,8 @@ static void browser_update_offset(FileBrowser* browser) { | ||||
|                 model->list_offset = | ||||
|                     CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| @ -285,7 +284,9 @@ static void | ||||
|     int32_t load_offset = 0; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|         browser->view, | ||||
|         FileBrowserModel * model, | ||||
|         { | ||||
|             items_array_reset(model->items); | ||||
|             if(is_root) { | ||||
|                 model->item_cnt = item_cnt; | ||||
| @ -303,8 +304,8 @@ static void | ||||
|             model->is_root = is_root; | ||||
|             model->list_loading = true; | ||||
|             model->folder_loading = false; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     browser_update_offset(browser); | ||||
| 
 | ||||
|     file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX); | ||||
| @ -319,7 +320,9 @@ static void browser_list_load_cb(void* context, uint32_t list_load_offset) { | ||||
|     back_item.type = BrowserItemTypeBack; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|         browser->view, | ||||
|         FileBrowserModel * model, | ||||
|         { | ||||
|             items_array_reset(model->items); | ||||
|             model->array_offset = list_load_offset; | ||||
|             if(!model->is_root) { | ||||
| @ -329,8 +332,8 @@ static void browser_list_load_cb(void* context, uint32_t list_load_offset) { | ||||
|                     model->array_offset += 1; | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     BrowserItem_t_clear(&back_item); | ||||
| } | ||||
| @ -371,11 +374,13 @@ static void | ||||
|         } | ||||
| 
 | ||||
|         with_view_model( | ||||
|             browser->view, (FileBrowserModel * model) { | ||||
|             browser->view, | ||||
|             FileBrowserModel * model, | ||||
|             { | ||||
|                 items_array_push_back(model->items, item); | ||||
|                 // TODO: calculate if element is visible
 | ||||
|                 return true; | ||||
|             }); | ||||
|             }, | ||||
|             true); | ||||
|         furi_string_free(item.display_name); | ||||
|         furi_string_free(item.path); | ||||
|         if(item.custom_icon_data) { | ||||
| @ -383,10 +388,7 @@ static void | ||||
|         } | ||||
|     } else { | ||||
|         with_view_model( | ||||
|             browser->view, (FileBrowserModel * model) { | ||||
|                 model->list_loading = false; | ||||
|                 return true; | ||||
|             }); | ||||
|             browser->view, FileBrowserModel * model, { model->list_loading = false; }, true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -395,10 +397,7 @@ static void browser_long_load_cb(void* context) { | ||||
|     FileBrowser* browser = (FileBrowser*)context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|             model->folder_loading = true; | ||||
|             return true; | ||||
|         }); | ||||
|         browser->view, FileBrowserModel * model, { model->folder_loading = true; }, true); | ||||
| } | ||||
| 
 | ||||
| static void browser_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { | ||||
| @ -508,17 +507,16 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { | ||||
|     bool is_loading = false; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         browser->view, (FileBrowserModel * model) { | ||||
|             is_loading = model->folder_loading; | ||||
|             return false; | ||||
|         }); | ||||
|         browser->view, FileBrowserModel * model, { is_loading = model->folder_loading; }, false); | ||||
| 
 | ||||
|     if(is_loading) { | ||||
|         return false; | ||||
|     } else if(event->key == InputKeyUp || event->key == InputKeyDown) { | ||||
|         if(event->type == InputTypeShort || event->type == InputTypeRepeat) { | ||||
|             with_view_model( | ||||
|                 browser->view, (FileBrowserModel * model) { | ||||
|                 browser->view, | ||||
|                 FileBrowserModel * model, | ||||
|                 { | ||||
|                     if(event->key == InputKeyUp) { | ||||
|                         model->item_idx = | ||||
|                             ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; | ||||
| @ -543,8 +541,8 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { | ||||
|                                 browser->worker, load_offset, ITEM_LIST_LEN_MAX); | ||||
|                         } | ||||
|                     } | ||||
|                     return true; | ||||
|                 }); | ||||
|                 }, | ||||
|                 true); | ||||
|             browser_update_offset(browser); | ||||
|             consumed = true; | ||||
|         } | ||||
| @ -553,7 +551,9 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { | ||||
|             BrowserItem_t* selected_item = NULL; | ||||
|             int32_t select_index = 0; | ||||
|             with_view_model( | ||||
|                 browser->view, (FileBrowserModel * model) { | ||||
|                 browser->view, | ||||
|                 FileBrowserModel * model, | ||||
|                 { | ||||
|                     if(browser_is_item_in_array(model, model->item_idx)) { | ||||
|                         selected_item = | ||||
|                             items_array_get(model->items, model->item_idx - model->array_offset); | ||||
| @ -562,8 +562,8 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { | ||||
|                             select_index -= 1; | ||||
|                         } | ||||
|                     } | ||||
|                     return false; | ||||
|                 }); | ||||
|                 }, | ||||
|                 false); | ||||
| 
 | ||||
|             if(selected_item) { | ||||
|                 if(selected_item->type == BrowserItemTypeBack) { | ||||
| @ -584,10 +584,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { | ||||
|         if(event->type == InputTypeShort) { | ||||
|             bool is_root = false; | ||||
|             with_view_model( | ||||
|                 browser->view, (FileBrowserModel * model) { | ||||
|                     is_root = model->is_root; | ||||
|                     return false; | ||||
|                 }); | ||||
|                 browser->view, FileBrowserModel * model, { is_root = model->is_root; }, false); | ||||
|             if(!is_root) { | ||||
|                 file_browser_worker_folder_exit(browser->worker); | ||||
|             } | ||||
|  | ||||
| @ -103,25 +103,29 @@ static bool menu_input_callback(InputEvent* event, void* context) { | ||||
| static void menu_enter(void* context) { | ||||
|     Menu* menu = context; | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||
|             if(item && item->icon) { | ||||
|                 icon_animation_start(item->icon); | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| static void menu_exit(void* context) { | ||||
|     Menu* menu = context; | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||
|             if(item && item->icon) { | ||||
|                 icon_animation_stop(item->icon); | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| Menu* menu_alloc() { | ||||
| @ -135,11 +139,13 @@ Menu* menu_alloc() { | ||||
|     view_set_exit_callback(menu->view, menu_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             MenuItemArray_init(model->items); | ||||
|             model->position = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return menu; | ||||
| } | ||||
| @ -168,7 +174,9 @@ void menu_add_item( | ||||
| 
 | ||||
|     MenuItem* item = NULL; | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             item = MenuItemArray_push_new(model->items); | ||||
|             item->label = label; | ||||
|             item->icon = icon ? icon_animation_alloc(icon) : icon_animation_alloc(&A_Plugins_14); | ||||
| @ -176,14 +184,16 @@ void menu_add_item( | ||||
|             item->index = index; | ||||
|             item->callback = callback; | ||||
|             item->callback_context = context; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void menu_reset(Menu* menu) { | ||||
|     furi_assert(menu); | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             for | ||||
|                 M_EACH(item, model->items, MenuItemArray_t) { | ||||
|                     icon_animation_stop(item->icon); | ||||
| @ -192,25 +202,27 @@ void menu_reset(Menu* menu) { | ||||
| 
 | ||||
|             MenuItemArray_reset(model->items); | ||||
|             model->position = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void menu_set_selected_item(Menu* menu, uint32_t index) { | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|             if(index >= MenuItemArray_size(model->items)) { | ||||
|                 return false; | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             if(index < MenuItemArray_size(model->items)) { | ||||
|                 model->position = index; | ||||
|             } | ||||
| 
 | ||||
|             model->position = index; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void menu_process_up(Menu* menu) { | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||
|             if(item && item->icon) { | ||||
|                 icon_animation_stop(item->icon); | ||||
| @ -226,13 +238,15 @@ static void menu_process_up(Menu* menu) { | ||||
|             if(item && item->icon) { | ||||
|                 icon_animation_start(item->icon); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void menu_process_down(Menu* menu) { | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||
|             if(item && item->icon) { | ||||
|                 icon_animation_stop(item->icon); | ||||
| @ -248,19 +262,21 @@ static void menu_process_down(Menu* menu) { | ||||
|             if(item && item->icon) { | ||||
|                 icon_animation_start(item->icon); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void menu_process_ok(Menu* menu) { | ||||
|     MenuItem* item = NULL; | ||||
|     with_view_model( | ||||
|         menu->view, (MenuModel * model) { | ||||
|         menu->view, | ||||
|         MenuModel * model, | ||||
|         { | ||||
|             if(model->position < MenuItemArray_size(model->items)) { | ||||
|                 item = MenuItemArray_get(model->items, model->position); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     if(item && item->callback) { | ||||
|         item->callback(item->callback_context, item->index); | ||||
|     } | ||||
|  | ||||
| @ -124,7 +124,9 @@ Popup* popup_alloc() { | ||||
|     view_set_exit_callback(popup->view, popup_stop_timer); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         popup->view, (PopupModel * model) { | ||||
|         popup->view, | ||||
|         PopupModel * model, | ||||
|         { | ||||
|             model->header.text = NULL; | ||||
|             model->header.x = 0; | ||||
|             model->header.y = 0; | ||||
| @ -140,8 +142,8 @@ Popup* popup_alloc() { | ||||
|             model->icon.x = 0; | ||||
|             model->icon.y = 0; | ||||
|             model->icon.icon = NULL; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
|     return popup; | ||||
| } | ||||
| 
 | ||||
| @ -176,14 +178,16 @@ void popup_set_header( | ||||
|     Align vertical) { | ||||
|     furi_assert(popup); | ||||
|     with_view_model( | ||||
|         popup->view, (PopupModel * model) { | ||||
|         popup->view, | ||||
|         PopupModel * model, | ||||
|         { | ||||
|             model->header.text = text; | ||||
|             model->header.x = x; | ||||
|             model->header.y = y; | ||||
|             model->header.horizontal = horizontal; | ||||
|             model->header.vertical = vertical; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void popup_set_text( | ||||
| @ -195,25 +199,29 @@ void popup_set_text( | ||||
|     Align vertical) { | ||||
|     furi_assert(popup); | ||||
|     with_view_model( | ||||
|         popup->view, (PopupModel * model) { | ||||
|         popup->view, | ||||
|         PopupModel * model, | ||||
|         { | ||||
|             model->text.text = text; | ||||
|             model->text.x = x; | ||||
|             model->text.y = y; | ||||
|             model->text.horizontal = horizontal; | ||||
|             model->text.vertical = vertical; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void popup_set_icon(Popup* popup, uint8_t x, uint8_t y, const Icon* icon) { | ||||
|     furi_assert(popup); | ||||
|     with_view_model( | ||||
|         popup->view, (PopupModel * model) { | ||||
|         popup->view, | ||||
|         PopupModel * model, | ||||
|         { | ||||
|             model->icon.x = x; | ||||
|             model->icon.y = y; | ||||
|             model->icon.icon = icon; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void popup_set_timeout(Popup* popup, uint32_t timeout_in_ms) { | ||||
| @ -233,12 +241,14 @@ void popup_reset(Popup* popup) { | ||||
|     furi_assert(popup); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         popup->view, (PopupModel * model) { | ||||
|         popup->view, | ||||
|         PopupModel * model, | ||||
|         { | ||||
|             memset(&model->header, 0, sizeof(model->header)); | ||||
|             memset(&model->text, 0, sizeof(model->text)); | ||||
|             memset(&model->icon, 0, sizeof(model->icon)); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     popup->callback = NULL; | ||||
|     popup->context = NULL; | ||||
|     popup->timer_enabled = false; | ||||
|  | ||||
| @ -128,13 +128,15 @@ Submenu* submenu_alloc() { | ||||
|     view_set_input_callback(submenu->view, submenu_view_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|         submenu->view, | ||||
|         SubmenuModel * model, | ||||
|         { | ||||
|             SubmenuItemArray_init(model->items); | ||||
|             model->position = 0; | ||||
|             model->window_position = 0; | ||||
|             model->header = NULL; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return submenu; | ||||
| } | ||||
| @ -143,10 +145,7 @@ void submenu_free(Submenu* submenu) { | ||||
|     furi_assert(submenu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|             SubmenuItemArray_clear(model->items); | ||||
|             return true; | ||||
|         }); | ||||
|         submenu->view, SubmenuModel * model, { SubmenuItemArray_clear(model->items); }, true); | ||||
|     view_free(submenu->view); | ||||
|     free(submenu); | ||||
| } | ||||
| @ -167,32 +166,38 @@ void submenu_add_item( | ||||
|     furi_assert(submenu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|         submenu->view, | ||||
|         SubmenuModel * model, | ||||
|         { | ||||
|             item = SubmenuItemArray_push_new(model->items); | ||||
|             item->label = label; | ||||
|             item->index = index; | ||||
|             item->callback = callback; | ||||
|             item->callback_context = callback_context; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void submenu_reset(Submenu* submenu) { | ||||
|     furi_assert(submenu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|         submenu->view, | ||||
|         SubmenuModel * model, | ||||
|         { | ||||
|             SubmenuItemArray_reset(model->items); | ||||
|             model->position = 0; | ||||
|             model->window_position = 0; | ||||
|             model->header = NULL; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void submenu_set_selected_item(Submenu* submenu, uint32_t index) { | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|         submenu->view, | ||||
|         SubmenuModel * model, | ||||
|         { | ||||
|             uint32_t position = 0; | ||||
|             SubmenuItemArray_it_t it; | ||||
|             for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); | ||||
| @ -225,14 +230,15 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { | ||||
|                         (SubmenuItemArray_size(model->items) - items_on_screen); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void submenu_process_up(Submenu* submenu) { | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|         submenu->view, | ||||
|         SubmenuModel * model, | ||||
|         { | ||||
|             uint8_t items_on_screen = model->header ? 3 : 4; | ||||
|             if(model->position > 0) { | ||||
|                 model->position--; | ||||
| @ -246,13 +252,15 @@ void submenu_process_up(Submenu* submenu) { | ||||
|                     model->window_position = model->position - (items_on_screen - 1); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void submenu_process_down(Submenu* submenu) { | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|         submenu->view, | ||||
|         SubmenuModel * model, | ||||
|         { | ||||
|             uint8_t items_on_screen = model->header ? 3 : 4; | ||||
|             if(model->position < (SubmenuItemArray_size(model->items) - 1)) { | ||||
|                 model->position++; | ||||
| @ -265,20 +273,22 @@ void submenu_process_down(Submenu* submenu) { | ||||
|                 model->position = 0; | ||||
|                 model->window_position = 0; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void submenu_process_ok(Submenu* submenu) { | ||||
|     SubmenuItem* item = NULL; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|         submenu->view, | ||||
|         SubmenuModel * model, | ||||
|         { | ||||
|             if(model->position < (SubmenuItemArray_size(model->items))) { | ||||
|                 item = SubmenuItemArray_get(model->items, model->position); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     if(item && item->callback) { | ||||
|         item->callback(item->callback_context, item->index); | ||||
| @ -289,8 +299,5 @@ void submenu_set_header(Submenu* submenu, const char* header) { | ||||
|     furi_assert(submenu); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         submenu->view, (SubmenuModel * model) { | ||||
|             model->header = header; | ||||
|             return true; | ||||
|         }); | ||||
|         submenu->view, SubmenuModel * model, { model->header = header; }, true); | ||||
| } | ||||
|  | ||||
| @ -21,20 +21,24 @@ typedef struct { | ||||
| 
 | ||||
| static void text_box_process_down(TextBox* text_box) { | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|         text_box->view, | ||||
|         TextBoxModel * model, | ||||
|         { | ||||
|             if(model->scroll_pos < model->scroll_num - 1) { | ||||
|                 model->scroll_pos++; | ||||
|                 // Search next line start
 | ||||
|                 while(*model->text_pos++ != '\n') | ||||
|                     ; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void text_box_process_up(TextBox* text_box) { | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|         text_box->view, | ||||
|         TextBoxModel * model, | ||||
|         { | ||||
|             if(model->scroll_pos > 0) { | ||||
|                 model->scroll_pos--; | ||||
|                 // Reach last symbol of previous line
 | ||||
| @ -46,8 +50,8 @@ static void text_box_process_up(TextBox* text_box) { | ||||
|                     model->text_pos++; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { | ||||
| @ -137,13 +141,15 @@ TextBox* text_box_alloc() { | ||||
|     view_set_input_callback(text_box->view, text_box_view_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|         text_box->view, | ||||
|         TextBoxModel * model, | ||||
|         { | ||||
|             model->text = NULL; | ||||
|             model->text_formatted = furi_string_alloc_set(""); | ||||
|             model->formatted = false; | ||||
|             model->font = TextBoxFontText; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return text_box; | ||||
| } | ||||
| @ -152,10 +158,7 @@ void text_box_free(TextBox* text_box) { | ||||
|     furi_assert(text_box); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|             furi_string_free(model->text_formatted); | ||||
|             return true; | ||||
|         }); | ||||
|         text_box->view, TextBoxModel * model, { furi_string_free(model->text_formatted); }, true); | ||||
|     view_free(text_box->view); | ||||
|     free(text_box); | ||||
| } | ||||
| @ -169,13 +172,15 @@ void text_box_reset(TextBox* text_box) { | ||||
|     furi_assert(text_box); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|         text_box->view, | ||||
|         TextBoxModel * model, | ||||
|         { | ||||
|             model->text = NULL; | ||||
|             furi_string_set(model->text_formatted, ""); | ||||
|             model->font = TextBoxFontText; | ||||
|             model->focus = TextBoxFocusStart; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void text_box_set_text(TextBox* text_box, const char* text) { | ||||
| @ -183,31 +188,27 @@ void text_box_set_text(TextBox* text_box, const char* text) { | ||||
|     furi_assert(text); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|         text_box->view, | ||||
|         TextBoxModel * model, | ||||
|         { | ||||
|             model->text = text; | ||||
|             furi_string_reset(model->text_formatted); | ||||
|             furi_string_reserve(model->text_formatted, strlen(text)); | ||||
|             model->formatted = false; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void text_box_set_font(TextBox* text_box, TextBoxFont font) { | ||||
|     furi_assert(text_box); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|             model->font = font; | ||||
|             return true; | ||||
|         }); | ||||
|         text_box->view, TextBoxModel * model, { model->font = font; }, true); | ||||
| } | ||||
| 
 | ||||
| void text_box_set_focus(TextBox* text_box, TextBoxFocus focus) { | ||||
|     furi_assert(text_box); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_box->view, (TextBoxModel * model) { | ||||
|             model->focus = focus; | ||||
|             return true; | ||||
|         }); | ||||
|         text_box->view, TextBoxModel * model, { model->focus = focus; }, true); | ||||
| } | ||||
|  | ||||
| @ -429,10 +429,10 @@ void text_input_timer_callback(void* context) { | ||||
|     TextInput* text_input = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|             model->valadator_message_visible = false; | ||||
|             return true; | ||||
|         }); | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { model->valadator_message_visible = false; }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| TextInput* text_input_alloc() { | ||||
| @ -446,10 +446,10 @@ TextInput* text_input_alloc() { | ||||
|     text_input->timer = furi_timer_alloc(text_input_timer_callback, FuriTimerTypeOnce, text_input); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|             model->validator_text = furi_string_alloc(); | ||||
|             return false; | ||||
|         }); | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { model->validator_text = furi_string_alloc(); }, | ||||
|         false); | ||||
| 
 | ||||
|     text_input_reset(text_input); | ||||
| 
 | ||||
| @ -459,10 +459,10 @@ TextInput* text_input_alloc() { | ||||
| void text_input_free(TextInput* text_input) { | ||||
|     furi_assert(text_input); | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|             furi_string_free(model->validator_text); | ||||
|             return false; | ||||
|         }); | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { furi_string_free(model->validator_text); }, | ||||
|         false); | ||||
| 
 | ||||
|     // Send stop command
 | ||||
|     furi_timer_stop(text_input->timer); | ||||
| @ -477,7 +477,9 @@ void text_input_free(TextInput* text_input) { | ||||
| void text_input_reset(TextInput* text_input) { | ||||
|     furi_assert(text_input); | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { | ||||
|             model->text_buffer_size = 0; | ||||
|             model->header = ""; | ||||
|             model->selected_row = 0; | ||||
| @ -491,8 +493,8 @@ void text_input_reset(TextInput* text_input) { | ||||
|             model->validator_callback_context = NULL; | ||||
|             furi_string_reset(model->validator_text); | ||||
|             model->valadator_message_visible = false; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| View* text_input_get_view(TextInput* text_input) { | ||||
| @ -508,7 +510,9 @@ void text_input_set_result_callback( | ||||
|     size_t text_buffer_size, | ||||
|     bool clear_default_text) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { | ||||
|             model->callback = callback; | ||||
|             model->callback_context = callback_context; | ||||
|             model->text_buffer = text_buffer; | ||||
| @ -519,8 +523,8 @@ void text_input_set_result_callback( | ||||
|                 model->selected_row = 2; | ||||
|                 model->selected_column = 8; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void text_input_set_validator( | ||||
| @ -528,37 +532,36 @@ void text_input_set_validator( | ||||
|     TextInputValidatorCallback callback, | ||||
|     void* callback_context) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { | ||||
|             model->validator_callback = callback; | ||||
|             model->validator_callback_context = callback_context; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input) { | ||||
|     TextInputValidatorCallback validator_callback = NULL; | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|             validator_callback = model->validator_callback; | ||||
|             return false; | ||||
|         }); | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { validator_callback = model->validator_callback; }, | ||||
|         false); | ||||
|     return validator_callback; | ||||
| } | ||||
| 
 | ||||
| void* text_input_get_validator_callback_context(TextInput* text_input) { | ||||
|     void* validator_callback_context = NULL; | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|             validator_callback_context = model->validator_callback_context; | ||||
|             return false; | ||||
|         }); | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { validator_callback_context = model->validator_callback_context; }, | ||||
|         false); | ||||
|     return validator_callback_context; | ||||
| } | ||||
| 
 | ||||
| void text_input_set_header_text(TextInput* text_input, const char* text) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
|             model->header = text; | ||||
|             return true; | ||||
|         }); | ||||
|         text_input->view, TextInputModel * model, { model->header = text; }, true); | ||||
| } | ||||
|  | ||||
| @ -92,7 +92,9 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { | ||||
| 
 | ||||
| void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) { | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             uint8_t position = index; | ||||
|             if(position >= VariableItemArray_size(model->items)) { | ||||
|                 position = 0; | ||||
| @ -112,9 +114,8 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, | ||||
|                     model->window_position = (VariableItemArray_size(model->items) - 4); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) { | ||||
| @ -181,7 +182,9 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context) | ||||
| 
 | ||||
| void variable_item_list_process_up(VariableItemList* variable_item_list) { | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             uint8_t items_on_screen = 4; | ||||
|             if(model->position > 0) { | ||||
|                 model->position--; | ||||
| @ -195,13 +198,15 @@ void variable_item_list_process_up(VariableItemList* variable_item_list) { | ||||
|                     model->window_position = model->position - (items_on_screen - 1); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void variable_item_list_process_down(VariableItemList* variable_item_list) { | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             uint8_t items_on_screen = 4; | ||||
|             if(model->position < (VariableItemArray_size(model->items) - 1)) { | ||||
|                 model->position++; | ||||
| @ -214,8 +219,8 @@ void variable_item_list_process_down(VariableItemList* variable_item_list) { | ||||
|                 model->position = 0; | ||||
|                 model->window_position = 0; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) { | ||||
| @ -239,7 +244,9 @@ VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) | ||||
| 
 | ||||
| void variable_item_list_process_left(VariableItemList* variable_item_list) { | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             VariableItem* item = variable_item_list_get_selected_item(model); | ||||
|             if(item->current_value_index > 0) { | ||||
|                 item->current_value_index--; | ||||
| @ -247,13 +254,15 @@ void variable_item_list_process_left(VariableItemList* variable_item_list) { | ||||
|                     item->change_callback(item); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void variable_item_list_process_right(VariableItemList* variable_item_list) { | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             VariableItem* item = variable_item_list_get_selected_item(model); | ||||
|             if(item->current_value_index < (item->values_count - 1)) { | ||||
|                 item->current_value_index++; | ||||
| @ -261,18 +270,20 @@ void variable_item_list_process_right(VariableItemList* variable_item_list) { | ||||
|                     item->change_callback(item); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void variable_item_list_process_ok(VariableItemList* variable_item_list) { | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             if(variable_item_list->callback) { | ||||
|                 variable_item_list->callback(variable_item_list->context, model->position); | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| VariableItemList* variable_item_list_alloc() { | ||||
| @ -285,12 +296,14 @@ VariableItemList* variable_item_list_alloc() { | ||||
|     view_set_input_callback(variable_item_list->view, variable_item_list_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             VariableItemArray_init(model->items); | ||||
|             model->position = 0; | ||||
|             model->window_position = 0; | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return variable_item_list; | ||||
| } | ||||
| @ -299,15 +312,17 @@ void variable_item_list_free(VariableItemList* variable_item_list) { | ||||
|     furi_assert(variable_item_list); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             VariableItemArray_it_t it; | ||||
|             for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); | ||||
|                 VariableItemArray_next(it)) { | ||||
|                 furi_string_free(VariableItemArray_ref(it)->current_value_text); | ||||
|             } | ||||
|             VariableItemArray_clear(model->items); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
|     view_free(variable_item_list->view); | ||||
|     free(variable_item_list); | ||||
| } | ||||
| @ -316,15 +331,17 @@ void variable_item_list_reset(VariableItemList* variable_item_list) { | ||||
|     furi_assert(variable_item_list); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             VariableItemArray_it_t it; | ||||
|             for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); | ||||
|                 VariableItemArray_next(it)) { | ||||
|                 furi_string_free(VariableItemArray_ref(it)->current_value_text); | ||||
|             } | ||||
|             VariableItemArray_reset(model->items); | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| View* variable_item_list_get_view(VariableItemList* variable_item_list) { | ||||
| @ -343,7 +360,9 @@ VariableItem* variable_item_list_add( | ||||
|     furi_assert(variable_item_list); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             item = VariableItemArray_push_new(model->items); | ||||
|             item->label = label; | ||||
|             item->values_count = values_count; | ||||
| @ -351,8 +370,8 @@ VariableItem* variable_item_list_add( | ||||
|             item->context = context; | ||||
|             item->current_value_index = 0; | ||||
|             item->current_value_text = furi_string_alloc(); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return item; | ||||
| } | ||||
| @ -363,12 +382,14 @@ void variable_item_list_set_enter_callback( | ||||
|     void* context) { | ||||
|     furi_assert(callback); | ||||
|     with_view_model( | ||||
|         variable_item_list->view, (VariableItemListModel * model) { | ||||
|         variable_item_list->view, | ||||
|         VariableItemListModel * model, | ||||
|         { | ||||
|             UNUSED(model); | ||||
|             variable_item_list->callback = callback; | ||||
|             variable_item_list->context = context; | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) { | ||||
|  | ||||
| @ -36,7 +36,9 @@ static bool gui_widget_view_input_callback(InputEvent* event, void* context) { | ||||
| 
 | ||||
|     // Call all Widget Elements input handlers
 | ||||
|     with_view_model( | ||||
|         widget->view, (GuiWidgetModel * model) { | ||||
|         widget->view, | ||||
|         GuiWidgetModel * model, | ||||
|         { | ||||
|             ElementArray_it_t it; | ||||
|             ElementArray_it(it, model->element); | ||||
|             while(!ElementArray_end_p(it)) { | ||||
| @ -46,8 +48,8 @@ static bool gui_widget_view_input_callback(InputEvent* event, void* context) { | ||||
|                 } | ||||
|                 ElementArray_next(it); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| @ -61,10 +63,7 @@ Widget* widget_alloc() { | ||||
|     view_set_input_callback(widget->view, gui_widget_view_input_callback); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         widget->view, (GuiWidgetModel * model) { | ||||
|             ElementArray_init(model->element); | ||||
|             return true; | ||||
|         }); | ||||
|         widget->view, GuiWidgetModel * model, { ElementArray_init(model->element); }, true); | ||||
| 
 | ||||
|     return widget; | ||||
| } | ||||
| @ -73,7 +72,9 @@ void widget_reset(Widget* widget) { | ||||
|     furi_assert(widget); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         widget->view, (GuiWidgetModel * model) { | ||||
|         widget->view, | ||||
|         GuiWidgetModel * model, | ||||
|         { | ||||
|             ElementArray_it_t it; | ||||
|             ElementArray_it(it, model->element); | ||||
|             while(!ElementArray_end_p(it)) { | ||||
| @ -83,8 +84,8 @@ void widget_reset(Widget* widget) { | ||||
|                 ElementArray_next(it); | ||||
|             } | ||||
|             ElementArray_reset(model->element); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void widget_free(Widget* widget) { | ||||
| @ -93,10 +94,7 @@ void widget_free(Widget* widget) { | ||||
|     widget_reset(widget); | ||||
|     // Free elements container
 | ||||
|     with_view_model( | ||||
|         widget->view, (GuiWidgetModel * model) { | ||||
|             ElementArray_clear(model->element); | ||||
|             return true; | ||||
|         }); | ||||
|         widget->view, GuiWidgetModel * model, { ElementArray_clear(model->element); }, true); | ||||
| 
 | ||||
|     view_free(widget->view); | ||||
|     free(widget); | ||||
| @ -112,11 +110,13 @@ static void widget_add_element(Widget* widget, WidgetElement* element) { | ||||
|     furi_assert(element); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         widget->view, (GuiWidgetModel * model) { | ||||
|         widget->view, | ||||
|         GuiWidgetModel * model, | ||||
|         { | ||||
|             element->parent = widget; | ||||
|             ElementArray_push_back(model->element, element); | ||||
|             return true; | ||||
|         }); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void widget_add_string_multiline_element( | ||||
|  | ||||
| @ -211,25 +211,25 @@ void view_commit_model(View* view, bool update); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| #define with_view_model_cpp(view, type, var, function_body) \ | ||||
| #define with_view_model_cpp(view, type, var, code, update)  \ | ||||
|     {                                                       \ | ||||
|         type* p = static_cast<type*>(view_get_model(view)); \ | ||||
|         bool update = [&](type * var) function_body(p);     \ | ||||
|         type var = static_cast<type>(view_get_model(view)); \ | ||||
|         {code};                                             \ | ||||
|         view_commit_model(view, update);                    \ | ||||
|     } | ||||
| #else | ||||
| /** With clause for view model
 | ||||
|  * | ||||
|  * @param      view           View instance pointer | ||||
|  * @param      function_body  a (){} lambda declaration, executed within you | ||||
|  *                            parent function context | ||||
|  * @param      type           View model type | ||||
|  * @param      code           Code block that will be executed between model lock and unlock | ||||
|  * @param      update         Bool flag, if true, view will be updated after code block. Can be variable, so code block can decide if update is needed. | ||||
|  * | ||||
|  * @return     true if you want to emit view update, false otherwise | ||||
|  */ | ||||
| #define with_view_model(view, function_body)                      \ | ||||
|     {                                                             \ | ||||
|         void* p = view_get_model(view);                           \ | ||||
|         bool update = ({ bool __fn__ function_body __fn__; })(p); \ | ||||
|         view_commit_model(view, update);                          \ | ||||
| #define with_view_model(view, type, code, update) \ | ||||
|     {                                             \ | ||||
|         type = view_get_model(view);              \ | ||||
|         {code};                                   \ | ||||
|         view_commit_model(view, update);          \ | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| @ -87,19 +87,13 @@ View* power_off_get_view(PowerOff* power_off) { | ||||
| void power_off_set_time_left(PowerOff* power_off, uint8_t time_left) { | ||||
|     furi_assert(power_off); | ||||
|     with_view_model( | ||||
|         power_off->view, (PowerOffModel * model) { | ||||
|             model->time_left_sec = time_left; | ||||
|             return true; | ||||
|         }); | ||||
|         power_off->view, PowerOffModel * model, { model->time_left_sec = time_left; }, true); | ||||
| } | ||||
| 
 | ||||
| PowerOffResponse power_off_get_response(PowerOff* power_off) { | ||||
|     furi_assert(power_off); | ||||
|     PowerOffResponse response; | ||||
|     with_view_model( | ||||
|         power_off->view, (PowerOffModel * model) { | ||||
|             response = model->response; | ||||
|             return false; | ||||
|         }); | ||||
|         power_off->view, PowerOffModel * model, { response = model->response; }, false); | ||||
|     return response; | ||||
| } | ||||
|  | ||||
| @ -119,8 +119,8 @@ void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { | ||||
|     furi_assert(battery_info); | ||||
|     furi_assert(data); | ||||
|     with_view_model( | ||||
|         battery_info->view, (BatteryInfoModel * model) { | ||||
|             memcpy(model, data, sizeof(BatteryInfoModel)); | ||||
|             return true; | ||||
|         }); | ||||
|         battery_info->view, | ||||
|         BatteryInfoModel * model, | ||||
|         { memcpy(model, data, sizeof(BatteryInfoModel)); }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -28,22 +28,25 @@ void updater_main_model_set_state( | ||||
|     const char* message, | ||||
|     uint8_t progress, | ||||
|     bool failed) { | ||||
|     bool update = false; | ||||
|     with_view_model( | ||||
|         main_view->view, (UpdaterProgressModel * model) { | ||||
|         main_view->view, | ||||
|         UpdaterProgressModel * model, | ||||
|         { | ||||
|             model->failed = failed; | ||||
|             model->progress = progress; | ||||
|             if(furi_string_cmp_str(model->status, message)) { | ||||
|                 furi_string_set(model->status, message); | ||||
|                 model->rendered_progress = progress; | ||||
|                 return true; | ||||
|             } | ||||
|             if((model->rendered_progress > progress) || | ||||
|                ((progress - model->rendered_progress) > PROGRESS_RENDER_STEP)) { | ||||
|                 update = true; | ||||
|             } else if( | ||||
|                 (model->rendered_progress > progress) || | ||||
|                 ((progress - model->rendered_progress) > PROGRESS_RENDER_STEP)) { | ||||
|                 model->rendered_progress = progress; | ||||
|                 return true; | ||||
|                 update = true; | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
|         }, | ||||
|         update); | ||||
| } | ||||
| 
 | ||||
| View* updater_main_get_view(UpdaterMainView* main_view) { | ||||
| @ -104,10 +107,10 @@ UpdaterMainView* updater_main_alloc() { | ||||
|     view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(UpdaterProgressModel)); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         main_view->view, (UpdaterProgressModel * model) { | ||||
|             model->status = furi_string_alloc_set("Waiting for SD card"); | ||||
|             return true; | ||||
|         }); | ||||
|         main_view->view, | ||||
|         UpdaterProgressModel * model, | ||||
|         { model->status = furi_string_alloc_set("Waiting for SD card"); }, | ||||
|         true); | ||||
| 
 | ||||
|     view_set_context(main_view->view, main_view); | ||||
|     view_set_input_callback(main_view->view, updater_main_input); | ||||
| @ -119,10 +122,7 @@ UpdaterMainView* updater_main_alloc() { | ||||
| void updater_main_free(UpdaterMainView* main_view) { | ||||
|     furi_assert(main_view); | ||||
|     with_view_model( | ||||
|         main_view->view, (UpdaterProgressModel * model) { | ||||
|             furi_string_free(model->status); | ||||
|             return false; | ||||
|         }); | ||||
|         main_view->view, UpdaterProgressModel * model, { furi_string_free(model->status); }, false); | ||||
|     view_free(main_view->view); | ||||
|     free(main_view); | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sergey Gavrilov
						Sergey Gavrilov