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:
David Lee 2024-08-08 08:05:48 +02:00 committed by GitHub
parent f4122a924a
commit 741329a743
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 907 additions and 2 deletions

View 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.

View 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",
)

View file

@ -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;
}

View file

@ -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

View file

@ -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,
};

View file

@ -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

View file

@ -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)

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -19,6 +19,7 @@ App(
"view_holder.h",
"modules/button_menu.h",
"modules/byte_input.h",
"modules/number_input.h",
"modules/popup.h",
"modules/text_input.h",
"modules/widget.h",

View file

@ -0,0 +1,443 @@
#include "number_input.h"
#include <gui/elements.h>
#include <furi.h>
#include <assets_icons.h>
struct NumberInput {
View* view;
};
typedef struct {
const char text;
const size_t x;
const size_t y;
} NumberInputKey;
typedef struct {
FuriString* header;
FuriString* text_buffer;
int32_t current_number;
int32_t max_value;
int32_t min_value;
NumberInputCallback callback;
void* callback_context;
size_t selected_row;
size_t selected_column;
} NumberInputModel;
static const size_t keyboard_origin_x = 7;
static const size_t keyboard_origin_y = 31;
static const size_t keyboard_row_count = 2;
static const char enter_symbol = '\r';
static const char backspace_symbol = '\b';
static const char sign_symbol = '-';
static const NumberInputKey keyboard_keys_row_1[] = {
{'0', 0, 12},
{'1', 11, 12},
{'2', 22, 12},
{'3', 33, 12},
{'4', 44, 12},
{backspace_symbol, 103, 4},
};
static const NumberInputKey keyboard_keys_row_2[] = {
{'5', 0, 26},
{'6', 11, 26},
{'7', 22, 26},
{'8', 33, 26},
{'9', 44, 26},
{sign_symbol, 55, 17},
{enter_symbol, 95, 17},
};
static size_t number_input_get_row_size(size_t row_index) {
size_t row_size = 0;
switch(row_index + 1) {
case 1:
row_size = COUNT_OF(keyboard_keys_row_1);
break;
case 2:
row_size = COUNT_OF(keyboard_keys_row_2);
break;
default:
furi_crash();
}
return row_size;
}
static const NumberInputKey* number_input_get_row(size_t row_index) {
const NumberInputKey* row = NULL;
switch(row_index + 1) {
case 1:
row = keyboard_keys_row_1;
break;
case 2:
row = keyboard_keys_row_2;
break;
default:
furi_crash();
}
return row;
}
static void number_input_draw_input(Canvas* canvas, NumberInputModel* model) {
const size_t text_x = 8;
const size_t text_y = 25;
elements_slightly_rounded_frame(canvas, 6, 14, 116, 15);
canvas_draw_str(canvas, text_x, text_y, furi_string_get_cstr(model->text_buffer));
}
static bool number_input_use_sign(NumberInputModel* model) {
//only show sign button if allowed number range needs it
if(model->min_value < 0 && model->max_value >= 0) {
return true;
}
return false;
}
static void number_input_backspace_cb(NumberInputModel* model) {
size_t text_length = furi_string_utf8_length(model->text_buffer);
if(text_length < 1 || (text_length < 2 && model->current_number <= 0)) {
return;
}
furi_string_set_strn(
model->text_buffer, furi_string_get_cstr(model->text_buffer), text_length - 1);
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
}
static void number_input_handle_up(NumberInputModel* model) {
if(model->selected_row > 0) {
model->selected_row--;
if(model->selected_column > number_input_get_row_size(model->selected_row) - 1) {
model->selected_column = number_input_get_row_size(model->selected_row) - 1;
}
}
}
static void number_input_handle_down(NumberInputModel* model) {
if(model->selected_row < keyboard_row_count - 1) {
if(model->selected_column >= number_input_get_row_size(model->selected_row) - 1) {
model->selected_column = number_input_get_row_size(model->selected_row + 1) - 1;
}
model->selected_row += 1;
}
const NumberInputKey* keys = number_input_get_row(model->selected_row);
if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {
model->selected_column--;
}
}
static void number_input_handle_left(NumberInputModel* model) {
if(model->selected_column > 0) {
model->selected_column--;
} else {
model->selected_column = number_input_get_row_size(model->selected_row) - 1;
}
const NumberInputKey* keys = number_input_get_row(model->selected_row);
if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {
model->selected_column--;
}
}
static void number_input_handle_right(NumberInputModel* model) {
if(model->selected_column < number_input_get_row_size(model->selected_row) - 1) {
model->selected_column++;
} else {
model->selected_column = 0;
}
const NumberInputKey* keys = number_input_get_row(model->selected_row);
if(keys[model->selected_column].text == sign_symbol && !number_input_use_sign(model)) {
model->selected_column++;
}
}
static bool is_number_too_large(NumberInputModel* model) {
int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(value > (int64_t)model->max_value) {
return true;
}
return false;
}
static bool is_number_too_small(NumberInputModel* model) {
int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(value < (int64_t)model->min_value) {
return true;
}
return false;
}
static void number_input_sign(NumberInputModel* model) {
int32_t number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(number == 0 && furi_string_cmp_str(model->text_buffer, "-") != 0) {
furi_string_set_str(model->text_buffer, "-");
return;
}
number = number * -1;
furi_string_printf(model->text_buffer, "%ld", number);
if(is_number_too_large(model) || is_number_too_small(model)) {
furi_string_printf(model->text_buffer, "%ld", model->current_number);
return;
}
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(model->current_number == 0) {
furi_string_set_str(model->text_buffer, ""); //show empty if 0, better for usability
}
}
static void number_input_add_digit(NumberInputModel* model, char* newChar) {
furi_string_cat_str(model->text_buffer, newChar);
if((model->max_value >= 0 && is_number_too_large(model)) ||
(model->min_value < 0 && is_number_too_small(model))) {
//you still need to be able to type invalid numbers in some cases to reach valid numbers on later keypress
furi_string_printf(model->text_buffer, "%ld", model->current_number);
return;
}
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
if(model->current_number == 0) {
furi_string_reset(model->text_buffer);
}
}
static void number_input_handle_ok(NumberInputModel* model) {
char selected = number_input_get_row(model->selected_row)[model->selected_column].text;
char temp_str[2] = {selected, '\0'};
if(selected == enter_symbol) {
if(is_number_too_large(model) || is_number_too_small(model)) {
return; //Do nothing if number outside allowed range
}
model->current_number = strtol(furi_string_get_cstr(model->text_buffer), NULL, 10);
model->callback(model->callback_context, model->current_number);
} else if(selected == backspace_symbol) {
number_input_backspace_cb(model);
} else if(selected == sign_symbol) {
number_input_sign(model);
} else {
number_input_add_digit(model, temp_str);
}
}
static void number_input_view_draw_callback(Canvas* canvas, void* _model) {
NumberInputModel* model = _model;
number_input_draw_input(canvas, model);
if(!furi_string_empty(model->header)) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 9, furi_string_get_cstr(model->header));
}
canvas_set_font(canvas, FontKeyboard);
// Draw keyboard
for(size_t row = 0; row < keyboard_row_count; row++) {
const size_t column_count = number_input_get_row_size(row);
const NumberInputKey* keys = number_input_get_row(row);
for(size_t column = 0; column < column_count; column++) {
if(keys[column].text == sign_symbol && !number_input_use_sign(model)) {
continue;
}
if(keys[column].text == enter_symbol) {
if(is_number_too_small(model) || is_number_too_large(model)) {
//in some cases you need to be able to type a number smaller/larger than the limits (expl. min = 50, clear all and editor must allow to type 9 and later 0 for 90)
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySaveBlockedSelected_24x11);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySaveBlocked_24x11);
}
} else {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySaveSelected_24x11);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySave_24x11);
}
}
} else if(keys[column].text == backspace_symbol) {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeyBackspaceSelected_16x9);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeyBackspace_16x9);
}
} else if(keys[column].text == sign_symbol) {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySignSelected_21x11);
} else {
canvas_draw_icon(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
&I_KeySign_21x11);
}
} else {
if(model->selected_row == row && model->selected_column == column) {
canvas_draw_box(
canvas,
keyboard_origin_x + keys[column].x - 3,
keyboard_origin_y + keys[column].y - 10,
11,
13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_glyph(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
keys[column].text);
canvas_set_color(canvas, ColorBlack);
}
}
}
}
static bool number_input_view_input_callback(InputEvent* event, void* context) {
furi_assert(context);
NumberInput* number_input = context;
bool consumed = false;
// Fetch the model
NumberInputModel* model = view_get_model(number_input->view);
if(event->type == InputTypeShort || event->type == InputTypeLong ||
event->type == InputTypeRepeat) {
consumed = true;
switch(event->key) {
case InputKeyLeft:
number_input_handle_left(model);
break;
case InputKeyRight:
number_input_handle_right(model);
break;
case InputKeyUp:
number_input_handle_up(model);
break;
case InputKeyDown:
number_input_handle_down(model);
break;
case InputKeyOk:
number_input_handle_ok(model);
break;
default:
consumed = false;
break;
}
}
// commit view
view_commit_model(number_input->view, consumed);
return consumed;
}
NumberInput* number_input_alloc(void) {
NumberInput* number_input = malloc(sizeof(NumberInput));
number_input->view = view_alloc();
view_set_context(number_input->view, number_input);
view_allocate_model(number_input->view, ViewModelTypeLocking, sizeof(NumberInputModel));
view_set_draw_callback(number_input->view, number_input_view_draw_callback);
view_set_input_callback(number_input->view, number_input_view_input_callback);
with_view_model(
number_input->view,
NumberInputModel * model,
{
model->header = furi_string_alloc();
model->text_buffer = furi_string_alloc();
},
true);
return number_input;
}
void number_input_free(NumberInput* number_input) {
furi_check(number_input);
with_view_model(
number_input->view,
NumberInputModel * model,
{
furi_string_free(model->header);
furi_string_free(model->text_buffer);
},
true);
view_free(number_input->view);
free(number_input);
}
View* number_input_get_view(NumberInput* number_input) {
furi_check(number_input);
return number_input->view;
}
void number_input_set_result_callback(
NumberInput* number_input,
NumberInputCallback callback,
void* callback_context,
int32_t current_number,
int32_t min_value,
int32_t max_value) {
furi_check(number_input);
current_number = CLAMP(current_number, max_value, min_value);
with_view_model(
number_input->view,
NumberInputModel * model,
{
model->callback = callback;
model->callback_context = callback_context;
model->current_number = current_number;
furi_string_printf(model->text_buffer, "%ld", current_number);
model->min_value = min_value;
model->max_value = max_value;
},
true);
}
void number_input_set_header_text(NumberInput* number_input, const char* text) {
furi_check(number_input);
with_view_model(
number_input->view,
NumberInputModel * model,
{ furi_string_set(model->header, text); },
true);
}

View file

@ -0,0 +1,69 @@
/**
* @file number_input.h
* GUI: Integer string keyboard view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Number input anonymous structure */
typedef struct NumberInput NumberInput;
/** Callback to be called on save button press */
typedef void (*NumberInputCallback)(void* context, int32_t number);
/** Allocate and initialize Number input.
*
* This Number input is used to enter Numbers (Integers).
*
* @return NumberInput instance pointer
*/
NumberInput* number_input_alloc(void);
/** Deinitialize and free byte input
*
* @param number_input Number input instance
*/
void number_input_free(NumberInput* number_input);
/** Get byte input view
*
* @param number_input byte input instance
*
* @return View instance that can be used for embedding
*/
View* number_input_get_view(NumberInput* number_input);
/** Set byte input result callback
*
* @param number_input byte input instance
* @param input_callback input callback fn
* @param callback_context callback context
* @param[in] current_number The current number
* @param min_value Min number value
* @param max_value Max number value
*/
void number_input_set_result_callback(
NumberInput* number_input,
NumberInputCallback input_callback,
void* callback_context,
int32_t current_number,
int32_t min_value,
int32_t max_value);
/** Set byte input header text
*
* @param number_input byte input instance
* @param text text to be shown
*/
void number_input_set_header_text(NumberInput* number_input, const char* text);
#ifdef __cplusplus
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

View file

@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,72.0,,
Version,+,72.1,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
Header,+,applications/services/cli/cli.h,,
@ -13,6 +13,7 @@ Header,+,applications/services/gui/icon_i.h,,
Header,+,applications/services/gui/modules/button_menu.h,,
Header,+,applications/services/gui/modules/button_panel.h,,
Header,+,applications/services/gui/modules/byte_input.h,,
Header,+,applications/services/gui/modules/number_input.h,,
Header,+,applications/services/gui/modules/dialog_ex.h,,
Header,+,applications/services/gui/modules/empty_screen.h,,
Header,+,applications/services/gui/modules/file_browser.h,,
@ -722,6 +723,11 @@ Function,+,byte_input_free,void,ByteInput*
Function,+,byte_input_get_view,View*,ByteInput*
Function,+,byte_input_set_header_text,void,"ByteInput*, const char*"
Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t"
Function,+,number_input_alloc,NumberInput*,
Function,+,number_input_free,void,NumberInput*
Function,+,number_input_get_view,View*,NumberInput*
Function,+,number_input_set_header_text,void,"NumberInput*, const char*"
Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t"
Function,-,bzero,void,"void*, size_t"
Function,+,calloc,void*,"size_t, size_t"
Function,+,canvas_clear,void,Canvas*

1 entry status name type params
2 Version + 72.0 72.1
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/bt/bt_service/bt_keys_storage.h
5 Header + applications/services/cli/cli.h
13 Header + applications/services/gui/modules/button_menu.h
14 Header + applications/services/gui/modules/button_panel.h
15 Header + applications/services/gui/modules/byte_input.h
16 Header + applications/services/gui/modules/number_input.h
17 Header + applications/services/gui/modules/dialog_ex.h
18 Header + applications/services/gui/modules/empty_screen.h
19 Header + applications/services/gui/modules/file_browser.h
723 Function + byte_input_get_view View* ByteInput*
724 Function + byte_input_set_header_text void ByteInput*, const char*
725 Function + byte_input_set_result_callback void ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t
726 Function + number_input_alloc NumberInput*
727 Function + number_input_free void NumberInput*
728 Function + number_input_get_view View* NumberInput*
729 Function + number_input_set_header_text void NumberInput*, const char*
730 Function + number_input_set_result_callback void NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t
731 Function - bzero void void*, size_t
732 Function + calloc void* size_t, size_t
733 Function + canvas_clear void Canvas*

View file

@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,72.0,,
Version,+,72.1,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@ -20,6 +20,7 @@ Header,+,applications/services/gui/modules/file_browser.h,,
Header,+,applications/services/gui/modules/file_browser_worker.h,,
Header,+,applications/services/gui/modules/loading.h,,
Header,+,applications/services/gui/modules/menu.h,,
Header,+,applications/services/gui/modules/number_input.h,,
Header,+,applications/services/gui/modules/popup.h,,
Header,+,applications/services/gui/modules/submenu.h,,
Header,+,applications/services/gui/modules/text_box.h,,
@ -2815,6 +2816,11 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not
Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*"
Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*"
Function,-,nrand48,long,unsigned short[3]
Function,+,number_input_alloc,NumberInput*,
Function,+,number_input_free,void,NumberInput*
Function,+,number_input_get_view,View*,NumberInput*
Function,+,number_input_set_header_text,void,"NumberInput*, const char*"
Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t"
Function,-,on_exit,int,"void (*)(int, void*), void*"
Function,+,onewire_host_alloc,OneWireHost*,const GpioPin*
Function,+,onewire_host_free,void,OneWireHost*

1 entry status name type params
2 Version + 72.0 72.1
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
20 Header + applications/services/gui/modules/file_browser_worker.h
21 Header + applications/services/gui/modules/loading.h
22 Header + applications/services/gui/modules/menu.h
23 Header + applications/services/gui/modules/number_input.h
24 Header + applications/services/gui/modules/popup.h
25 Header + applications/services/gui/modules/submenu.h
26 Header + applications/services/gui/modules/text_box.h
2816 Function + notification_message void NotificationApp*, const NotificationSequence*
2817 Function + notification_message_block void NotificationApp*, const NotificationSequence*
2818 Function - nrand48 long unsigned short[3]
2819 Function + number_input_alloc NumberInput*
2820 Function + number_input_free void NumberInput*
2821 Function + number_input_get_view View* NumberInput*
2822 Function + number_input_set_header_text void NumberInput*, const char*
2823 Function + number_input_set_result_callback void NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t
2824 Function - on_exit int void (*)(int, void*), void*
2825 Function + onewire_host_alloc OneWireHost* const GpioPin*
2826 Function + onewire_host_free void OneWireHost*