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>
This commit is contained in:
@@ -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() {
|
||||
|
@@ -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
applications/gui/modules/dialog_ex.c
Normal file
232
applications/gui/modules/dialog_ex.c
Normal file
@@ -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
applications/gui/modules/dialog_ex.h
Normal file
105
applications/gui/modules/dialog_ex.h
Normal file
@@ -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
applications/gui/modules/popup.c
Normal file
227
applications/gui/modules/popup.c
Normal file
@@ -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
applications/gui/modules/popup.h
Normal file
92
applications/gui/modules/popup.h
Normal file
@@ -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
applications/gui/modules/submenu.c
Normal file
194
applications/gui/modules/submenu.c
Normal file
@@ -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
applications/gui/modules/submenu.h
Normal file
36
applications/gui/modules/submenu.h
Normal file
@@ -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
applications/gui/modules/text_input.c
Normal file
370
applications/gui/modules/text_input.c
Normal file
@@ -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
applications/gui/modules/text_input.h
Normal file
42
applications/gui/modules/text_input.h
Normal file
@@ -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);
|
Reference in New Issue
Block a user