[FL-1497] GUI textbox element and widget (#792)
* canvas: add font parameters * elements: add text box element * widget: add text box element * nfc: rework delete and info scene with text box widget * gui: update documentation * gui: fix canvas_get_font_params return Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
215
applications/gui/elements.c
Normal file → Executable file
215
applications/gui/elements.c
Normal file → Executable file
@@ -10,6 +10,18 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t leading_min;
|
||||
uint8_t leading_default;
|
||||
uint8_t height;
|
||||
uint8_t descender;
|
||||
uint8_t len;
|
||||
const char* text;
|
||||
} ElementTextBoxLine;
|
||||
|
||||
void elements_progress_bar(
|
||||
Canvas* canvas,
|
||||
@@ -352,3 +364,206 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) {
|
||||
string_cat(string, "...");
|
||||
}
|
||||
}
|
||||
|
||||
void elements_text_box(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
Align horizontal,
|
||||
Align vertical,
|
||||
const char* text) {
|
||||
furi_assert(canvas);
|
||||
|
||||
ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM];
|
||||
bool bold = false;
|
||||
bool mono = false;
|
||||
bool inversed = false;
|
||||
bool inversed_present = false;
|
||||
Font current_font = FontSecondary;
|
||||
Font prev_font = FontSecondary;
|
||||
CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font);
|
||||
|
||||
// Fill line parameters
|
||||
uint8_t line_leading_min = font_params->leading_min;
|
||||
uint8_t line_leading_default = font_params->leading_default;
|
||||
uint8_t line_height = font_params->height;
|
||||
uint8_t line_descender = font_params->descender;
|
||||
uint8_t line_num = 0;
|
||||
uint8_t line_width = 0;
|
||||
uint8_t line_len = 0;
|
||||
uint8_t total_height_min = 0;
|
||||
uint8_t total_height_default = 0;
|
||||
uint16_t i = 0;
|
||||
bool full_text_processed = false;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// Fill all lines
|
||||
line[0].text = text;
|
||||
for(i = 0; !full_text_processed; i++) {
|
||||
line_len++;
|
||||
// Identify line height
|
||||
if(prev_font != current_font) {
|
||||
font_params = canvas_get_font_params(canvas, current_font);
|
||||
line_leading_min = MAX(line_leading_min, font_params->leading_min);
|
||||
line_leading_default = MAX(line_leading_default, font_params->leading_default);
|
||||
line_height = MAX(line_height, font_params->height);
|
||||
line_descender = MAX(line_descender, font_params->descender);
|
||||
prev_font = current_font;
|
||||
}
|
||||
// Set the font
|
||||
if(text[i] == '\e' && text[i + 1]) {
|
||||
i++;
|
||||
line_len++;
|
||||
if(text[i] == ELEMENTS_BOLD_MARKER) {
|
||||
if(bold) {
|
||||
current_font = FontSecondary;
|
||||
} else {
|
||||
current_font = FontPrimary;
|
||||
}
|
||||
canvas_set_font(canvas, current_font);
|
||||
bold = !bold;
|
||||
}
|
||||
if(text[i] == ELEMENTS_MONO_MARKER) {
|
||||
if(mono) {
|
||||
current_font = FontSecondary;
|
||||
} else {
|
||||
current_font = FontKeyboard;
|
||||
}
|
||||
canvas_set_font(canvas, FontKeyboard);
|
||||
mono = !mono;
|
||||
}
|
||||
if(text[i] == ELEMENTS_INVERSED_MARKER) {
|
||||
inversed_present = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(text[i] != '\n') {
|
||||
line_width += canvas_glyph_width(canvas, text[i]);
|
||||
}
|
||||
// Process new line
|
||||
if(text[i] == '\n' || text[i] == '\0' || line_width > width) {
|
||||
if(line_width > width) {
|
||||
line_width -= canvas_glyph_width(canvas, text[i--]);
|
||||
line_len--;
|
||||
}
|
||||
if(text[i] == '\0') {
|
||||
full_text_processed = true;
|
||||
}
|
||||
if(inversed_present) {
|
||||
line_leading_min += 1;
|
||||
line_leading_default += 1;
|
||||
inversed_present = false;
|
||||
}
|
||||
line[line_num].leading_min = line_leading_min;
|
||||
line[line_num].leading_default = line_leading_default;
|
||||
line[line_num].height = line_height;
|
||||
line[line_num].descender = line_descender;
|
||||
if(total_height_min + line_leading_min > height) {
|
||||
line_num--;
|
||||
break;
|
||||
}
|
||||
total_height_min += line_leading_min;
|
||||
total_height_default += line_leading_default;
|
||||
line[line_num].len = line_len;
|
||||
if(horizontal == AlignCenter) {
|
||||
line[line_num].x = x + (width - line_width) / 2;
|
||||
} else if(horizontal == AlignRight) {
|
||||
line[line_num].x = x + (width - line_width);
|
||||
} else {
|
||||
line[line_num].x = x;
|
||||
}
|
||||
line[line_num].y = total_height_min;
|
||||
line_num++;
|
||||
if(text[i + 1]) {
|
||||
line[line_num].text = &text[i + 1];
|
||||
}
|
||||
|
||||
line_leading_min = font_params->leading_min;
|
||||
line_height = font_params->height;
|
||||
line_descender = font_params->descender;
|
||||
line_width = 0;
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Set vertical alignment for all lines
|
||||
if(full_text_processed) {
|
||||
if(total_height_default < height) {
|
||||
if(vertical == AlignTop) {
|
||||
line[0].y = y + line[0].height;
|
||||
} else if(vertical == AlignCenter) {
|
||||
line[0].y = y + line[0].height + (height - total_height_default) / 2;
|
||||
} else if(vertical == AlignBottom) {
|
||||
line[0].y = y + line[0].height + (height - total_height_default);
|
||||
}
|
||||
if(line_num > 1) {
|
||||
for(uint8_t i = 1; i < line_num; i++) {
|
||||
line[i].y = line[i - 1].y + line[i - 1].leading_default;
|
||||
}
|
||||
}
|
||||
} else if(line_num > 1) {
|
||||
uint8_t free_pixel_num = height - total_height_min;
|
||||
uint8_t fill_pixel = 0;
|
||||
uint8_t j = 1;
|
||||
line[0].y = line[0].height;
|
||||
while(fill_pixel < free_pixel_num) {
|
||||
line[j].y = line[j - 1].y + line[j - 1].leading_min + 1;
|
||||
fill_pixel++;
|
||||
j = j % (line_num - 1) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw line by line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
bold = false;
|
||||
mono = false;
|
||||
inversed = false;
|
||||
for(uint8_t i = 0; i < line_num; i++) {
|
||||
for(uint8_t j = 0; j < line[i].len; j++) {
|
||||
// Process format symbols
|
||||
if(line[i].text[j] == ELEMENTS_BOLD_MARKER) {
|
||||
if(bold) {
|
||||
current_font = FontSecondary;
|
||||
} else {
|
||||
current_font = FontPrimary;
|
||||
}
|
||||
canvas_set_font(canvas, current_font);
|
||||
bold = !bold;
|
||||
continue;
|
||||
}
|
||||
if(line[i].text[j] == ELEMENTS_MONO_MARKER) {
|
||||
if(mono) {
|
||||
current_font = FontSecondary;
|
||||
} else {
|
||||
current_font = FontKeyboard;
|
||||
}
|
||||
canvas_set_font(canvas, current_font);
|
||||
mono = !mono;
|
||||
continue;
|
||||
}
|
||||
if(line[i].text[j] == ELEMENTS_INVERSED_MARKER) {
|
||||
inversed = !inversed;
|
||||
continue;
|
||||
}
|
||||
if(inversed) {
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
line[i].x - 1,
|
||||
line[i].y - line[i].height - 1,
|
||||
canvas_glyph_width(canvas, line[i].text[j]) + 1,
|
||||
line[i].height + line[i].descender + 2);
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
|
||||
canvas_invert_color(canvas);
|
||||
} else {
|
||||
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
|
||||
}
|
||||
line[i].x += canvas_glyph_width(canvas, line[i].text[j]);
|
||||
}
|
||||
}
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
}
|
||||
|
Reference in New Issue
Block a user