mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-13 13:09:49 +04:00
Added a text input that only accepts full numbers (int) (#3350)
* Added a text input that only accepts full numbers (int) * Added to Gui sdk_headers and api_symbols in f7 and f18 * Fixed _Bool declarations in symbols csv * renamed int_input to number_input * Changed name & added example fap * Added a text input that only accepts full numbers (int) * Added to Gui sdk_headers and api_symbols in f7 and f18 * Changed name & added example fap * update for clearing views * GUI: Fix array out of bounds in menu exit (#3604) * GUI: Fix array out of bounds in menu exit * Gui: fix incorrect empty menu handling * Gui: add missing item check in menu ok handling * Gui: remove dead code from menu module * nfc app: add legacy keys for plantain cards (#3602) * refactoring test app, part 1 * Refactor test app, part 2 * Minor updates while travelling * Switched from const char to FuriString. Using Temp module copy for development to spare compile time * Option to limit number output with min and max values * Preparations for option to change number sign from + to - * Preparations for option to change number sign from + to - * Preparing for testing * counter automatic API version change * added trailing comma in application.fam ... because the lint check wants it¿ * removed unused callback NumberChangedCallback * change uint8_t to size_t in number_input_backspace_cb * Removal of unused view_stack in demo app * copied module to app folder for faster development (remove later) * Replaced all uint8_t with size_t... removed unused logic for selected_row < 0 * Optimize use of canvas_set_color * Remove alloc/free of furistring that actually is a pointer * Dynamic Header text with min/max in Example * Removed the need of useSign in Model * Number_input Removed sign from model, started transfer from text to int32_t * number_input FuriString in input_show_number * number_input FuriString in input_show_number * limiting inputs for min/max values * limiting inputs for min/max values * number_input change save button on invalid numbers * input_number update demo app to allow change of min/max * number input fine tuning * number_input, Remove temp development folder * number_input, fbt format * Bump CSV Files * Clear input if value is zero * number_input: handle null on header text * number_input: change keyboard values to char * number input: Remove static on char for header text, change numbers to INT32_MIN/INT32_MAX * number_input: removal of dead code * number_input: fix for crash if number_input not opened before free * number_input: added icon for example app * number_input: Replaced view for show_number with DialogEx * Number_input: FBT Format * number_input: bump csv versions * number_input: allow negative input if max_value is 0 * Number_input: linting / format * Removed dead code, fbt format * Examples: cleanup number input code * Examples: moar code cleanup in number input, simplify as much as possible, highlight incorrect input handling * Gui: correctly handle INT_MAX and INT_MIN * Gui: fix memory leak in number input module Co-authored-by: David Lee <david.lee@arcmedia.ch> Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com> Co-authored-by: WillyJL <49810075+Willy-JL@users.noreply.github.com> Co-authored-by: gornekich <n.gorbadey@gmail.com>
This commit is contained in:
7
applications/examples/example_number_input/ReadMe.md
Normal file
7
applications/examples/example_number_input/ReadMe.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Number Input
|
||||
|
||||
Simple keyboard that limits user inputs to a full number (integer). Useful to enforce correct entries without the need of intense validations after a user input.
|
||||
|
||||
Definition of min/max values is required. Numbers are of type int32_t. If negative numbers are allowed withing min - max, an additional button is displayed to switch the sign between + and -.
|
||||
|
||||
It is also possible to define a header text, shown in this example app with the 3 different input options.
|
||||
10
applications/examples/example_number_input/application.fam
Normal file
10
applications/examples/example_number_input/application.fam
Normal file
@@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="example_number_input",
|
||||
name="Example: Number Input",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_number_input",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_icon="example_number_input_10px.png",
|
||||
fap_category="Examples",
|
||||
)
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "example_number_input.h"
|
||||
|
||||
bool example_number_input_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
ExampleNumberInput* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool example_number_input_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ExampleNumberInput* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static ExampleNumberInput* example_number_input_alloc() {
|
||||
ExampleNumberInput* app = malloc(sizeof(ExampleNumberInput));
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&example_number_input_scene_handlers, app);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, example_number_input_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, example_number_input_back_event_callback);
|
||||
|
||||
app->number_input = number_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
ExampleNumberInputViewIdNumberInput,
|
||||
number_input_get_view(app->number_input));
|
||||
|
||||
app->dialog_ex = dialog_ex_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
ExampleNumberInputViewIdShowNumber,
|
||||
dialog_ex_get_view(app->dialog_ex));
|
||||
|
||||
app->current_number = 5;
|
||||
app->min_value = INT32_MIN;
|
||||
app->max_value = INT32_MAX;
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void example_number_input_free(ExampleNumberInput* app) {
|
||||
furi_assert(app);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ExampleNumberInputViewIdShowNumber);
|
||||
dialog_ex_free(app->dialog_ex);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);
|
||||
number_input_free(app->number_input);
|
||||
|
||||
scene_manager_free(app->scene_manager);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
app->gui = NULL;
|
||||
|
||||
//Remove whatever is left
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t example_number_input(void* p) {
|
||||
UNUSED(p);
|
||||
ExampleNumberInput* app = example_number_input_alloc();
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneShowNumber);
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
example_number_input_free(app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/number_input.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#include "scenes/example_number_input_scene.h"
|
||||
|
||||
typedef struct ExampleNumberInputShowNumber ExampleNumberInputShowNumber;
|
||||
|
||||
typedef enum {
|
||||
ExampleNumberInputViewIdShowNumber,
|
||||
ExampleNumberInputViewIdNumberInput,
|
||||
} ExampleNumberInputViewId;
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
|
||||
NumberInput* number_input;
|
||||
DialogEx* dialog_ex;
|
||||
|
||||
int32_t current_number;
|
||||
int32_t min_value;
|
||||
int32_t max_value;
|
||||
} ExampleNumberInput;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 87 B |
@@ -0,0 +1,30 @@
|
||||
#include "example_number_input_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const example_number_input_on_enter_handlers[])(void*) = {
|
||||
#include "example_number_input_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const example_number_input_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "example_number_input_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const example_number_input_on_exit_handlers[])(void* context) = {
|
||||
#include "example_number_input_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers example_number_input_scene_handlers = {
|
||||
.on_enter_handlers = example_number_input_on_enter_handlers,
|
||||
.on_event_handlers = example_number_input_on_event_handlers,
|
||||
.on_exit_handlers = example_number_input_on_exit_handlers,
|
||||
.scene_num = ExampleNumberInputSceneNum,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) ExampleNumberInputScene##id,
|
||||
typedef enum {
|
||||
#include "example_number_input_scene_config.h"
|
||||
ExampleNumberInputSceneNum,
|
||||
} ExampleNumberInputScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers example_number_input_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "example_number_input_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "example_number_input_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "example_number_input_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -0,0 +1,4 @@
|
||||
ADD_SCENE(example_number_input, input_number, InputNumber)
|
||||
ADD_SCENE(example_number_input, show_number, ShowNumber)
|
||||
ADD_SCENE(example_number_input, input_max, InputMax)
|
||||
ADD_SCENE(example_number_input, input_min, InputMin)
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "../example_number_input.h"
|
||||
|
||||
void example_number_input_scene_input_max_callback(void* context, int32_t number) {
|
||||
ExampleNumberInput* app = context;
|
||||
app->max_value = number;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, 0);
|
||||
}
|
||||
|
||||
void example_number_input_scene_input_max_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
ExampleNumberInput* app = context;
|
||||
NumberInput* number_input = app->number_input;
|
||||
|
||||
number_input_set_header_text(number_input, "Enter the maximum value");
|
||||
number_input_set_result_callback(
|
||||
number_input,
|
||||
example_number_input_scene_input_max_callback,
|
||||
context,
|
||||
app->max_value,
|
||||
app->min_value,
|
||||
INT32_MAX);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);
|
||||
}
|
||||
|
||||
bool example_number_input_scene_input_max_on_event(void* context, SceneManagerEvent event) {
|
||||
ExampleNumberInput* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
return true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void example_number_input_scene_input_max_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "../example_number_input.h"
|
||||
|
||||
void example_number_input_scene_input_min_callback(void* context, int32_t number) {
|
||||
ExampleNumberInput* app = context;
|
||||
app->min_value = number;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, 0);
|
||||
}
|
||||
|
||||
void example_number_input_scene_input_min_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
ExampleNumberInput* app = context;
|
||||
NumberInput* number_input = app->number_input;
|
||||
|
||||
number_input_set_header_text(number_input, "Enter the minimum value");
|
||||
number_input_set_result_callback(
|
||||
number_input,
|
||||
example_number_input_scene_input_min_callback,
|
||||
context,
|
||||
app->min_value,
|
||||
INT32_MIN,
|
||||
app->max_value);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);
|
||||
}
|
||||
|
||||
bool example_number_input_scene_input_min_on_event(void* context, SceneManagerEvent event) {
|
||||
ExampleNumberInput* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
return true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void example_number_input_scene_input_min_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "../example_number_input.h"
|
||||
|
||||
void example_number_input_scene_input_number_callback(void* context, int32_t number) {
|
||||
ExampleNumberInput* app = context;
|
||||
app->current_number = number;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, 0);
|
||||
}
|
||||
|
||||
void example_number_input_scene_input_number_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
ExampleNumberInput* app = context;
|
||||
NumberInput* number_input = app->number_input;
|
||||
|
||||
char str[50];
|
||||
snprintf(str, sizeof(str), "Set Number (%ld - %ld)", app->min_value, app->max_value);
|
||||
|
||||
number_input_set_header_text(number_input, str);
|
||||
number_input_set_result_callback(
|
||||
number_input,
|
||||
example_number_input_scene_input_number_callback,
|
||||
context,
|
||||
app->current_number,
|
||||
app->min_value,
|
||||
app->max_value);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdNumberInput);
|
||||
}
|
||||
|
||||
bool example_number_input_scene_input_number_on_event(void* context, SceneManagerEvent event) {
|
||||
ExampleNumberInput* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) { //Back button pressed
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
return true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void example_number_input_scene_input_number_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "../example_number_input.h"
|
||||
|
||||
static void
|
||||
example_number_input_scene_confirm_dialog_callback(DialogExResult result, void* context) {
|
||||
ExampleNumberInput* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||
}
|
||||
|
||||
static void example_number_input_scene_update_view(void* context) {
|
||||
ExampleNumberInput* app = context;
|
||||
DialogEx* dialog_ex = app->dialog_ex;
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "The number is", 64, 0, AlignCenter, AlignTop);
|
||||
|
||||
static char buffer[12]; //needs static for extended lifetime
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%ld", app->current_number);
|
||||
dialog_ex_set_text(dialog_ex, buffer, 64, 29, AlignCenter, AlignCenter);
|
||||
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Min");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Max");
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Change");
|
||||
|
||||
dialog_ex_set_result_callback(dialog_ex, example_number_input_scene_confirm_dialog_callback);
|
||||
dialog_ex_set_context(dialog_ex, app);
|
||||
}
|
||||
|
||||
void example_number_input_scene_show_number_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
ExampleNumberInput* app = context;
|
||||
|
||||
example_number_input_scene_update_view(app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ExampleNumberInputViewIdShowNumber);
|
||||
}
|
||||
|
||||
bool example_number_input_scene_show_number_on_event(void* context, SceneManagerEvent event) {
|
||||
ExampleNumberInput* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DialogExResultCenter:
|
||||
scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneInputNumber);
|
||||
consumed = true;
|
||||
break;
|
||||
case DialogExResultLeft:
|
||||
scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneInputMin);
|
||||
consumed = true;
|
||||
break;
|
||||
case DialogExResultRight:
|
||||
scene_manager_next_scene(app->scene_manager, ExampleNumberInputSceneInputMax);
|
||||
consumed = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void example_number_input_scene_show_number_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
Reference in New Issue
Block a user