Преглед изворни кода

Implementation of some widgets based on real use cases and designs [FL-392][FL-809] (#315)

* gui test app
* aligned string draw functions
* add canvas_invert_color, canvas_draw_button_left, canvas_draw_button_right
* use new str and button fns in dialog
* real dialog mockup
* add new gui test app recipe
* submenu module init
* delete unused variable
* move buttons to element, add canvas_string_width fn, new center button element
* button icons
* submenu module
* use submenu module, switch views
* keyboard buttons img
* new font for keyboard
* text input (keyboard) module
* add text input to gui test app
* add gui tesst app to release build, fix flags
* handle transition from start and end position, fix input switch
* add long text support to text input
* canvas_string_width and the underlying u8g2_GetStrWidth now return uint16_t
* remove deprecated libs and apps
* canvas_font_max_height fn
* new element, aligned multiline text
* use multiline text instead of plain string
* fix second keyboard row, rename uppercase fn
* qwerty-like keyboard layout
* new icons for iButton app
* better dialog text position and events handling
* remove confusing comment
* new extended dialog module
* extended dialog module usage
* update docs
* new gui module, popup with timeout
* popup usage
* canvas, remove outdated canvas_font_max_height, use canvas_current_font_height
* use furi check
* use new view_enter and view_exit callback for timers
* add DrZlo to gui tester codeowner

Co-authored-by: aanper <mail@s3f.ru>
DrZlo13 пре 5 година
родитељ
комит
4341da90dc
35 измењених фајлова са 1767 додато и 155 уклоњено
  1. 1 0
      .github/CODEOWNERS
  2. 10 5
      applications/applications.c
  3. 12 16
      applications/applications.mk
  4. 157 0
      applications/gui-test/gui-test.c
  5. 56 1
      applications/gui/canvas.c
  6. 37 1
      applications/gui/canvas.h
  7. 136 5
      applications/gui/elements.c
  8. 32 0
      applications/gui/elements.h
  9. 18 8
      applications/gui/modules/dialog.c
  10. 3 3
      applications/gui/modules/dialog.h
  11. 232 0
      applications/gui/modules/dialog_ex.c
  12. 105 0
      applications/gui/modules/dialog_ex.h
  13. 227 0
      applications/gui/modules/popup.c
  14. 92 0
      applications/gui/modules/popup.h
  15. 194 0
      applications/gui/modules/submenu.c
  16. 36 0
      applications/gui/modules/submenu.h
  17. 370 0
      applications/gui/modules/text_input.c
  18. 42 0
      applications/gui/modules/text_input.h
  19. BIN
      assets/icons/Common/ButtonCenter_7x7.png
  20. BIN
      assets/icons/Common/ButtonLeft_4x7.png
  21. BIN
      assets/icons/Common/ButtonRight_4x7.png
  22. BIN
      assets/icons/Keyboard/KeyBackspaceSelected_16x9.png
  23. BIN
      assets/icons/Keyboard/KeyBackspace_16x9.png
  24. BIN
      assets/icons/Keyboard/KeySaveSelected_24x11.png
  25. BIN
      assets/icons/Keyboard/KeySave_24x11.png
  26. BIN
      assets/icons/iButton/DolphinExcited_64x63.png
  27. BIN
      assets/icons/iButton/DolphinMafia_115x62.png
  28. BIN
      assets/icons/iButton/DolphinWait_61x59.png
  29. BIN
      assets/icons/iButton/iButtonDolphinSuccess_109x60.png
  30. BIN
      assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png
  31. BIN
      assets/icons/iButton/iButtonKey_49x44.png
  32. 1 1
      lib/u8g2/u8g2.h
  33. 6 5
      lib/u8g2/u8g2_font.c
  34. 0 106
      lib/u8g2_vendor/u8g2_vendor.c
  35. 0 4
      lib/u8g2_vendor/u8g2_vendor.h

+ 1 - 0
.github/CODEOWNERS

@@ -110,6 +110,7 @@ firmware/targets/f4/api-hal/api-hal-power.c @skotopes
 applications/music-player/** @DrZlo13
 applications/floopper-bloopper/** @glitchcore
 applications/gpio-tester/** @glitchcore
+applications/gui-test/** @DrZlo13
 
 lib/app-template/** @DrZlo13
 lib/qrcode/** @DrZlo13

+ 10 - 5
applications/applications.c

@@ -7,7 +7,6 @@ void flipper_test_app(void* p);
 void application_blink(void* p);
 void application_uart_write(void* p);
 void application_input_dump(void* p);
-void display_u8g2(void* p);
 void u8g2_example(void* p);
 void input_task(void* p);
 void menu_task(void* p);
@@ -34,11 +33,9 @@ void sdnfc(void* p);
 void floopper_bloopper(void* p);
 void sd_filesystem(void* p);
 
-const FuriApplication FLIPPER_SERVICES[] = {
-#ifdef APP_DISPLAY
-    {.app = display_u8g2, .name = "display_u8g2", .stack_size = 1024, .icon = A_Plugins_14},
-#endif
+void gui_test(void* p);
 
+const FuriApplication FLIPPER_SERVICES[] = {
 #ifdef APP_CLI
     {.app = cli_task, .name = "cli_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
@@ -152,6 +149,10 @@ const FuriApplication FLIPPER_SERVICES[] = {
 #ifdef APP_SDNFC
     {.app = sdnfc, .name = "sdnfc", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
+
+#ifdef APP_GUI_TEST
+    {.app = gui_test, .name = "gui_test", .icon = A_Plugins_14},
+#endif
 };
 
 size_t FLIPPER_SERVICES_size() {
@@ -224,6 +225,10 @@ const FuriApplication FLIPPER_PLUGINS[] = {
 #ifdef BUILD_SDNFC
     {.app = sdnfc, .name = "sdnfc", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
+
+#ifdef BUILD_GUI_TEST
+    {.app = gui_test, .name = "gui_test", .icon = A_Plugins_14},
+#endif
 };
 
 size_t FLIPPER_PLUGINS_size() {

+ 12 - 16
applications/applications.mk

@@ -29,6 +29,7 @@ BUILD_GPIO_DEMO = 1
 BUILD_MUSIC_PLAYER = 1
 BUILD_FLOOPPER_BLOOPPER = 1
 BUILD_IBUTTON = 1
+BUILD_GUI_TEST = 1
 endif
 
 APP_NFC ?= 0
@@ -144,15 +145,6 @@ ifeq ($(BUILD_EXAMPLE_QRCODE), 1)
 CFLAGS		+= -DBUILD_EXAMPLE_QRCODE
 C_SOURCES	+= $(APP_DIR)/examples/u8g2_qrcode.c
 C_SOURCES	+= $(LIB_DIR)/qrcode/qrcode.c
-APP_DISPLAY = 1
-endif
-
-# deprecated
-APP_EXAMPLE_DISPLAY ?= 0
-ifeq ($(APP_EXAMPLE_DISPLAY), 1)
-CFLAGS		+= -DAPP_EXAMPLE_DISPLAY
-C_SOURCES	+= $(APP_DIR)/examples/u8g2_example.c
-APP_DISPLAY = 1
 endif
 
 APP_EXAMPLE_FATFS ?= 0
@@ -165,7 +157,6 @@ ifeq ($(BUILD_EXAMPLE_FATFS), 1)
 CFLAGS		+= -DBUILD_EXAMPLE_FATFS
 C_SOURCES	+= $(APP_DIR)/examples/fatfs_list.c
 APP_INPUT = 1
-APP_DISPLAY = 1
 endif
 
 APP_CC1101 ?= 0
@@ -289,6 +280,17 @@ CFLAGS		+= -DBUILD_IBUTTON
 CPP_SOURCES	+= $(wildcard $(APP_DIR)/ibutton/*.cpp)
 endif
 
+APP_GUI_TEST ?= 0
+ifeq ($(APP_GUI_TEST), 1)
+CFLAGS		+= -DAPP_GUI_TEST
+BUILD_GUI_TEST = 1
+endif
+BUILD_GUI_TEST ?= 0
+ifeq ($(BUILD_GUI_TEST), 1)
+CFLAGS		+= -DBUILD_GUI_TEST
+C_SOURCES	+= $(wildcard $(APP_DIR)/gui-test/*.c)
+endif
+
 APP_SDNFC ?= 0
 ifeq ($(APP_SDNFC), 1)
 CFLAGS		+= -DAPP_SDNFC
@@ -315,12 +317,6 @@ CFLAGS		+= -DAPP_SD_FILESYSTEM
 C_SOURCES	+= $(wildcard $(APP_DIR)/sd-filesystem/*.c)
 endif
 
-# deprecated
-ifeq ($(APP_DISPLAY), 1)
-CFLAGS		+= -DAPP_DISPLAY
-C_SOURCES	+= $(APP_DIR)/display-u8g2/display-u8g2.c
-endif
-
 APP_INPUT	?= 0
 ifeq ($(APP_INPUT), 1)
 CFLAGS		+= -DAPP_INPUT

+ 157 - 0
applications/gui-test/gui-test.c

@@ -0,0 +1,157 @@
+#include <furi.h>
+#include <gui/gui.h>
+#include <gui/view_port.h>
+#include <gui/view.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/dialog.h>
+#include <gui/modules/dialog_ex.h>
+#include <gui/modules/submenu.h>
+#include <gui/modules/text_input.h>
+#include <gui/modules/popup.h>
+
+typedef enum {
+    GuiTesterViewTextInput = 0,
+    GuiTesterViewSubmenu,
+    GuiTesterViewDialog,
+    GuiTesterViewDialogEx,
+    GuiTesterViewPopup,
+    GuiTesterViewLast
+} GuiTesterView;
+
+typedef struct {
+    ViewDispatcher* view_dispatcher;
+    Dialog* dialog;
+    DialogEx* dialog_ex;
+    Submenu* submenu;
+    TextInput* text_input;
+    Popup* popup;
+    GuiTesterView view_index;
+} GuiTester;
+
+GuiTester* gui_test_alloc(void) {
+    GuiTester* gui_tester = furi_alloc(sizeof(GuiTester));
+    gui_tester->view_dispatcher = view_dispatcher_alloc();
+    gui_tester->view_index = GuiTesterViewDialogEx;
+
+    gui_tester->dialog = dialog_alloc();
+    view_dispatcher_add_view(
+        gui_tester->view_dispatcher, GuiTesterViewDialog, dialog_get_view(gui_tester->dialog));
+
+    gui_tester->dialog_ex = dialog_ex_alloc();
+    view_dispatcher_add_view(
+        gui_tester->view_dispatcher,
+        GuiTesterViewDialogEx,
+        dialog_ex_get_view(gui_tester->dialog_ex));
+
+    gui_tester->submenu = submenu_alloc();
+    view_dispatcher_add_view(
+        gui_tester->view_dispatcher, GuiTesterViewSubmenu, submenu_get_view(gui_tester->submenu));
+
+    gui_tester->text_input = text_input_alloc();
+    view_dispatcher_add_view(
+        gui_tester->view_dispatcher,
+        GuiTesterViewTextInput,
+        text_input_get_view(gui_tester->text_input));
+
+    gui_tester->popup = popup_alloc();
+    view_dispatcher_add_view(
+        gui_tester->view_dispatcher, GuiTesterViewPopup, popup_get_view(gui_tester->popup));
+
+    return gui_tester;
+}
+
+void next_view(void* context) {
+    furi_assert(context);
+    GuiTester* gui_tester = context;
+
+    gui_tester->view_index++;
+    if(gui_tester->view_index >= GuiTesterViewLast) {
+        gui_tester->view_index = 0;
+    }
+
+    view_dispatcher_switch_to_view(gui_tester->view_dispatcher, gui_tester->view_index);
+}
+
+void popup_callback(void* context) {
+    next_view(context);
+}
+
+void submenu_callback(void* context) {
+    next_view(context);
+}
+
+void dialog_callback(DialogResult result, void* context) {
+    next_view(context);
+}
+
+void dialog_ex_callback(DialogExResult result, void* context) {
+    next_view(context);
+}
+
+void text_input_callback(void* context, char* text) {
+    next_view(context);
+}
+
+void gui_test(void* param) {
+    (void)param;
+    GuiTester* gui_tester = gui_test_alloc();
+
+    Gui* gui = furi_record_open("gui");
+    view_dispatcher_attach_to_gui(gui_tester->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
+
+    // Submenu
+    submenu_add_item(gui_tester->submenu, "Read", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Saved", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Emulate", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Enter manually", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Blah blah", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Set time", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Gender-bender", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Hack American Elections", submenu_callback, gui_tester);
+    submenu_add_item(gui_tester->submenu, "Hack the White House", submenu_callback, gui_tester);
+
+    // Dialog
+    dialog_set_result_callback(gui_tester->dialog, dialog_callback);
+    dialog_set_context(gui_tester->dialog, gui_tester);
+    dialog_set_header_text(gui_tester->dialog, "Delete Abc123?");
+    dialog_set_text(gui_tester->dialog, "ID: F0 00 01 02 03 04\nAre you shure?");
+    dialog_set_left_button_text(gui_tester->dialog, "< Yes");
+    dialog_set_right_button_text(gui_tester->dialog, "No >");
+
+    // Dialog extended
+    dialog_ex_set_result_callback(gui_tester->dialog_ex, dialog_ex_callback);
+    dialog_ex_set_context(gui_tester->dialog_ex, gui_tester);
+    dialog_ex_set_header(gui_tester->dialog_ex, "Dallas", 95, 12, AlignCenter, AlignCenter);
+    dialog_ex_set_text(
+        gui_tester->dialog_ex, "F6 E5 D4\nC3 B2 A1", 95, 32, AlignCenter, AlignCenter);
+    dialog_ex_set_icon(gui_tester->dialog_ex, 0, 1, I_DolphinExcited_64x63);
+    dialog_ex_set_left_button_text(gui_tester->dialog_ex, "< More");
+    dialog_ex_set_right_button_text(gui_tester->dialog_ex, "Save >");
+
+    // Popup
+    popup_set_callback(gui_tester->popup, popup_callback);
+    popup_set_context(gui_tester->popup, gui_tester);
+    popup_set_icon(gui_tester->popup, 0, 2, I_DolphinMafia_115x62);
+    popup_set_text(gui_tester->popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
+    popup_set_timeout(gui_tester->popup, 5000);
+    popup_enable_timeout(gui_tester->popup);
+
+    // Text input
+    const uint8_t text_input_text_len = 64;
+    char* text_input_text = calloc(text_input_text_len + 1, 1);
+    memcpy(text_input_text, "New_ke", strlen("New_ke"));
+
+    text_input_set_result_callback(
+        gui_tester->text_input,
+        text_input_callback,
+        gui_tester,
+        text_input_text,
+        text_input_text_len);
+    text_input_set_header_text(gui_tester->text_input, "Name the key");
+
+    view_dispatcher_switch_to_view(gui_tester->view_dispatcher, gui_tester->view_index);
+
+    while(1) {
+        osDelay(1000);
+    }
+}

+ 56 - 1
applications/gui/canvas.c

@@ -77,6 +77,10 @@ void canvas_set_color(Canvas* canvas, Color color) {
     u8g2_SetDrawColor(&canvas->fb, color);
 }
 
+void canvas_invert_color(Canvas* canvas) {
+    canvas->fb.draw_color = !canvas->fb.draw_color;
+}
+
 void canvas_set_font(Canvas* canvas, Font font) {
     furi_assert(canvas);
     u8g2_SetFontMode(&canvas->fb, 1);
@@ -86,6 +90,8 @@ void canvas_set_font(Canvas* canvas, Font font) {
         u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr);
     } else if(font == FontGlyph) {
         u8g2_SetFont(&canvas->fb, u8g2_font_unifont_t_symbols);
+    } else if(font == FontKeyboard) {
+        u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mf);
     } else {
         furi_check(0);
     }
@@ -99,6 +105,55 @@ void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) {
     u8g2_DrawStr(&canvas->fb, x, y, str);
 }
 
+void canvas_draw_str_aligned(
+    Canvas* canvas,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    const char* str) {
+    furi_assert(canvas);
+    if(!str) return;
+    x += canvas->offset_x;
+    y += canvas->offset_y;
+
+    switch(horizontal) {
+    case AlignLeft:
+        break;
+    case AlignRight:
+        x -= u8g2_GetStrWidth(&canvas->fb, str);
+        break;
+    case AlignCenter:
+        x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2);
+        break;
+    default:
+        furi_check(0);
+        break;
+    }
+
+    switch(vertical) {
+    case AlignTop:
+        y += u8g2_GetAscent(&canvas->fb);
+        break;
+    case AlignBottom:
+        break;
+    case AlignCenter:
+        y += (u8g2_GetAscent(&canvas->fb) / 2);
+        break;
+    default:
+        furi_check(0);
+        break;
+    }
+
+    u8g2_DrawStr(&canvas->fb, x, y, str);
+}
+
+uint16_t canvas_string_width(Canvas* canvas, const char* str) {
+    furi_assert(canvas);
+    if(!str) return 0;
+    return u8g2_GetStrWidth(&canvas->fb, str);
+}
+
 void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, Icon* icon) {
     furi_assert(canvas);
     if(!icon) return;
@@ -164,4 +219,4 @@ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) {
     x += canvas->offset_x;
     y += canvas->offset_y;
     u8g2_DrawGlyph(&canvas->fb, x, y, ch);
-}
+}

+ 37 - 1
applications/gui/canvas.h

@@ -13,7 +13,20 @@ typedef enum {
     ColorBlack = 0x01,
 } Color;
 
-typedef enum { FontPrimary = 0x00, FontSecondary = 0x01, FontGlyph = 0x02 } Font;
+typedef enum {
+    FontPrimary = 0x00,
+    FontSecondary = 0x01,
+    FontGlyph = 0x02,
+    FontKeyboard = 0x03
+} Font;
+
+typedef enum {
+    AlignLeft,
+    AlignRight,
+    AlignTop,
+    AlignBottom,
+    AlignCenter,
+} Align;
 
 typedef struct Canvas Canvas;
 
@@ -45,6 +58,11 @@ void canvas_clear(Canvas* canvas);
  */
 void canvas_set_color(Canvas* canvas, Color color);
 
+/*
+ * Invert drawing color
+ */
+void canvas_invert_color(Canvas* canvas);
+
 /*
  * Set drawing font
  */
@@ -55,6 +73,24 @@ void canvas_set_font(Canvas* canvas, Font font);
  */
 void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str);
 
+/*
+ * Draw aligned string defined by x, y.
+ * Align calculated from position of baseline, string width and ascent (height of the glyphs above the baseline)
+ */
+void canvas_draw_str_aligned(
+    Canvas* canvas,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    const char* str);
+
+/*
+ * Get string width
+ * @return width in pixels.
+ */
+uint16_t canvas_string_width(Canvas* canvas, const char* str);
+
 /*
  * Draw stateful icon at position defined by x,y.
  */

+ 136 - 5
applications/gui/elements.c

@@ -1,10 +1,10 @@
 #include "elements.h"
-#include "canvas_i.h"
-
+#include <assets_icons.h>
+#include <gui/icon_i.h>
+#include <m-string.h>
 #include <furi.h>
-
+#include "canvas_i.h"
 #include <string.h>
-#include <m-string.h>
 
 void elements_scrollbar(Canvas* canvas, uint8_t pos, uint8_t total) {
     furi_assert(canvas);
@@ -40,6 +40,137 @@ void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t
     canvas_draw_dot(canvas, x + 1, y + 1);
 }
 
+void elements_button_left(Canvas* canvas, const char* str) {
+    const uint8_t button_height = 13;
+    const uint8_t vertical_offset = 3;
+    const uint8_t horizontal_offset = 3;
+    const uint8_t string_width = canvas_string_width(canvas, str);
+    const IconData* icon = assets_icons_get_data(I_ButtonLeft_4x7);
+    const uint8_t icon_offset = 6;
+    const uint8_t icon_width_with_offset = icon->width + icon_offset;
+    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
+
+    const uint8_t x = 0;
+    const uint8_t y = canvas_height(canvas);
+
+    canvas_draw_box(canvas, x, y - button_height, button_width, button_height);
+    canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0);
+    canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1);
+    canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2);
+
+    canvas_invert_color(canvas);
+    canvas_draw_icon_name(
+        canvas, x + horizontal_offset, y - button_height + vertical_offset, I_ButtonLeft_4x7);
+    canvas_draw_str(
+        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
+    canvas_invert_color(canvas);
+}
+
+void elements_button_right(Canvas* canvas, const char* str) {
+    const uint8_t button_height = 13;
+    const uint8_t vertical_offset = 3;
+    const uint8_t horizontal_offset = 3;
+    const uint8_t string_width = canvas_string_width(canvas, str);
+    const IconData* icon = assets_icons_get_data(I_ButtonRight_4x7);
+    const uint8_t icon_offset = 6;
+    const uint8_t icon_width_with_offset = icon->width + icon_offset;
+    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
+
+    const uint8_t x = canvas_width(canvas);
+    const uint8_t y = canvas_height(canvas);
+
+    canvas_draw_box(canvas, x - button_width, y - button_height, button_width, button_height);
+    canvas_draw_line(canvas, x - button_width - 1, y, x - button_width - 1, y - button_height + 0);
+    canvas_draw_line(canvas, x - button_width - 2, y, x - button_width - 2, y - button_height + 1);
+    canvas_draw_line(canvas, x - button_width - 3, y, x - button_width - 3, y - button_height + 2);
+
+    canvas_invert_color(canvas);
+    canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);
+    canvas_draw_icon_name(
+        canvas,
+        x - horizontal_offset - icon->width,
+        y - button_height + vertical_offset,
+        I_ButtonRight_4x7);
+    canvas_invert_color(canvas);
+}
+
+void elements_button_center(Canvas* canvas, const char* str) {
+    const uint8_t button_height = 13;
+    const uint8_t vertical_offset = 3;
+    const uint8_t horizontal_offset = 3;
+    const uint8_t string_width = canvas_string_width(canvas, str);
+    const IconData* icon = assets_icons_get_data(I_ButtonCenter_7x7);
+    const uint8_t icon_offset = 6;
+    const uint8_t icon_width_with_offset = icon->width + icon_offset;
+    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
+
+    const uint8_t x = (canvas_width(canvas) - button_width) / 2;
+    const uint8_t y = canvas_height(canvas);
+
+    canvas_draw_box(canvas, x, y - button_height, button_width, button_height);
+
+    canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0);
+    canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1);
+    canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2);
+
+    canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0);
+    canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1);
+    canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2);
+
+    canvas_invert_color(canvas);
+    canvas_draw_icon_name(
+        canvas, x + horizontal_offset, y - button_height + vertical_offset, I_ButtonCenter_7x7);
+    canvas_draw_str(
+        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
+    canvas_invert_color(canvas);
+}
+
+void elements_multiline_text_aligned(
+    Canvas* canvas,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    const char* text) {
+    furi_assert(canvas);
+    furi_assert(text);
+
+    uint8_t font_height = canvas_current_font_height(canvas);
+    string_t str;
+    string_init(str);
+    const char* start = text;
+    char* end;
+
+    // get lines count
+    uint8_t i, lines_count;
+    for(i = 0, lines_count = 0; text[i]; i++) lines_count += (text[i] == '\n');
+
+    switch(vertical) {
+    case AlignBottom:
+        y -= font_height * lines_count;
+        break;
+    case AlignCenter:
+        y -= (font_height * lines_count) / 2;
+        break;
+    case AlignTop:
+    default:
+        break;
+    }
+
+    do {
+        end = strchr(start, '\n');
+        if(end) {
+            string_set_strn(str, start, end - start);
+        } else {
+            string_set_str(str, start);
+        }
+        canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(str));
+        start = end + 1;
+        y += font_height;
+    } while(end);
+    string_clear(str);
+}
+
 void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, char* text) {
     furi_assert(canvas);
     furi_assert(text);
@@ -61,4 +192,4 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, char* text) {
         y += font_height;
     } while(end);
     string_clear(str);
-}
+}

+ 32 - 0
applications/gui/elements.h

@@ -22,6 +22,38 @@ void elements_scrollbar(Canvas* canvas, uint8_t pos, uint8_t total);
  */
 void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
 
+/*
+ * Draw button in left corner
+ * @param str - button text
+ */
+void elements_button_left(Canvas* canvas, const char* str);
+
+/*
+ * Draw button in right corner
+ * @param str - button text
+ */
+void elements_button_right(Canvas* canvas, const char* str);
+
+/*
+ * Draw button in center
+ * @param str - button text
+ */
+void elements_button_center(Canvas* canvas, const char* str);
+
+/*
+ * Draw aligned multiline text
+ * @param x, y - coordinates based on align param
+ * @param horizontal, vertical - aligment of multiline text
+ * @param text - string (possible multiline)
+ */
+void elements_multiline_text_aligned(
+    Canvas* canvas,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    const char* text);
+
 /*
  * Draw multiline text
  * @param x, y - top left corner coordinates

+ 18 - 8
applications/gui/modules/dialog.c

@@ -1,4 +1,5 @@
 #include "dialog.h"
+#include <gui/elements.h>
 #include <furi.h>
 
 struct Dialog {
@@ -16,34 +17,43 @@ typedef struct {
 
 static void dialog_view_draw_callback(Canvas* canvas, void* _model) {
     DialogModel* model = _model;
+    uint8_t canvas_center = canvas_width(canvas) / 2;
+
     // Prepare canvas
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
+
     // Draw header
     canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 2, 10, model->header_text);
+    canvas_draw_str_aligned(
+        canvas, canvas_center, 17, AlignCenter, AlignBottom, model->header_text);
+
     // Draw text
     canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 5, 22, model->text);
+    elements_multiline_text_aligned(
+        canvas, canvas_center, 32, AlignCenter, AlignCenter, model->text);
+
     // Draw buttons
-    uint8_t bottom_base_line = canvas_height(canvas) - 2;
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 5, bottom_base_line, model->left_text);
-    canvas_draw_str(canvas, 69, bottom_base_line, model->right_text);
+    elements_button_left(canvas, model->left_text);
+    elements_button_right(canvas, model->right_text);
 }
 
 static bool dialog_view_input_callback(InputEvent* event, void* context) {
     Dialog* dialog = context;
+    bool consumed = false;
+
     // Process key presses only
     if(event->state && dialog->callback) {
         if(event->input == InputLeft) {
             dialog->callback(DialogResultLeft, dialog->context);
+            consumed = true;
         } else if(event->input == InputRight) {
             dialog->callback(DialogResultRight, dialog->context);
+            consumed = true;
         }
     }
-    // All input events consumed
-    return true;
+
+    return consumed;
 }
 
 Dialog* dialog_alloc() {

+ 3 - 3
applications/gui/modules/dialog.h

@@ -32,13 +32,13 @@ void dialog_free(Dialog* dialog);
  */
 View* dialog_get_view(Dialog* dialog);
 
-/* Set dialog header text
+/* Set dialog result callback
  * @param dialog - Dialog instance
- * @param text - text to be shown
+ * @param callback - result callback function
  */
 void dialog_set_result_callback(Dialog* dialog, DialogResultCallback callback);
 
-/* Set dialog header text
+/* Set dialog context
  * @param dialog - Dialog instance
  * @param context - context pointer, will be passed to result callback
  */

+ 232 - 0
applications/gui/modules/dialog_ex.c

@@ -0,0 +1,232 @@
+#include "dialog_ex.h"
+#include <gui/elements.h>
+#include <furi.h>
+
+struct DialogEx {
+    View* view;
+    void* context;
+    DialogExResultCallback callback;
+};
+
+typedef struct {
+    const char* text;
+    uint8_t x;
+    uint8_t y;
+    Align horizontal;
+    Align vertical;
+} TextElement;
+
+typedef struct {
+    int8_t x;
+    int8_t y;
+    IconName name;
+} IconElement;
+
+typedef struct {
+    TextElement header;
+    TextElement text;
+    IconElement icon;
+
+    const char* left_text;
+    const char* center_text;
+    const char* right_text;
+} DialogExModel;
+
+static void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) {
+    DialogExModel* model = _model;
+
+    // Prepare canvas
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    // TODO other criteria for the draw
+    if(model->icon.x >= 0 && model->icon.y >= 0) {
+        canvas_draw_icon_name(canvas, model->icon.x, model->icon.y, model->icon.name);
+    }
+
+    // Draw header
+    if(model->header.text != NULL) {
+        canvas_set_font(canvas, FontPrimary);
+        elements_multiline_text_aligned(
+            canvas,
+            model->header.x,
+            model->header.y,
+            model->header.horizontal,
+            model->header.vertical,
+            model->header.text);
+    }
+
+    // Draw text
+    if(model->text.text != NULL) {
+        canvas_set_font(canvas, FontSecondary);
+        elements_multiline_text_aligned(
+            canvas,
+            model->text.x,
+            model->text.y,
+            model->text.horizontal,
+            model->text.vertical,
+            model->text.text);
+    }
+
+    // Draw buttons
+    if(model->left_text != NULL) {
+        elements_button_left(canvas, model->left_text);
+    }
+
+    if(model->center_text != NULL) {
+        elements_button_center(canvas, model->center_text);
+    }
+
+    if(model->right_text != NULL) {
+        elements_button_right(canvas, model->right_text);
+    }
+}
+
+static bool dialog_ex_view_input_callback(InputEvent* event, void* context) {
+    DialogEx* dialog_ex = context;
+    bool consumed = false;
+    const char* left_text = NULL;
+    const char* center_text = NULL;
+    const char* right_text = NULL;
+
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) {
+            left_text = model->left_text;
+            center_text = model->center_text;
+            right_text = model->right_text;
+        });
+
+    // Process key presses only
+    if(event->state && dialog_ex->callback) {
+        if(event->input == InputLeft && left_text != NULL) {
+            dialog_ex->callback(DialogExResultLeft, dialog_ex->context);
+            consumed = true;
+        } else if(event->input == InputOk && center_text != NULL) {
+            dialog_ex->callback(DialogExResultCenter, dialog_ex->context);
+            consumed = true;
+        } else if(event->input == InputRight && right_text != NULL) {
+            dialog_ex->callback(DialogExResultRight, dialog_ex->context);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+DialogEx* dialog_ex_alloc() {
+    DialogEx* dialog_ex = furi_alloc(sizeof(DialogEx));
+    dialog_ex->view = view_alloc();
+    view_set_context(dialog_ex->view, dialog_ex);
+    view_allocate_model(dialog_ex->view, ViewModelTypeLockFree, sizeof(DialogExModel));
+    view_set_draw_callback(dialog_ex->view, dialog_ex_view_draw_callback);
+    view_set_input_callback(dialog_ex->view, dialog_ex_view_input_callback);
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) {
+            model->header.text = NULL;
+            model->header.x = 0;
+            model->header.y = 0;
+            model->header.horizontal = AlignLeft;
+            model->header.vertical = AlignBottom;
+
+            model->text.text = NULL;
+            model->text.x = 0;
+            model->text.y = 0;
+            model->text.horizontal = AlignLeft;
+            model->text.vertical = AlignBottom;
+
+            // TODO other criteria for the draw
+            model->icon.x = -1;
+            model->icon.y = -1;
+            model->icon.name = I_ButtonCenter_7x7;
+
+            model->left_text = NULL;
+            model->center_text = NULL;
+            model->right_text = NULL;
+        });
+    return dialog_ex;
+}
+
+void dialog_ex_free(DialogEx* dialog_ex) {
+    furi_assert(dialog_ex);
+    view_free(dialog_ex->view);
+    free(dialog_ex);
+}
+
+View* dialog_ex_get_view(DialogEx* dialog_ex) {
+    furi_assert(dialog_ex);
+    return dialog_ex->view;
+}
+
+void dialog_ex_set_result_callback(DialogEx* dialog_ex, DialogExResultCallback callback) {
+    furi_assert(dialog_ex);
+    dialog_ex->callback = callback;
+}
+
+void dialog_ex_set_context(DialogEx* dialog_ex, void* context) {
+    furi_assert(dialog_ex);
+    dialog_ex->context = context;
+}
+
+void dialog_ex_set_header(
+    DialogEx* dialog_ex,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical) {
+    furi_assert(dialog_ex);
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) {
+            model->header.text = text;
+            model->header.x = x;
+            model->header.y = y;
+            model->header.horizontal = horizontal;
+            model->header.vertical = vertical;
+        });
+}
+
+void dialog_ex_set_text(
+    DialogEx* dialog_ex,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical) {
+    furi_assert(dialog_ex);
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) {
+            model->text.text = text;
+            model->text.x = x;
+            model->text.y = y;
+            model->text.horizontal = horizontal;
+            model->text.vertical = vertical;
+        });
+}
+
+void dialog_ex_set_icon(DialogEx* dialog_ex, int8_t x, int8_t y, IconName name) {
+    furi_assert(dialog_ex);
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) {
+            model->icon.x = x;
+            model->icon.y = y;
+            model->icon.name = name;
+        });
+}
+
+void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) {
+    furi_assert(dialog_ex);
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) { model->left_text = text; });
+}
+
+void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) {
+    furi_assert(dialog_ex);
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) { model->center_text = text; });
+}
+
+void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) {
+    furi_assert(dialog_ex);
+    with_view_model(
+        dialog_ex->view, (DialogExModel * model) { model->right_text = text; });
+}

+ 105 - 0
applications/gui/modules/dialog_ex.h

@@ -0,0 +1,105 @@
+#pragma once
+
+#include <gui/view.h>
+
+/* Dialog anonymous structure */
+typedef struct DialogEx DialogEx;
+
+/* DialogEx result */
+typedef enum {
+    DialogExResultLeft,
+    DialogExResultCenter,
+    DialogExResultRight,
+} DialogExResult;
+
+/* DialogEx result callback type
+ * @warning comes from GUI thread
+ */
+typedef void (*DialogExResultCallback)(DialogExResult result, void* context);
+
+/* Allocate and initialize dialog
+ * This dialog used to ask simple questions like Yes/
+ */
+DialogEx* dialog_ex_alloc();
+
+/* Deinitialize and free dialog
+ * @param dialog - DialogEx instance
+ */
+void dialog_ex_free(DialogEx* dialog_ex);
+
+/* Get dialog view
+ * @param dialog - DialogEx instance
+ * @return View instance that can be used for embedding
+ */
+View* dialog_ex_get_view(DialogEx* dialog_ex);
+
+/* Set dialog result callback
+ * @param dialog_ex - DialogEx instance
+ * @param callback - result callback function
+ */
+void dialog_ex_set_result_callback(DialogEx* dialog_ex, DialogExResultCallback callback);
+
+/* Set dialog context
+ * @param dialog_ex - DialogEx instance
+ * @param context - context pointer, will be passed to result callback
+ */
+void dialog_ex_set_context(DialogEx* dialog_ex, void* context);
+
+/* Set dialog header text
+ * If text is null, dialog header will not be rendered
+ * @param dialog - DialogEx instance
+ * @param text - text to be shown, can be multiline
+ * @param x, y - text position
+ * @param horizontal, vertical - text aligment
+ */
+void dialog_ex_set_header(
+    DialogEx* dialog_ex,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical);
+
+/* Set dialog text
+ * If text is null, dialog text will not be rendered
+ * @param dialog - DialogEx instance
+ * @param text - text to be shown, can be multiline
+ * @param x, y - text position
+ * @param horizontal, vertical - text aligment
+ */
+void dialog_ex_set_text(
+    DialogEx* dialog_ex,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical);
+
+/* Set dialog icon
+ * If x or y is negative, dialog icon will not be rendered
+ * @param dialog - DialogEx instance
+ * @param x, y - icon position
+ * @param name - icon to be shown
+ */
+void dialog_ex_set_icon(DialogEx* dialog_ex, int8_t x, int8_t y, IconName name);
+
+/* Set left button text
+ * If text is null, left button will not be rendered and processed
+ * @param dialog - DialogEx instance
+ * @param text - text to be shown
+ */
+void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text);
+
+/* Set center button text
+ * If text is null, center button will not be rendered and processed
+ * @param dialog - DialogEx instance
+ * @param text - text to be shown
+ */
+void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text);
+
+/* Set right button text
+ * If text is null, right button will not be rendered and processed
+ * @param dialog - DialogEx instance
+ * @param text - text to be shown
+ */
+void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text);

+ 227 - 0
applications/gui/modules/popup.c

@@ -0,0 +1,227 @@
+#include "popup.h"
+#include <gui/elements.h>
+#include <furi.h>
+
+struct Popup {
+    View* view;
+    void* context;
+    PopupCallback callback;
+
+    osTimerId_t timer;
+    uint32_t timer_period_in_ms;
+    bool timer_enabled;
+};
+
+typedef struct {
+    const char* text;
+    uint8_t x;
+    uint8_t y;
+    Align horizontal;
+    Align vertical;
+} TextElement;
+
+typedef struct {
+    int8_t x;
+    int8_t y;
+    IconName name;
+} IconElement;
+
+typedef struct {
+    TextElement header;
+    TextElement text;
+    IconElement icon;
+} PopupModel;
+
+static void popup_view_draw_callback(Canvas* canvas, void* _model) {
+    PopupModel* model = _model;
+
+    // Prepare canvas
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    // TODO other criteria for the draw
+    if(model->icon.x >= 0 && model->icon.y >= 0) {
+        canvas_draw_icon_name(canvas, model->icon.x, model->icon.y, model->icon.name);
+    }
+
+    // Draw header
+    if(model->header.text != NULL) {
+        canvas_set_font(canvas, FontPrimary);
+        elements_multiline_text_aligned(
+            canvas,
+            model->header.x,
+            model->header.y,
+            model->header.horizontal,
+            model->header.vertical,
+            model->header.text);
+    }
+
+    // Draw text
+    if(model->text.text != NULL) {
+        canvas_set_font(canvas, FontSecondary);
+        elements_multiline_text_aligned(
+            canvas,
+            model->text.x,
+            model->text.y,
+            model->text.horizontal,
+            model->text.vertical,
+            model->text.text);
+    }
+}
+
+static void popup_timer_callback(void* context) {
+    furi_assert(context);
+    Popup* popup = context;
+
+    if(popup->callback) {
+        popup->callback(popup->context);
+    }
+}
+
+static bool popup_view_input_callback(InputEvent* event, void* context) {
+    Popup* popup = context;
+    bool consumed = false;
+
+    // Process key presses only
+    if(event->state && popup->callback) {
+        popup->callback(popup->context);
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+void popup_start_timer(void* context) {
+    Popup* popup = context;
+    if(popup->timer_enabled) {
+        uint32_t timer_period = popup->timer_period_in_ms / (1000.0f / osKernelGetTickFreq());
+        if(timer_period == 0) timer_period = 1;
+
+        if(osTimerStart(popup->timer, timer_period) != osOK) {
+            furi_assert(0);
+        };
+    }
+}
+
+void popup_stop_timer(void* context) {
+    Popup* popup = context;
+    osTimerStop(popup->timer);
+}
+
+Popup* popup_alloc() {
+    Popup* popup = furi_alloc(sizeof(Popup));
+    popup->view = view_alloc();
+    popup->timer = osTimerNew(popup_timer_callback, osTimerOnce, popup, NULL);
+    furi_assert(popup->timer);
+    popup->timer_period_in_ms = 1000;
+    popup->timer_enabled = false;
+
+    view_set_context(popup->view, popup);
+    view_allocate_model(popup->view, ViewModelTypeLockFree, sizeof(PopupModel));
+    view_set_draw_callback(popup->view, popup_view_draw_callback);
+    view_set_input_callback(popup->view, popup_view_input_callback);
+    view_set_enter_callback(popup->view, popup_start_timer);
+    view_set_exit_callback(popup->view, popup_stop_timer);
+
+    with_view_model(
+        popup->view, (PopupModel * model) {
+            model->header.text = NULL;
+            model->header.x = 0;
+            model->header.y = 0;
+            model->header.horizontal = AlignLeft;
+            model->header.vertical = AlignBottom;
+
+            model->text.text = NULL;
+            model->text.x = 0;
+            model->text.y = 0;
+            model->text.horizontal = AlignLeft;
+            model->text.vertical = AlignBottom;
+
+            // TODO other criteria for the draw
+            model->icon.x = -1;
+            model->icon.y = -1;
+            model->icon.name = I_ButtonCenter_7x7;
+        });
+    return popup;
+}
+
+void popup_free(Popup* popup) {
+    furi_assert(popup);
+    osTimerDelete(popup->timer);
+    view_free(popup->view);
+    free(popup);
+}
+
+View* popup_get_view(Popup* popup) {
+    furi_assert(popup);
+    return popup->view;
+}
+
+void popup_set_callback(Popup* popup, PopupCallback callback) {
+    furi_assert(popup);
+    popup->callback = callback;
+}
+
+void popup_set_context(Popup* popup, void* context) {
+    furi_assert(popup);
+    popup->context = context;
+}
+
+void popup_set_header(
+    Popup* popup,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical) {
+    furi_assert(popup);
+    with_view_model(
+        popup->view, (PopupModel * model) {
+            model->header.text = text;
+            model->header.x = x;
+            model->header.y = y;
+            model->header.horizontal = horizontal;
+            model->header.vertical = vertical;
+        });
+}
+
+void popup_set_text(
+    Popup* popup,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical) {
+    furi_assert(popup);
+    with_view_model(
+        popup->view, (PopupModel * model) {
+            model->text.text = text;
+            model->text.x = x;
+            model->text.y = y;
+            model->text.horizontal = horizontal;
+            model->text.vertical = vertical;
+        });
+}
+
+void popup_set_icon(Popup* popup, int8_t x, int8_t y, IconName name) {
+    furi_assert(popup);
+    with_view_model(
+        popup->view, (PopupModel * model) {
+            model->icon.x = x;
+            model->icon.y = y;
+            model->icon.name = name;
+        });
+}
+
+void popup_set_timeout(Popup* popup, uint32_t timeout_in_ms) {
+    furi_assert(popup);
+    popup->timer_period_in_ms = timeout_in_ms;
+}
+
+void popup_enable_timeout(Popup* popup) {
+    popup->timer_enabled = true;
+}
+
+void popup_disable_timeout(Popup* popup) {
+    popup->timer_enabled = false;
+}

+ 92 - 0
applications/gui/modules/popup.h

@@ -0,0 +1,92 @@
+#pragma once
+
+#include <gui/view.h>
+
+/* Popup anonymous structure */
+typedef struct Popup Popup;
+
+/* Popup result callback type
+ * @warning comes from GUI thread
+ */
+typedef void (*PopupCallback)(void* context);
+
+/* Allocate and initialize popup
+ * This popup used to ask simple questions like Yes/
+ */
+Popup* popup_alloc();
+
+/* Deinitialize and free popup
+ * @param popup - Popup instance
+ */
+void popup_free(Popup* popup);
+
+/* Get popup view
+ * @param popup - Popup instance
+ * @return View instance that can be used for embedding
+ */
+View* popup_get_view(Popup* popup);
+
+/* Set popup header text
+ * @param popup - Popup instance
+ * @param text - text to be shown
+ */
+void popup_set_callback(Popup* popup, PopupCallback callback);
+
+/* Set popup context
+ * @param popup - Popup instance
+ * @param context - context pointer, will be passed to result callback
+ */
+void popup_set_context(Popup* popup, void* context);
+
+/* Set popup header text
+ * If text is null, popup header will not be rendered
+ * @param popup - Popup instance
+ * @param text - text to be shown, can be multiline
+ * @param x, y - text position
+ * @param horizontal, vertical - text aligment
+ */
+void popup_set_header(
+    Popup* popup,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical);
+
+/* Set popup text
+ * If text is null, popup text will not be rendered
+ * @param popup - Popup instance
+ * @param text - text to be shown, can be multiline
+ * @param x, y - text position
+ * @param horizontal, vertical - text aligment
+ */
+void popup_set_text(
+    Popup* popup,
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical);
+
+/* Set popup icon
+ * @param popup - Popup instance
+ * @param x, y - icon position
+ * @param name - icon to be shown
+ */
+void popup_set_icon(Popup* popup, int8_t x, int8_t y, IconName name);
+
+/* Set popup timeout
+ * @param popup - Popup instance
+ * @param timeout_in_ms - popup timeout value in milliseconds
+ */
+void popup_set_timeout(Popup* popup, uint32_t timeout_in_ms);
+
+/* Enable popup timeout
+ * @param popup - Popup instance
+ */
+void popup_enable_timeout(Popup* popup);
+
+/* Disable popup timeout
+ * @param popup - Popup instance
+ */
+void popup_disable_timeout(Popup* popup);

+ 194 - 0
applications/gui/modules/submenu.c

@@ -0,0 +1,194 @@
+#include "submenu.h"
+#include <m-array.h>
+#include <furi.h>
+#include <gui/elements.h>
+
+struct SubmenuItem {
+    const char* label;
+    SubmenuItemCallback callback;
+    void* callback_context;
+};
+
+ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST);
+
+struct Submenu {
+    View* view;
+};
+
+typedef struct {
+    SubmenuItemArray_t items;
+    uint8_t position;
+    uint8_t window_position;
+} SubmenuModel;
+
+static void submenu_process_up(Submenu* submenu);
+static void submenu_process_down(Submenu* submenu);
+static void submenu_process_ok(Submenu* submenu);
+
+static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
+    SubmenuModel* model = _model;
+
+    const uint8_t item_height = 16;
+    const uint8_t item_width = 123;
+
+    canvas_clear(canvas);
+    canvas_set_font(canvas, FontSecondary);
+
+    uint8_t position = 0;
+    SubmenuItemArray_it_t it;
+
+    for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);
+        SubmenuItemArray_next(it)) {
+        uint8_t item_position = position - model->window_position;
+
+        if(item_position < 4) {
+            if(position == model->position) {
+                canvas_set_color(canvas, ColorBlack);
+                canvas_draw_box(
+                    canvas, 0, (item_position * item_height) + 1, item_width, item_height - 2);
+                canvas_set_color(canvas, ColorWhite);
+
+                canvas_draw_dot(canvas, 0, (item_position * item_height) + 1);
+                canvas_draw_dot(canvas, 0, (item_position * item_height) + item_height - 2);
+                canvas_draw_dot(canvas, item_width - 1, (item_position * item_height) + 1);
+                canvas_draw_dot(
+                    canvas, item_width - 1, (item_position * item_height) + item_height - 2);
+            } else {
+                canvas_set_color(canvas, ColorBlack);
+            }
+            canvas_draw_str(
+                canvas,
+                6,
+                (item_position * item_height) + item_height - 4,
+                SubmenuItemArray_cref(it)->label);
+        }
+
+        position++;
+    }
+
+    elements_scrollbar(canvas, model->position, SubmenuItemArray_size(model->items));
+}
+
+static bool submenu_view_input_callback(InputEvent* event, void* context) {
+    Submenu* submenu = context;
+    furi_assert(submenu);
+    bool consumed = false;
+
+    if(event->state) {
+        switch(event->input) {
+        case InputUp:
+            consumed = true;
+            submenu_process_up(submenu);
+            break;
+        case InputDown:
+            consumed = true;
+            submenu_process_down(submenu);
+            break;
+        case InputOk:
+            consumed = true;
+            submenu_process_ok(submenu);
+            break;
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+Submenu* submenu_alloc() {
+    Submenu* submenu = furi_alloc(sizeof(Submenu));
+    submenu->view = view_alloc();
+    view_set_context(submenu->view, submenu);
+    view_allocate_model(submenu->view, ViewModelTypeLocking, sizeof(SubmenuModel));
+    view_set_draw_callback(submenu->view, submenu_view_draw_callback);
+    view_set_input_callback(submenu->view, submenu_view_input_callback);
+
+    with_view_model(
+        submenu->view, (SubmenuModel * model) {
+            SubmenuItemArray_init(model->items);
+            model->position = 0;
+        });
+
+    return submenu;
+}
+
+void submenu_free(Submenu* submenu) {
+    furi_assert(submenu);
+
+    with_view_model(
+        submenu->view, (SubmenuModel * model) { SubmenuItemArray_clear(model->items); });
+    view_free(submenu->view);
+    free(submenu);
+}
+
+View* submenu_get_view(Submenu* submenu) {
+    furi_assert(submenu);
+    return submenu->view;
+}
+
+SubmenuItem* submenu_add_item(
+    Submenu* submenu,
+    const char* label,
+    SubmenuItemCallback callback,
+    void* callback_context) {
+    SubmenuItem* item = NULL;
+    furi_assert(label);
+    furi_assert(submenu);
+
+    with_view_model(
+        submenu->view, (SubmenuModel * model) {
+            item = SubmenuItemArray_push_new(model->items);
+            item->label = label;
+            item->callback = callback;
+            item->callback_context = callback_context;
+        });
+
+    return item;
+}
+
+void submenu_process_up(Submenu* submenu) {
+    with_view_model(
+        submenu->view, (SubmenuModel * model) {
+            if(model->position > 0) {
+                model->position--;
+                if((model->position - model->window_position) < 1 && model->window_position > 0) {
+                    model->window_position--;
+                }
+            } else {
+                model->position = SubmenuItemArray_size(model->items) - 1;
+                model->window_position = model->position - 3;
+            }
+        });
+}
+
+void submenu_process_down(Submenu* submenu) {
+    with_view_model(
+        submenu->view, (SubmenuModel * model) {
+            if(model->position < (SubmenuItemArray_size(model->items) - 1)) {
+                model->position++;
+                if((model->position - model->window_position) > 2 &&
+                   model->window_position < (SubmenuItemArray_size(model->items) - 4)) {
+                    model->window_position++;
+                }
+            } else {
+                model->position = 0;
+                model->window_position = 0;
+            }
+        });
+}
+
+void submenu_process_ok(Submenu* submenu) {
+    SubmenuItem* item = NULL;
+
+    with_view_model(
+        submenu->view, (SubmenuModel * model) {
+            if(model->position < (SubmenuItemArray_size(model->items))) {
+                item = SubmenuItemArray_get(model->items, model->position);
+            }
+        });
+
+    if(item && item->callback) {
+        item->callback(item->callback_context);
+    }
+}

+ 36 - 0
applications/gui/modules/submenu.h

@@ -0,0 +1,36 @@
+#pragma once
+#include <gui/view.h>
+
+/* Submenu anonymous structure */
+typedef struct Submenu Submenu;
+typedef struct SubmenuItem SubmenuItem;
+typedef void (*SubmenuItemCallback)(void* context);
+
+/* Allocate and initialize submenu
+ * This submenu is used to select one option
+ */
+Submenu* submenu_alloc();
+
+/* Deinitialize and free submenu
+ * @param submenu - Submenu instance
+ */
+void submenu_free(Submenu* submenu);
+
+/* Get submenu view
+ * @param submenu - Submenu instance
+ * @return View instance that can be used for embedding
+ */
+View* submenu_get_view(Submenu* submenu);
+
+/* Add item to submenu
+ * @param submenu - Submenu instance
+ * @param label - menu item label
+ * @param callback - menu item callback
+ * @param callback_context - menu item callback context
+ * @return SubmenuItem instance that can be used to modify or delete that item
+ */
+SubmenuItem* submenu_add_item(
+    Submenu* submenu,
+    const char* label,
+    SubmenuItemCallback callback,
+    void* callback_context);

+ 370 - 0
applications/gui/modules/text_input.c

@@ -0,0 +1,370 @@
+#include "text_input.h"
+#include <furi.h>
+
+struct TextInput {
+    View* view;
+};
+
+typedef struct {
+    const char text;
+    const uint8_t x;
+    const uint8_t y;
+} TextInputKey;
+
+typedef struct {
+    const char* header;
+    char* text;
+    uint8_t max_text_length;
+
+    TextInputCallback callback;
+    void* callback_context;
+
+    uint8_t selected_row;
+    uint8_t selected_column;
+} TextInputModel;
+
+static const uint8_t keyboard_origin_x = 1;
+static const uint8_t keyboard_origin_y = 29;
+static const uint8_t keyboard_row_count = 3;
+
+#define ENTER_KEY '\r'
+#define BACKSPACE_KEY '\b'
+
+static const TextInputKey keyboard_keys_row_1[] = {
+    {'q', 1, 8},
+    {'w', 10, 8},
+    {'e', 19, 8},
+    {'r', 28, 8},
+    {'t', 37, 8},
+    {'y', 46, 8},
+    {'u', 55, 8},
+    {'i', 64, 8},
+    {'o', 73, 8},
+    {'p', 82, 8},
+    {'7', 91, 8},
+    {'8', 100, 8},
+    {'9', 109, 8},
+    {'_', 118, 8},
+};
+
+static const TextInputKey keyboard_keys_row_2[] = {
+    {'a', 1, 20},
+    {'s', 10, 20},
+    {'d', 19, 20},
+    {'f', 28, 20},
+    {'g', 37, 20},
+    {'h', 46, 20},
+    {'j', 55, 20},
+    {'k', 64, 20},
+    {'l', 73, 20},
+    {'4', 82, 20},
+    {'5', 91, 20},
+    {'6', 100, 20},
+    {BACKSPACE_KEY, 110, 12},
+};
+
+static const TextInputKey keyboard_keys_row_3[] = {
+    {'z', 1, 32},
+    {'x', 10, 32},
+    {'c', 19, 32},
+    {'v', 28, 32},
+    {'b', 37, 32},
+    {'n', 46, 32},
+    {'m', 55, 32},
+    {'0', 64, 32},
+    {'1', 73, 32},
+    {'2', 82, 32},
+    {'3', 91, 32},
+    {ENTER_KEY, 102, 23},
+};
+
+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(TextInputKey);
+        break;
+    case 2:
+        row_size = sizeof(keyboard_keys_row_2) / sizeof(TextInputKey);
+        break;
+    case 3:
+        row_size = sizeof(keyboard_keys_row_3) / sizeof(TextInputKey);
+        break;
+    }
+
+    return row_size;
+}
+
+static const TextInputKey* get_row(uint8_t row_index) {
+    const 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;
+    }
+
+    return row;
+}
+
+static const char get_selected_char(TextInputModel* model) {
+    return get_row(model->selected_row)[model->selected_column].text;
+}
+
+static const bool char_is_lowercase(char letter) {
+    return (letter >= 0x61 && letter <= 0x7A);
+}
+
+static const char char_to_uppercase(const char letter) {
+    return (letter - 0x20);
+}
+
+static void text_input_view_draw_callback(Canvas* canvas, void* _model) {
+    TextInputModel* model = _model;
+    uint8_t text_length = strlen(model->text);
+    uint8_t needed_string_width = canvas_width(canvas) - 4 - 7 - 4;
+    char* text = model->text;
+
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    canvas_draw_str(canvas, 2, 8, model->header);
+    canvas_draw_line(canvas, 2, 12, canvas_width(canvas) - 7, 12);
+    canvas_draw_line(canvas, 1, 13, 1, 25);
+    canvas_draw_line(canvas, canvas_width(canvas) - 6, 13, canvas_width(canvas) - 6, 25);
+    canvas_draw_line(canvas, 2, 26, canvas_width(canvas) - 7, 26);
+
+    while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) {
+        text++;
+    }
+
+    canvas_draw_str(canvas, 4, 22, text);
+    canvas_draw_str(canvas, 4 + canvas_string_width(canvas, text) + 1, 22, "|");
+
+    canvas_set_font(canvas, FontKeyboard);
+
+    for(uint8_t row = 0; row <= keyboard_row_count; row++) {
+        uint8_t volatile column_count = get_row_size(row);
+        const 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_name(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        I_KeySaveSelected_24x11);
+                } else {
+                    canvas_draw_icon_name(
+                        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_name(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        I_KeyBackspaceSelected_16x9);
+                } else {
+                    canvas_draw_icon_name(
+                        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(text_length == 0 && char_is_lowercase(keys[column].text)) {
+                    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);
+                }
+            }
+        }
+    }
+}
+
+static void text_input_handle_up(TextInput* text_input) {
+    with_view_model(
+        text_input->view, (TextInputModel * model) {
+            if(model->selected_row > 0) {
+                model->selected_row--;
+            }
+        });
+}
+
+static void text_input_handle_down(TextInput* text_input) {
+    with_view_model(
+        text_input->view, (TextInputModel * model) {
+            if(model->selected_row < keyboard_row_count - 1) {
+                model->selected_row++;
+                if(model->selected_column > get_row_size(model->selected_row) - 1) {
+                    model->selected_column = get_row_size(model->selected_row) - 1;
+                }
+            }
+        });
+}
+
+static void text_input_handle_left(TextInput* text_input) {
+    with_view_model(
+        text_input->view, (TextInputModel * model) {
+            if(model->selected_column > 0) {
+                model->selected_column--;
+            } else {
+                model->selected_column = get_row_size(model->selected_row) - 1;
+            }
+        });
+}
+
+static void text_input_handle_right(TextInput* text_input) {
+    with_view_model(
+        text_input->view, (TextInputModel * model) {
+            if(model->selected_column < get_row_size(model->selected_row) - 1) {
+                model->selected_column++;
+            } else {
+                model->selected_column = 0;
+            }
+        });
+}
+
+static void text_input_handle_ok(TextInput* text_input) {
+    with_view_model(
+        text_input->view, (TextInputModel * model) {
+            char selected = get_selected_char(model);
+            uint8_t text_length = strlen(model->text);
+
+            if(selected == ENTER_KEY) {
+                if(model->callback != 0) {
+                    model->callback(model->callback_context, model->text);
+                }
+            } else if(selected == BACKSPACE_KEY) {
+                if(text_length > 0) {
+                    model->text[text_length - 1] = 0;
+                }
+            } else if(text_length < model->max_text_length) {
+                if(text_length == 0 && char_is_lowercase(selected)) {
+                    selected = char_to_uppercase(selected);
+                }
+                model->text[text_length] = selected;
+                model->text[text_length + 1] = 0;
+            }
+        });
+}
+
+static bool text_input_view_input_callback(InputEvent* event, void* context) {
+    TextInput* text_input = context;
+    furi_assert(text_input);
+    bool consumed = false;
+
+    if(event->state) {
+        switch(event->input) {
+        case InputUp:
+            text_input_handle_up(text_input);
+            consumed = true;
+            break;
+        case InputDown:
+            text_input_handle_down(text_input);
+            consumed = true;
+            break;
+        case InputLeft:
+            text_input_handle_left(text_input);
+            consumed = true;
+            break;
+        case InputRight:
+            text_input_handle_right(text_input);
+            consumed = true;
+            break;
+        case InputOk:
+            text_input_handle_ok(text_input);
+            consumed = true;
+            break;
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+TextInput* text_input_alloc() {
+    TextInput* text_input = furi_alloc(sizeof(TextInput));
+    text_input->view = view_alloc();
+    view_set_context(text_input->view, text_input);
+    view_allocate_model(text_input->view, ViewModelTypeLocking, sizeof(TextInputModel));
+    view_set_draw_callback(text_input->view, text_input_view_draw_callback);
+    view_set_input_callback(text_input->view, text_input_view_input_callback);
+
+    with_view_model(
+        text_input->view, (TextInputModel * model) {
+            model->max_text_length = 0;
+            model->header = "";
+            model->selected_row = 0;
+            model->selected_column = 0;
+        });
+
+    return text_input;
+}
+
+void text_input_free(TextInput* text_input) {
+    furi_assert(text_input);
+    view_free(text_input->view);
+    free(text_input);
+}
+
+View* text_input_get_view(TextInput* text_input) {
+    furi_assert(text_input);
+    return text_input->view;
+}
+
+void text_input_set_result_callback(
+    TextInput* text_input,
+    TextInputCallback callback,
+    void* callback_context,
+    char* text,
+    uint8_t max_text_length) {
+    with_view_model(
+        text_input->view, (TextInputModel * model) {
+            model->callback = callback;
+            model->callback_context = callback_context;
+            model->text = text;
+            model->max_text_length = max_text_length;
+        });
+}
+
+void text_input_set_header_text(TextInput* text_input, const char* text) {
+    with_view_model(
+        text_input->view, (TextInputModel * model) { model->header = text; });
+}

+ 42 - 0
applications/gui/modules/text_input.h

@@ -0,0 +1,42 @@
+#pragma once
+#include <gui/view.h>
+
+/* Text input anonymous structure */
+typedef struct TextInput TextInput;
+typedef void (*TextInputCallback)(void* context, char* text);
+
+/* Allocate and initialize text input
+ * This text input is used to enter string
+ */
+TextInput* text_input_alloc();
+
+/* Deinitialize and free text input
+ * @param text_input - Text input instance
+ */
+void text_input_free(TextInput* text_input);
+
+/* Get text input view
+ * @param text_input - Text input instance
+ * @return View instance that can be used for embedding
+ */
+View* text_input_get_view(TextInput* text_input);
+
+/* Deinitialize and free text input
+ * @param text_input - Text input instance
+ * @param callback - callback fn
+ * @param callback_context - callback context
+ * @param text - text buffer to use
+ * @param max_text_length - text buffer length
+ */
+void text_input_set_result_callback(
+    TextInput* text_input,
+    TextInputCallback callback,
+    void* callback_context,
+    char* text,
+    uint8_t max_text_length);
+
+/* Set text input header text
+ * @param text input - Text input instance
+ * @param text - text to be shown
+ */
+void text_input_set_header_text(TextInput* text_input, const char* text);

BIN
assets/icons/Common/ButtonCenter_7x7.png


BIN
assets/icons/Common/ButtonLeft_4x7.png


BIN
assets/icons/Common/ButtonRight_4x7.png


BIN
assets/icons/Keyboard/KeyBackspaceSelected_16x9.png


BIN
assets/icons/Keyboard/KeyBackspace_16x9.png


BIN
assets/icons/Keyboard/KeySaveSelected_24x11.png


BIN
assets/icons/Keyboard/KeySave_24x11.png


BIN
assets/icons/iButton/DolphinExcited_64x63.png


BIN
assets/icons/iButton/DolphinMafia_115x62.png


BIN
assets/icons/iButton/DolphinWait_61x59.png


BIN
assets/icons/iButton/iButtonDolphinSuccess_109x60.png


BIN
assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png


BIN
assets/icons/iButton/iButtonKey_49x44.png


+ 1 - 1
lib/u8g2/u8g2.h

@@ -1325,7 +1325,7 @@ u8g2_uint_t u8g2_DrawExtUTF8(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, uint8_t
 
 uint8_t u8g2_IsAllValidUTF8(u8g2_t *u8g2, const char *str);	// checks whether all codes are valid
 
-u8g2_uint_t u8g2_GetStrWidth(u8g2_t *u8g2, const char *s);
+u8g2_long_t u8g2_GetStrWidth(u8g2_t *u8g2, const char *s);
 u8g2_uint_t u8g2_GetUTF8Width(u8g2_t *u8g2, const char *str);
 
 void u8g2_SetFontPosBaseline(u8g2_t *u8g2);

+ 6 - 5
lib/u8g2/u8g2_font.c

@@ -1113,12 +1113,13 @@ uint8_t u8g2_IsAllValidUTF8(u8g2_t *u8g2, const char *str)
 
 
 /* string calculation is stilll not 100% perfect as it addes the initial string offset to the overall size */
-static u8g2_uint_t u8g2_string_width(u8g2_t *u8g2, const char *str) U8G2_NOINLINE;
-static u8g2_uint_t u8g2_string_width(u8g2_t *u8g2, const char *str)
+static u8g2_long_t u8g2_string_width(u8g2_t *u8g2, const char *str) U8G2_NOINLINE;
+static u8g2_long_t u8g2_string_width(u8g2_t *u8g2, const char *str)
 {
   uint16_t e;
-  u8g2_uint_t  w, dx;
-  
+  u8g2_uint_t dx;
+  u8g2_long_t w;
+
   u8g2->font_decode.glyph_width = 0;
   u8x8_utf8_init(u8g2_GetU8x8(u8g2));
   
@@ -1251,7 +1252,7 @@ static u8g2_uint_t u8g2_calculate_exact_string_width(u8g2_t *u8g2, const char *s
 
 
 
-u8g2_uint_t u8g2_GetStrWidth(u8g2_t *u8g2, const char *s)
+u8g2_long_t u8g2_GetStrWidth(u8g2_t *u8g2, const char *s)
 {
   u8g2->u8x8.next_cb = u8x8_ascii_next;
   return u8g2_string_width(u8g2, s);

+ 0 - 106
lib/u8g2_vendor/u8g2_vendor.c

@@ -1,106 +0,0 @@
-#include "u8g2_support.h"
-#include "main.h"
-#include "cmsis_os.h"
-#include "gpio.h"
-
-#include <stdio.h>
-
-extern SPI_HandleTypeDef hspi1;
-
-// #define DEBUG 1
-
-uint8_t u8g2_gpio_and_delay_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
-    switch(msg){
-        //Initialize SPI peripheral
-        case U8X8_MSG_GPIO_AND_DELAY_INIT:
-            /* HAL initialization contains all what we need so we can skip this part. */
-        break;
-
-        //Function which implements a delay, arg_int contains the amount of ms
-        case U8X8_MSG_DELAY_MILLI:
-            osDelay(arg_int);
-        break;
-
-        //Function which delays 10us
-        case U8X8_MSG_DELAY_10MICRO:
-            delay_us(10);
-        break;
-
-        //Function which delays 100ns
-        case U8X8_MSG_DELAY_100NANO:
-            asm("nop");
-        break;
-
-        //Function to define the logic level of the RESET line
-        case U8X8_MSG_GPIO_RESET:
-            #ifdef DEBUG
-                printf("[u8g2] rst %d\n", arg_int);
-            #endif
-
-            HAL_GPIO_WritePin(DISPLAY_RST_GPIO_Port, DISPLAY_RST_Pin, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET);
-        break;
-
-        default:
-            #ifdef DEBUG
-                printf("[u8g2] unknown io %d\n", msg);
-            #endif
-
-            return 0; //A message was received which is not implemented, return 0 to indicate an error
-    }
-
-    return 1; // command processed successfully.
-}
-
-uint8_t u8x8_hw_spi_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr){
-    switch (msg) {
-        case U8X8_MSG_BYTE_SEND:
-            #ifdef DEBUG
-                printf("[u8g2] send %d bytes %02X\n", arg_int, ((uint8_t*)arg_ptr)[0]);
-            #endif
-
-            HAL_SPI_Transmit(&hspi1, (uint8_t *)arg_ptr, arg_int, 10000);
-        break;
-
-        case U8X8_MSG_BYTE_SET_DC:
-            #ifdef DEBUG
-                printf("[u8g2] dc %d\n", arg_int);
-            #endif
-
-            HAL_GPIO_WritePin(DISPLAY_DI_GPIO_Port, DISPLAY_DI_Pin, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET);
-        break;
-
-        case U8X8_MSG_BYTE_INIT:
-            #ifdef DEBUG
-                printf("[u8g2] init\n");
-            #endif
-            HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_RESET);
-        break;
-
-        case U8X8_MSG_BYTE_START_TRANSFER:
-            #ifdef DEBUG
-                printf("[u8g2] start\n");
-            #endif
-
-            HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_RESET);
-            asm("nop");
-        break;
-
-        case U8X8_MSG_BYTE_END_TRANSFER:
-            #ifdef DEBUG
-                printf("[u8g2] end\n");
-            #endif
-
-            asm("nop");
-            HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_SET);
-        break;
-
-        default:
-            #ifdef DEBUG
-                printf("[u8g2] unknown xfer %d\n", msg);
-            #endif
-
-            return 0;
-    }
-
-    return 1;
-}

+ 0 - 4
lib/u8g2_vendor/u8g2_vendor.h

@@ -1,4 +0,0 @@
-#include "u8g2/u8g2.h"
-
-uint8_t u8g2_gpio_and_delay_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
-uint8_t u8x8_hw_spi_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);