Просмотр исходного кода

UartTerminal: This is so fucking retarded, stop it

why make a dogshit hex input which is a copy of byte input but looks
ugly and stores data as ascii rather than actual bytes and then make it
reliant on the other dogshit text input module even though theyre
completely different??? i cba to even fix this its depressing
Willy-JL 2 лет назад
Родитель
Сommit
b6bd74a55f

BIN
uart_terminal/assets/KeyBackspaceSelected_16x9.png


BIN
uart_terminal/assets/KeyBackspace_16x9.png


BIN
uart_terminal/assets/KeySaveSelected_24x11.png


BIN
uart_terminal/assets/KeySave_24x11.png


+ 7 - 8
uart_terminal/scenes/uart_terminal_scene_hex_input.c

@@ -10,16 +10,16 @@ void uart_terminal_scene_hex_input_on_enter(void* context) {
     UART_TerminalApp* app = context;
 
     // Setup view
-    ByteInput* hex_input = app->hex_input;
+    UART_TextInput* text_input = app->hex_input;
     // Add help message to header
-    byte_input_set_header_text(hex_input, "Send HEX packet to UART");
-    byte_input_set_result_callback(
-        hex_input,
+    uart_hex_input_set_header_text(text_input, "Send HEX packet to UART");
+    uart_hex_input_set_result_callback(
+        text_input,
         uart_terminal_scene_hex_input_callback,
-        NULL,
         app,
         app->text_input_store,
-        UART_TERMINAL_TEXT_INPUT_STORE_SIZE);
+        UART_TERMINAL_TEXT_INPUT_STORE_SIZE,
+        false);
 
     view_dispatcher_switch_to_view(app->view_dispatcher, UART_TerminalAppViewHexInput);
 }
@@ -43,6 +43,5 @@ bool uart_terminal_scene_hex_input_on_event(void* context, SceneManagerEvent eve
 void uart_terminal_scene_hex_input_on_exit(void* context) {
     UART_TerminalApp* app = context;
 
-    byte_input_set_result_callback(app->hex_input, NULL, NULL, NULL, NULL, 0);
-    byte_input_set_header_text(app->hex_input, "");
+    uart_hex_input_reset(app->hex_input);
 }

+ 520 - 0
uart_terminal/uart_hex_input.c

@@ -0,0 +1,520 @@
+#include "uart_hex_input.h"
+#include "uart_text_input.h"
+#include <gui/elements.h>
+#include "uart_terminal_icons.h"
+#include <assets_icons.h>
+#include "uart_terminal_app_i.h"
+#include <furi.h>
+
+struct UART_TextInput {
+    View* view;
+    FuriTimer* timer;
+};
+
+typedef struct {
+    const char text;
+    const uint8_t x;
+    const uint8_t y;
+} UART_TextInputKey;
+
+typedef struct {
+    const char* header;
+    char* text_buffer;
+    size_t text_buffer_size;
+    bool clear_default_text;
+
+    UART_TextInputCallback callback;
+    void* callback_context;
+
+    uint8_t selected_row;
+    uint8_t selected_column;
+
+    UART_TextInputValidatorCallback validator_callback;
+    void* validator_callback_context;
+    FuriString* validator_text;
+    bool valadator_message_visible;
+} UART_TextInputModel;
+
+static const uint8_t keyboard_origin_x = 5;
+static const uint8_t keyboard_origin_y = 28;
+static const uint8_t keyboard_row_count = 2;
+
+#define ENTER_KEY '\r'
+#define BACKSPACE_KEY '\b'
+
+static const UART_TextInputKey keyboard_keys_row_1[] = {
+    {'0', 0, 12},
+    {'1', 11, 12},
+    {'2', 22, 12},
+    {'3', 33, 12},
+    {'4', 44, 12},
+    {'5', 55, 12},
+    {'6', 66, 12},
+    {'7', 77, 12},
+    {BACKSPACE_KEY, 103, 4},
+};
+
+static const UART_TextInputKey keyboard_keys_row_2[] = {
+    {'8', 0, 26},
+    {'9', 11, 26},
+    {'A', 22, 26},
+    {'B', 33, 26},
+    {'C', 44, 26},
+    {'D', 55, 26},
+    {'E', 66, 26},
+    {'F', 77, 26},
+    {ENTER_KEY, 95, 17},
+};
+
+static uint8_t get_row_size(uint8_t row_index) {
+    uint8_t row_size = 0;
+
+    switch(row_index + 1) {
+    case 1:
+        row_size = sizeof(keyboard_keys_row_1) / sizeof(UART_TextInputKey);
+        break;
+    case 2:
+        row_size = sizeof(keyboard_keys_row_2) / sizeof(UART_TextInputKey);
+        break;
+    }
+
+    return row_size;
+}
+
+static const UART_TextInputKey* get_row(uint8_t row_index) {
+    const UART_TextInputKey* row = NULL;
+
+    switch(row_index + 1) {
+    case 1:
+        row = keyboard_keys_row_1;
+        break;
+    case 2:
+        row = keyboard_keys_row_2;
+        break;
+    }
+
+    return row;
+}
+
+static char get_selected_char(UART_TextInputModel* model) {
+    return get_row(model->selected_row)[model->selected_column].text;
+}
+
+static void uart_hex_input_backspace_cb(UART_TextInputModel* model) {
+    uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
+    if(text_length > 0) {
+        model->text_buffer[text_length - 1] = 0;
+    }
+}
+
+static void uart_hex_input_view_draw_callback(Canvas* canvas, void* _model) {
+    UART_TextInputModel* model = _model;
+    uint8_t needed_string_width = canvas_width(canvas) - 8;
+    uint8_t start_pos = 4;
+
+    const char* text = model->text_buffer;
+
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    canvas_draw_str(canvas, 2, 7, model->header);
+    elements_slightly_rounded_frame(canvas, 1, 8, 126, 12);
+
+    if(canvas_string_width(canvas, text) > needed_string_width) {
+        canvas_draw_str(canvas, start_pos, 17, "...");
+        start_pos += 6;
+        needed_string_width -= 8;
+    }
+
+    while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) {
+        text++;
+    }
+
+    if(model->clear_default_text) {
+        elements_slightly_rounded_box(
+            canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);
+        canvas_set_color(canvas, ColorWhite);
+    } else {
+        canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|");
+        canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|");
+    }
+    canvas_draw_str(canvas, start_pos, 17, text);
+
+    canvas_set_font(canvas, FontKeyboard);
+
+    for(uint8_t row = 0; row <= keyboard_row_count; row++) {
+        const uint8_t column_count = get_row_size(row);
+        const UART_TextInputKey* keys = get_row(row);
+
+        for(size_t column = 0; column < column_count; column++) {
+            if(keys[column].text == ENTER_KEY) {
+                canvas_set_color(canvas, ColorBlack);
+                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_KEY) {
+                canvas_set_color(canvas, ColorBlack);
+                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(model->selected_row == row && model->selected_column == column) {
+                    canvas_set_color(canvas, ColorBlack);
+                    canvas_draw_box(
+                        canvas,
+                        keyboard_origin_x + keys[column].x - 1,
+                        keyboard_origin_y + keys[column].y - 8,
+                        7,
+                        10);
+                    canvas_set_color(canvas, ColorWhite);
+                } else {
+                    canvas_set_color(canvas, ColorBlack);
+                }
+
+                canvas_draw_glyph(
+                    canvas,
+                    keyboard_origin_x + keys[column].x,
+                    keyboard_origin_y + keys[column].y,
+                    keys[column].text);
+            }
+        }
+    }
+    if(model->valadator_message_visible) {
+        canvas_set_font(canvas, FontSecondary);
+        canvas_set_color(canvas, ColorWhite);
+        canvas_draw_box(canvas, 8, 10, 110, 48);
+        canvas_set_color(canvas, ColorBlack);
+        canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
+        canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
+        canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
+        elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text));
+        canvas_set_font(canvas, FontKeyboard);
+    }
+}
+
+static void
+    uart_hex_input_handle_up(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_row > 0) {
+        model->selected_row--;
+        if(model->selected_column > get_row_size(model->selected_row) - 6) {
+            model->selected_column = model->selected_column + 1;
+        }
+    }
+}
+
+static void
+    uart_hex_input_handle_down(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_row < keyboard_row_count - 1) {
+        model->selected_row++;
+        if(model->selected_column > get_row_size(model->selected_row) - 4) {
+            model->selected_column = model->selected_column - 1;
+        }
+    }
+}
+
+static void
+    uart_hex_input_handle_left(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_column > 0) {
+        model->selected_column--;
+    } else {
+        model->selected_column = get_row_size(model->selected_row) - 1;
+    }
+}
+
+static void
+    uart_hex_input_handle_right(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_column < get_row_size(model->selected_row) - 1) {
+        model->selected_column++;
+    } else {
+        model->selected_column = 0;
+    }
+}
+
+static void uart_hex_input_handle_ok(
+    UART_TextInput* uart_text_input,
+    UART_TextInputModel* model,
+    bool shift) {
+    UNUSED(shift);
+    char selected = get_selected_char(model);
+    uint8_t text_length = strlen(model->text_buffer);
+
+    if(selected == ENTER_KEY) {
+        if(model->validator_callback &&
+           (!model->validator_callback(
+               model->text_buffer, model->validator_text, model->validator_callback_context))) {
+            model->valadator_message_visible = true;
+            furi_timer_start(uart_text_input->timer, furi_kernel_get_tick_frequency() * 4);
+        } else if(model->callback != 0 && text_length > 0) {
+            model->callback(model->callback_context);
+        }
+    } else if(selected == BACKSPACE_KEY) {
+        uart_hex_input_backspace_cb(model);
+    } else {
+        if(model->clear_default_text) {
+            text_length = 0;
+        }
+        if(text_length < (model->text_buffer_size - 1)) {
+            model->text_buffer[text_length] = selected;
+            model->text_buffer[text_length + 1] = 0;
+        }
+    }
+    model->clear_default_text = false;
+}
+
+static bool uart_hex_input_view_input_callback(InputEvent* event, void* context) {
+    UART_TextInput* uart_text_input = context;
+    furi_assert(uart_text_input);
+
+    bool consumed = false;
+
+    // Acquire model
+    UART_TextInputModel* model = view_get_model(uart_text_input->view);
+
+    if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
+       model->valadator_message_visible) {
+        model->valadator_message_visible = false;
+        consumed = true;
+    } else if(event->type == InputTypeShort) {
+        consumed = true;
+        switch(event->key) {
+        case InputKeyUp:
+            uart_hex_input_handle_up(uart_text_input, model);
+            break;
+        case InputKeyDown:
+            uart_hex_input_handle_down(uart_text_input, model);
+            break;
+        case InputKeyLeft:
+            uart_hex_input_handle_left(uart_text_input, model);
+            break;
+        case InputKeyRight:
+            uart_hex_input_handle_right(uart_text_input, model);
+            break;
+        case InputKeyOk:
+            uart_hex_input_handle_ok(uart_text_input, model, false);
+            break;
+        default:
+            consumed = false;
+            break;
+        }
+    } else if(event->type == InputTypeLong) {
+        consumed = true;
+        switch(event->key) {
+        case InputKeyUp:
+            uart_hex_input_handle_up(uart_text_input, model);
+            break;
+        case InputKeyDown:
+            uart_hex_input_handle_down(uart_text_input, model);
+            break;
+        case InputKeyLeft:
+            uart_hex_input_handle_left(uart_text_input, model);
+            break;
+        case InputKeyRight:
+            uart_hex_input_handle_right(uart_text_input, model);
+            break;
+        case InputKeyOk:
+            uart_hex_input_handle_ok(uart_text_input, model, true);
+            break;
+        case InputKeyBack:
+            uart_hex_input_backspace_cb(model);
+            break;
+        default:
+            consumed = false;
+            break;
+        }
+    } else if(event->type == InputTypeRepeat) {
+        consumed = true;
+        switch(event->key) {
+        case InputKeyUp:
+            uart_hex_input_handle_up(uart_text_input, model);
+            break;
+        case InputKeyDown:
+            uart_hex_input_handle_down(uart_text_input, model);
+            break;
+        case InputKeyLeft:
+            uart_hex_input_handle_left(uart_text_input, model);
+            break;
+        case InputKeyRight:
+            uart_hex_input_handle_right(uart_text_input, model);
+            break;
+        case InputKeyBack:
+            uart_hex_input_backspace_cb(model);
+            break;
+        default:
+            consumed = false;
+            break;
+        }
+    }
+
+    // Commit model
+    view_commit_model(uart_text_input->view, consumed);
+
+    return consumed;
+}
+
+void uart_hex_input_timer_callback(void* context) {
+    furi_assert(context);
+    UART_TextInput* uart_text_input = context;
+
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { model->valadator_message_visible = false; },
+        true);
+}
+
+UART_TextInput* uart_hex_input_alloc() {
+    UART_TextInput* uart_text_input = malloc(sizeof(UART_TextInput));
+    uart_text_input->view = view_alloc();
+    view_set_context(uart_text_input->view, uart_text_input);
+    view_allocate_model(uart_text_input->view, ViewModelTypeLocking, sizeof(UART_TextInputModel));
+    view_set_draw_callback(uart_text_input->view, uart_hex_input_view_draw_callback);
+    view_set_input_callback(uart_text_input->view, uart_hex_input_view_input_callback);
+
+    uart_text_input->timer =
+        furi_timer_alloc(uart_hex_input_timer_callback, FuriTimerTypeOnce, uart_text_input);
+
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { model->validator_text = furi_string_alloc(); },
+        false);
+
+    uart_text_input_reset(uart_text_input);
+
+    return uart_text_input;
+}
+
+void uart_hex_input_free(UART_TextInput* uart_text_input) {
+    furi_assert(uart_text_input);
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { furi_string_free(model->validator_text); },
+        false);
+
+    // Send stop command
+    furi_timer_stop(uart_text_input->timer);
+    // Release allocated memory
+    furi_timer_free(uart_text_input->timer);
+
+    view_free(uart_text_input->view);
+
+    free(uart_text_input);
+}
+
+void uart_hex_input_reset(UART_TextInput* uart_text_input) {
+    furi_assert(uart_text_input);
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        {
+            model->text_buffer_size = 0;
+            model->header = "";
+            model->selected_row = 0;
+            model->selected_column = 0;
+            model->clear_default_text = false;
+            model->text_buffer = NULL;
+            model->text_buffer_size = 0;
+            model->callback = NULL;
+            model->callback_context = NULL;
+            model->validator_callback = NULL;
+            model->validator_callback_context = NULL;
+            furi_string_reset(model->validator_text);
+            model->valadator_message_visible = false;
+        },
+        true);
+}
+
+View* uart_hex_input_get_view(UART_TextInput* uart_text_input) {
+    furi_assert(uart_text_input);
+    return uart_text_input->view;
+}
+
+void uart_hex_input_set_result_callback(
+    UART_TextInput* uart_text_input,
+    UART_TextInputCallback callback,
+    void* callback_context,
+    char* text_buffer,
+    size_t text_buffer_size,
+    bool clear_default_text) {
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        {
+            model->callback = callback;
+            model->callback_context = callback_context;
+            model->text_buffer = text_buffer;
+            model->text_buffer_size = text_buffer_size;
+            model->clear_default_text = clear_default_text;
+            if(text_buffer && text_buffer[0] != '\0') {
+                // Set focus on Save
+                model->selected_row = 1;
+                model->selected_column = 8;
+            }
+        },
+        true);
+}
+
+void uart_hex_input_set_validator(
+    UART_TextInput* uart_text_input,
+    UART_TextInputValidatorCallback callback,
+    void* callback_context) {
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        {
+            model->validator_callback = callback;
+            model->validator_callback_context = callback_context;
+        },
+        true);
+}
+
+UART_TextInputValidatorCallback
+    uart_hex_input_get_validator_callback(UART_TextInput* uart_text_input) {
+    UART_TextInputValidatorCallback validator_callback = NULL;
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { validator_callback = model->validator_callback; },
+        false);
+    return validator_callback;
+}
+
+void* uart_hex_input_get_validator_callback_context(UART_TextInput* uart_text_input) {
+    void* validator_callback_context = NULL;
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { validator_callback_context = model->validator_callback_context; },
+        false);
+    return validator_callback_context;
+}
+
+void uart_hex_input_set_header_text(UART_TextInput* uart_text_input, const char* text) {
+    with_view_model(
+        uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true);
+}

+ 81 - 0
uart_terminal/uart_hex_input.h

@@ -0,0 +1,81 @@
+#pragma once
+
+#include <gui/view.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Text input anonymous structure */
+typedef struct UART_TextInput UART_TextInput;
+typedef void (*UART_TextInputCallback)(void* context);
+typedef bool (*UART_TextInputValidatorCallback)(const char* text, FuriString* error, void* context);
+
+/** Allocate and initialize text input 
+ * 
+ * This text input is used to enter string
+ *
+ * @return     UART_TextInput instance
+ */
+UART_TextInput* uart_hex_input_alloc();
+
+/** Deinitialize and free text input
+ *
+ * @param      uart_text_input  UART_TextInput instance
+ */
+void uart_hex_input_free(UART_TextInput* uart_text_input);
+
+/** Clean text input view Note: this function does not free memory
+ *
+ * @param      uart_text_input  Text input instance
+ */
+void uart_hex_input_reset(UART_TextInput* uart_text_input);
+
+/** Get text input view
+ *
+ * @param      uart_text_input  UART_TextInput instance
+ *
+ * @return     View instance that can be used for embedding
+ */
+View* uart_hex_input_get_view(UART_TextInput* uart_text_input);
+
+/** Set text input result callback
+ *
+ * @param      uart_text_input          UART_TextInput instance
+ * @param      callback            callback fn
+ * @param      callback_context    callback context
+ * @param      text_buffer         pointer to YOUR text buffer, that we going
+ *                                 to modify
+ * @param      text_buffer_size    YOUR text buffer size in bytes. Max string
+ *                                 length will be text_buffer_size-1.
+ * @param      clear_default_text  clear text from text_buffer on first OK
+ *                                 event
+ */
+void uart_hex_input_set_result_callback(
+    UART_TextInput* uart_text_input,
+    UART_TextInputCallback callback,
+    void* callback_context,
+    char* text_buffer,
+    size_t text_buffer_size,
+    bool clear_default_text);
+
+void uart_hex_input_set_validator(
+    UART_TextInput* uart_text_input,
+    UART_TextInputValidatorCallback callback,
+    void* callback_context);
+
+UART_TextInputValidatorCallback
+    uart_hex_input_get_validator_callback(UART_TextInput* uart_text_input);
+
+void* uart_hex_input_get_validator_callback_context(UART_TextInput* uart_text_input);
+
+/** Set text input header text
+ *
+ * @param      uart_text_input  UART_TextInput instance
+ * @param      text        text to be shown
+ */
+void uart_hex_input_set_header_text(UART_TextInput* uart_text_input, const char* text);
+
+#ifdef __cplusplus
+}
+#endif

+ 6 - 3
uart_terminal/uart_terminal_app.c

@@ -67,9 +67,12 @@ UART_TerminalApp* uart_terminal_app_alloc() {
     view_dispatcher_add_view(
         app->view_dispatcher, UART_TerminalAppViewTextInput, text_input_get_view(app->text_input));
 
-    app->hex_input = byte_input_alloc();
+    app->hex_input = uart_hex_input_alloc();
     view_dispatcher_add_view(
-        app->view_dispatcher, UART_TerminalAppViewHexInput, byte_input_get_view(app->hex_input));
+        app->view_dispatcher,
+        UART_TerminalAppViewHexInput,
+        uart_hex_input_get_view(app->hex_input)
+        );
 
     app->setup_selected_option_index[BAUDRATE_ITEM_IDX] = DEFAULT_BAUDRATE_OPT_IDX;
 
@@ -93,7 +96,7 @@ void uart_terminal_app_free(UART_TerminalApp* app) {
     text_box_free(app->text_box);
     furi_string_free(app->text_box_store);
     text_input_free(app->text_input);
-    byte_input_free(app->hex_input);
+    uart_hex_input_free(app->hex_input);
 
     // View dispatcher
     view_dispatcher_free(app->view_dispatcher);

+ 2 - 2
uart_terminal/uart_terminal_app_i.h

@@ -12,7 +12,7 @@
 #include <gui/modules/widget.h>
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/text_input.h>
-#include <gui/modules/byte_input.h>
+#include "uart_hex_input.h"
 
 #define START_MENU_ITEMS (7)
 #define SETUP_MENU_ITEMS (3)
@@ -30,7 +30,7 @@ struct UART_TerminalApp {
     size_t text_box_store_strlen;
     TextBox* text_box;
     TextInput* text_input;
-    ByteInput* hex_input;
+    UART_TextInput* hex_input;
     Widget* widget;
     VariableItemList* var_item_list;
     UART_TerminalUart* uart;

+ 684 - 0
uart_terminal/uart_text_input.c

@@ -0,0 +1,684 @@
+#include "uart_text_input.h"
+#include <gui/elements.h>
+#include "uart_terminal_icons.h"
+#include <assets_icons.h>
+#include "uart_terminal_app_i.h"
+#include <furi.h>
+
+struct UART_TextInput {
+    View* view;
+    FuriTimer* timer;
+};
+
+typedef struct {
+    const char text;
+    const uint8_t x;
+    const uint8_t y;
+} UART_TextInputKey;
+
+typedef struct {
+    const char* header;
+    char* text_buffer;
+    size_t text_buffer_size;
+    bool clear_default_text;
+
+    UART_TextInputCallback callback;
+    void* callback_context;
+
+    uint8_t selected_row;
+    uint8_t selected_column;
+
+    UART_TextInputValidatorCallback validator_callback;
+    void* validator_callback_context;
+    FuriString* validator_text;
+    bool valadator_message_visible;
+} UART_TextInputModel;
+
+static const uint8_t keyboard_origin_x = 1;
+static const uint8_t keyboard_origin_y = 29;
+static const uint8_t keyboard_row_count = 4;
+
+#define mode_AT "Send AT command to UART"
+
+#define ENTER_KEY '\r'
+#define BACKSPACE_KEY '\b'
+
+static const UART_TextInputKey keyboard_keys_row_1[] = {
+    {'{', 1, 0},
+    {'(', 9, 0},
+    {'[', 17, 0},
+    {'|', 25, 0},
+    {'@', 33, 0},
+    {'&', 41, 0},
+    {'#', 49, 0},
+    {';', 57, 0},
+    {'^', 65, 0},
+    {'*', 73, 0},
+    {'`', 81, 0},
+    {'"', 89, 0},
+    {'~', 97, 0},
+    {'\'', 105, 0},
+    {'.', 113, 0},
+    {'/', 120, 0},
+};
+
+static const UART_TextInputKey keyboard_keys_row_2[] = {
+    {'q', 1, 10},
+    {'w', 9, 10},
+    {'e', 17, 10},
+    {'r', 25, 10},
+    {'t', 33, 10},
+    {'y', 41, 10},
+    {'u', 49, 10},
+    {'i', 57, 10},
+    {'o', 65, 10},
+    {'p', 73, 10},
+    {'0', 81, 10},
+    {'1', 89, 10},
+    {'2', 97, 10},
+    {'3', 105, 10},
+    {'=', 113, 10},
+    {'-', 120, 10},
+};
+
+static const UART_TextInputKey keyboard_keys_row_3[] = {
+    {'a', 1, 21},
+    {'s', 9, 21},
+    {'d', 18, 21},
+    {'f', 25, 21},
+    {'g', 33, 21},
+    {'h', 41, 21},
+    {'j', 49, 21},
+    {'k', 57, 21},
+    {'l', 65, 21},
+    {BACKSPACE_KEY, 72, 13},
+    {'4', 89, 21},
+    {'5', 97, 21},
+    {'6', 105, 21},
+    {'$', 113, 21},
+    {'%', 120, 21},
+
+};
+
+static const UART_TextInputKey keyboard_keys_row_4[] = {
+    {'z', 1, 33},
+    {'x', 9, 33},
+    {'c', 18, 33},
+    {'v', 25, 33},
+    {'b', 33, 33},
+    {'n', 41, 33},
+    {'m', 49, 33},
+    {'_', 57, 33},
+    {ENTER_KEY, 64, 24},
+    {'7', 89, 33},
+    {'8', 97, 33},
+    {'9', 105, 33},
+    {'!', 113, 33},
+    {'+', 120, 33},
+};
+
+static uint8_t get_row_size(uint8_t row_index) {
+    uint8_t row_size = 0;
+
+    switch(row_index + 1) {
+    case 1:
+        row_size = sizeof(keyboard_keys_row_1) / sizeof(UART_TextInputKey);
+        break;
+    case 2:
+        row_size = sizeof(keyboard_keys_row_2) / sizeof(UART_TextInputKey);
+        break;
+    case 3:
+        row_size = sizeof(keyboard_keys_row_3) / sizeof(UART_TextInputKey);
+        break;
+    case 4:
+        row_size = sizeof(keyboard_keys_row_4) / sizeof(UART_TextInputKey);
+        break;
+    }
+
+    return row_size;
+}
+
+static const UART_TextInputKey* get_row(uint8_t row_index) {
+    const UART_TextInputKey* row = NULL;
+
+    switch(row_index + 1) {
+    case 1:
+        row = keyboard_keys_row_1;
+        break;
+    case 2:
+        row = keyboard_keys_row_2;
+        break;
+    case 3:
+        row = keyboard_keys_row_3;
+        break;
+    case 4:
+        row = keyboard_keys_row_4;
+        break;
+    }
+
+    return row;
+}
+
+static char get_selected_char(UART_TextInputModel* model) {
+    return get_row(model->selected_row)[model->selected_column].text;
+}
+
+static bool char_is_lowercase(char letter) {
+    return (letter >= 0x61 && letter <= 0x7A);
+}
+
+static bool char_is_uppercase(char letter) {
+    return (letter >= 0x41 && letter <= 0x5A);
+}
+
+static char char_to_lowercase(const char letter) {
+    switch(letter) {
+    case ' ':
+        return 0x5f;
+        break;
+    case ')':
+        return 0x28;
+        break;
+    case '}':
+        return 0x7b;
+        break;
+    case ']':
+        return 0x5b;
+        break;
+    case '\\':
+        return 0x2f;
+        break;
+    case ':':
+        return 0x3b;
+        break;
+    case ',':
+        return 0x2e;
+        break;
+    case '?':
+        return 0x21;
+        break;
+    case '>':
+        return 0x3c;
+        break;
+    }
+    if(char_is_uppercase(letter)) {
+        return (letter + 0x20);
+    } else {
+        return letter;
+    }
+}
+
+static char char_to_uppercase(const char letter) {
+    switch(letter) {
+    case '_':
+        return 0x20;
+        break;
+    case '(':
+        return 0x29;
+        break;
+    case '{':
+        return 0x7d;
+        break;
+    case '[':
+        return 0x5d;
+        break;
+    case '/':
+        return 0x5c;
+        break;
+    case ';':
+        return 0x3a;
+        break;
+    case '.':
+        return 0x2c;
+        break;
+    case '!':
+        return 0x3f;
+        break;
+    case '<':
+        return 0x3e;
+        break;
+    }
+    if(char_is_lowercase(letter)) {
+        return (letter - 0x20);
+    } else {
+        return letter;
+    }
+}
+
+static void uart_text_input_backspace_cb(UART_TextInputModel* model) {
+    uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
+    if(text_length > 0) {
+        model->text_buffer[text_length - 1] = 0;
+    }
+}
+
+static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) {
+    UART_TextInputModel* model = _model;
+    //uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
+    uint8_t needed_string_width = canvas_width(canvas) - 8;
+    uint8_t start_pos = 4;
+
+    const char* text = model->text_buffer;
+
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    canvas_draw_str(canvas, 2, 7, model->header);
+    elements_slightly_rounded_frame(canvas, 1, 8, 126, 12);
+
+    if(canvas_string_width(canvas, text) > needed_string_width) {
+        canvas_draw_str(canvas, start_pos, 17, "...");
+        start_pos += 6;
+        needed_string_width -= 8;
+    }
+
+    while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) {
+        text++;
+    }
+
+    if(model->clear_default_text) {
+        elements_slightly_rounded_box(
+            canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);
+        canvas_set_color(canvas, ColorWhite);
+    } else {
+        canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|");
+        canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|");
+    }
+    canvas_draw_str(canvas, start_pos, 17, text);
+
+    canvas_set_font(canvas, FontKeyboard);
+
+    for(uint8_t row = 0; row <= keyboard_row_count; row++) {
+        const uint8_t column_count = get_row_size(row);
+        const UART_TextInputKey* keys = get_row(row);
+
+        for(size_t column = 0; column < column_count; column++) {
+            if(keys[column].text == ENTER_KEY) {
+                canvas_set_color(canvas, ColorBlack);
+                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_KEY) {
+                canvas_set_color(canvas, ColorBlack);
+                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(model->selected_row == row && model->selected_column == column) {
+                    canvas_set_color(canvas, ColorBlack);
+                    canvas_draw_box(
+                        canvas,
+                        keyboard_origin_x + keys[column].x - 1,
+                        keyboard_origin_y + keys[column].y - 8,
+                        7,
+                        10);
+                    canvas_set_color(canvas, ColorWhite);
+                } else {
+                    canvas_set_color(canvas, ColorBlack);
+                }
+                if(0 == strcmp(model->header, mode_AT)) {
+                    canvas_draw_glyph(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        char_to_uppercase(keys[column].text));
+                } else {
+                    canvas_draw_glyph(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        keys[column].text);
+                }
+            }
+        }
+    }
+    if(model->valadator_message_visible) {
+        canvas_set_font(canvas, FontSecondary);
+        canvas_set_color(canvas, ColorWhite);
+        canvas_draw_box(canvas, 8, 10, 110, 48);
+        canvas_set_color(canvas, ColorBlack);
+        canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
+        canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
+        canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
+        elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text));
+        canvas_set_font(canvas, FontKeyboard);
+    }
+}
+
+static void
+    uart_text_input_handle_up(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_row > 0) {
+        model->selected_row--;
+        if(model->selected_column > get_row_size(model->selected_row) - 6) {
+            model->selected_column = model->selected_column + 1;
+        }
+    }
+}
+
+static void
+    uart_text_input_handle_down(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_row < keyboard_row_count - 1) {
+        model->selected_row++;
+        if(model->selected_column > get_row_size(model->selected_row) - 4) {
+            model->selected_column = model->selected_column - 1;
+        }
+    }
+}
+
+static void
+    uart_text_input_handle_left(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_column > 0) {
+        model->selected_column--;
+    } else {
+        model->selected_column = get_row_size(model->selected_row) - 1;
+    }
+}
+
+static void
+    uart_text_input_handle_right(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
+    UNUSED(uart_text_input);
+    if(model->selected_column < get_row_size(model->selected_row) - 1) {
+        model->selected_column++;
+    } else {
+        model->selected_column = 0;
+    }
+}
+
+static void uart_text_input_handle_ok(
+    UART_TextInput* uart_text_input,
+    UART_TextInputModel* model,
+    bool shift) {
+    char selected = get_selected_char(model);
+    uint8_t text_length = strlen(model->text_buffer);
+
+    if(0 == strcmp(model->header, mode_AT)) {
+        selected = char_to_uppercase(selected);
+    }
+
+    if(shift) {
+        if(0 == strcmp(model->header, mode_AT)) {
+            selected = char_to_lowercase(selected);
+        } else {
+            selected = char_to_uppercase(selected);
+        }
+    }
+
+    if(selected == ENTER_KEY) {
+        if(model->validator_callback &&
+           (!model->validator_callback(
+               model->text_buffer, model->validator_text, model->validator_callback_context))) {
+            model->valadator_message_visible = true;
+            furi_timer_start(uart_text_input->timer, furi_kernel_get_tick_frequency() * 4);
+        } else if(model->callback != 0 && text_length > 0) {
+            model->callback(model->callback_context);
+        }
+    } else if(selected == BACKSPACE_KEY) {
+        uart_text_input_backspace_cb(model);
+    } else {
+        if(model->clear_default_text) {
+            text_length = 0;
+        }
+        if(text_length < (model->text_buffer_size - 1)) {
+            model->text_buffer[text_length] = selected;
+            model->text_buffer[text_length + 1] = 0;
+        }
+    }
+    model->clear_default_text = false;
+}
+
+static bool uart_text_input_view_input_callback(InputEvent* event, void* context) {
+    UART_TextInput* uart_text_input = context;
+    furi_assert(uart_text_input);
+
+    bool consumed = false;
+
+    // Acquire model
+    UART_TextInputModel* model = view_get_model(uart_text_input->view);
+
+    if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
+       model->valadator_message_visible) {
+        model->valadator_message_visible = false;
+        consumed = true;
+    } else if(event->type == InputTypeShort) {
+        consumed = true;
+        switch(event->key) {
+        case InputKeyUp:
+            uart_text_input_handle_up(uart_text_input, model);
+            break;
+        case InputKeyDown:
+            uart_text_input_handle_down(uart_text_input, model);
+            break;
+        case InputKeyLeft:
+            uart_text_input_handle_left(uart_text_input, model);
+            break;
+        case InputKeyRight:
+            uart_text_input_handle_right(uart_text_input, model);
+            break;
+        case InputKeyOk:
+            uart_text_input_handle_ok(uart_text_input, model, false);
+            break;
+        default:
+            consumed = false;
+            break;
+        }
+    } else if(event->type == InputTypeLong) {
+        consumed = true;
+        switch(event->key) {
+        case InputKeyUp:
+            uart_text_input_handle_up(uart_text_input, model);
+            break;
+        case InputKeyDown:
+            uart_text_input_handle_down(uart_text_input, model);
+            break;
+        case InputKeyLeft:
+            uart_text_input_handle_left(uart_text_input, model);
+            break;
+        case InputKeyRight:
+            uart_text_input_handle_right(uart_text_input, model);
+            break;
+        case InputKeyOk:
+            uart_text_input_handle_ok(uart_text_input, model, true);
+            break;
+        case InputKeyBack:
+            uart_text_input_backspace_cb(model);
+            break;
+        default:
+            consumed = false;
+            break;
+        }
+    } else if(event->type == InputTypeRepeat) {
+        consumed = true;
+        switch(event->key) {
+        case InputKeyUp:
+            uart_text_input_handle_up(uart_text_input, model);
+            break;
+        case InputKeyDown:
+            uart_text_input_handle_down(uart_text_input, model);
+            break;
+        case InputKeyLeft:
+            uart_text_input_handle_left(uart_text_input, model);
+            break;
+        case InputKeyRight:
+            uart_text_input_handle_right(uart_text_input, model);
+            break;
+        case InputKeyBack:
+            uart_text_input_backspace_cb(model);
+            break;
+        default:
+            consumed = false;
+            break;
+        }
+    }
+
+    // Commit model
+    view_commit_model(uart_text_input->view, consumed);
+
+    return consumed;
+}
+
+void uart_text_input_timer_callback(void* context) {
+    furi_assert(context);
+    UART_TextInput* uart_text_input = context;
+
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { model->valadator_message_visible = false; },
+        true);
+}
+
+UART_TextInput* uart_text_input_alloc() {
+    UART_TextInput* uart_text_input = malloc(sizeof(UART_TextInput));
+    uart_text_input->view = view_alloc();
+    view_set_context(uart_text_input->view, uart_text_input);
+    view_allocate_model(uart_text_input->view, ViewModelTypeLocking, sizeof(UART_TextInputModel));
+    view_set_draw_callback(uart_text_input->view, uart_text_input_view_draw_callback);
+    view_set_input_callback(uart_text_input->view, uart_text_input_view_input_callback);
+
+    uart_text_input->timer =
+        furi_timer_alloc(uart_text_input_timer_callback, FuriTimerTypeOnce, uart_text_input);
+
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { model->validator_text = furi_string_alloc(); },
+        false);
+
+    uart_text_input_reset(uart_text_input);
+
+    return uart_text_input;
+}
+
+void uart_text_input_free(UART_TextInput* uart_text_input) {
+    furi_assert(uart_text_input);
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { furi_string_free(model->validator_text); },
+        false);
+
+    // Send stop command
+    furi_timer_stop(uart_text_input->timer);
+    // Release allocated memory
+    furi_timer_free(uart_text_input->timer);
+
+    view_free(uart_text_input->view);
+
+    free(uart_text_input);
+}
+
+void uart_text_input_reset(UART_TextInput* uart_text_input) {
+    furi_assert(uart_text_input);
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        {
+            model->text_buffer_size = 0;
+            model->header = "";
+            model->selected_row = 0;
+            model->selected_column = 0;
+            model->clear_default_text = false;
+            model->text_buffer = NULL;
+            model->text_buffer_size = 0;
+            model->callback = NULL;
+            model->callback_context = NULL;
+            model->validator_callback = NULL;
+            model->validator_callback_context = NULL;
+            furi_string_reset(model->validator_text);
+            model->valadator_message_visible = false;
+        },
+        true);
+}
+
+View* uart_text_input_get_view(UART_TextInput* uart_text_input) {
+    furi_assert(uart_text_input);
+    return uart_text_input->view;
+}
+
+void uart_text_input_set_result_callback(
+    UART_TextInput* uart_text_input,
+    UART_TextInputCallback callback,
+    void* callback_context,
+    char* text_buffer,
+    size_t text_buffer_size,
+    bool clear_default_text) {
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        {
+            model->callback = callback;
+            model->callback_context = callback_context;
+            model->text_buffer = text_buffer;
+            model->text_buffer_size = text_buffer_size;
+            model->clear_default_text = clear_default_text;
+            if(text_buffer && text_buffer[0] != '\0') {
+                // Set focus on Save
+                model->selected_row = 2;
+                model->selected_column = 8;
+            }
+        },
+        true);
+}
+
+void uart_text_input_set_validator(
+    UART_TextInput* uart_text_input,
+    UART_TextInputValidatorCallback callback,
+    void* callback_context) {
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        {
+            model->validator_callback = callback;
+            model->validator_callback_context = callback_context;
+        },
+        true);
+}
+
+UART_TextInputValidatorCallback
+    uart_text_input_get_validator_callback(UART_TextInput* uart_text_input) {
+    UART_TextInputValidatorCallback validator_callback = NULL;
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { validator_callback = model->validator_callback; },
+        false);
+    return validator_callback;
+}
+
+void* uart_text_input_get_validator_callback_context(UART_TextInput* uart_text_input) {
+    void* validator_callback_context = NULL;
+    with_view_model(
+        uart_text_input->view,
+        UART_TextInputModel * model,
+        { validator_callback_context = model->validator_callback_context; },
+        false);
+    return validator_callback_context;
+}
+
+void uart_text_input_set_header_text(UART_TextInput* uart_text_input, const char* text) {
+    with_view_model(
+        uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true);
+}

+ 81 - 0
uart_terminal/uart_text_input.h

@@ -0,0 +1,81 @@
+#pragma once
+
+#include <gui/view.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Text input anonymous structure */
+typedef struct UART_TextInput UART_TextInput;
+typedef void (*UART_TextInputCallback)(void* context);
+typedef bool (*UART_TextInputValidatorCallback)(const char* text, FuriString* error, void* context);
+
+/** Allocate and initialize text input 
+ * 
+ * This text input is used to enter string
+ *
+ * @return     UART_TextInput instance
+ */
+UART_TextInput* uart_text_input_alloc();
+
+/** Deinitialize and free text input
+ *
+ * @param      uart_text_input  UART_TextInput instance
+ */
+void uart_text_input_free(UART_TextInput* uart_text_input);
+
+/** Clean text input view Note: this function does not free memory
+ *
+ * @param      uart_text_input  Text input instance
+ */
+void uart_text_input_reset(UART_TextInput* uart_text_input);
+
+/** Get text input view
+ *
+ * @param      uart_text_input  UART_TextInput instance
+ *
+ * @return     View instance that can be used for embedding
+ */
+View* uart_text_input_get_view(UART_TextInput* uart_text_input);
+
+/** Set text input result callback
+ *
+ * @param      uart_text_input          UART_TextInput instance
+ * @param      callback            callback fn
+ * @param      callback_context    callback context
+ * @param      text_buffer         pointer to YOUR text buffer, that we going
+ *                                 to modify
+ * @param      text_buffer_size    YOUR text buffer size in bytes. Max string
+ *                                 length will be text_buffer_size-1.
+ * @param      clear_default_text  clear text from text_buffer on first OK
+ *                                 event
+ */
+void uart_text_input_set_result_callback(
+    UART_TextInput* uart_text_input,
+    UART_TextInputCallback callback,
+    void* callback_context,
+    char* text_buffer,
+    size_t text_buffer_size,
+    bool clear_default_text);
+
+void uart_text_input_set_validator(
+    UART_TextInput* uart_text_input,
+    UART_TextInputValidatorCallback callback,
+    void* callback_context);
+
+UART_TextInputValidatorCallback
+    uart_text_input_get_validator_callback(UART_TextInput* uart_text_input);
+
+void* uart_text_input_get_validator_callback_context(UART_TextInput* uart_text_input);
+
+/** Set text input header text
+ *
+ * @param      uart_text_input  UART_TextInput instance
+ * @param      text        text to be shown
+ */
+void uart_text_input_set_header_text(UART_TextInput* uart_text_input, const char* text);
+
+#ifdef __cplusplus
+}
+#endif

+ 0 - 57
uart_terminal/uart_validators.c

@@ -1,57 +0,0 @@
-#include <furi.h>
-#include "uart_validators.h"
-#include <storage/storage.h>
-
-struct ValidatorIsFile {
-    char* app_path_folder;
-    const char* app_extension;
-    char* current_name;
-};
-
-bool validator_is_file_callback(const char* text, FuriString* error, void* context) {
-    furi_assert(context);
-    ValidatorIsFile* instance = context;
-
-    if(instance->current_name != NULL) {
-        if(strcmp(instance->current_name, text) == 0) {
-            return true;
-        }
-    }
-
-    bool ret = true;
-    FuriString* path = furi_string_alloc_printf(
-        "%s/%s%s", instance->app_path_folder, text, instance->app_extension);
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    if(storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK) {
-        ret = false;
-        furi_string_printf(error, "This name\nexists!\nChoose\nanother one.");
-    } else {
-        ret = true;
-    }
-    furi_string_free(path);
-    furi_record_close(RECORD_STORAGE);
-
-    return ret;
-}
-
-ValidatorIsFile* validator_is_file_alloc_init(
-    const char* app_path_folder,
-    const char* app_extension,
-    const char* current_name) {
-    ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile));
-
-    instance->app_path_folder = strdup(app_path_folder);
-    instance->app_extension = app_extension;
-    if(current_name != NULL) {
-        instance->current_name = strdup(current_name);
-    }
-
-    return instance;
-}
-
-void validator_is_file_free(ValidatorIsFile* instance) {
-    furi_assert(instance);
-    free(instance->app_path_folder);
-    free(instance->current_name);
-    free(instance);
-}

+ 0 - 21
uart_terminal/uart_validators.h

@@ -1,21 +0,0 @@
-#pragma once
-
-#include <core/common_defines.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-typedef struct ValidatorIsFile ValidatorIsFile;
-
-ValidatorIsFile* validator_is_file_alloc_init(
-    const char* app_path_folder,
-    const char* app_extension,
-    const char* current_name);
-
-void validator_is_file_free(ValidatorIsFile* instance);
-
-bool validator_is_file_callback(const char* text, FuriString* error, void* context);
-
-#ifdef __cplusplus
-}
-#endif