widget_element_text_scroll.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #include "widget_element_i.h"
  2. #include <m-string.h>
  3. #include <gui/elements.h>
  4. #include <m-array.h>
  5. #define WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET (4)
  6. typedef struct {
  7. Font font;
  8. Align horizontal;
  9. string_t text;
  10. } TextScrollLineArray;
  11. ARRAY_DEF(TextScrollLineArray, TextScrollLineArray, M_POD_OPLIST)
  12. typedef struct {
  13. TextScrollLineArray_t line_array;
  14. uint8_t x;
  15. uint8_t y;
  16. uint8_t width;
  17. uint8_t height;
  18. string_t text;
  19. uint8_t scroll_pos_total;
  20. uint8_t scroll_pos_current;
  21. bool text_formatted;
  22. } WidgetElementTextScrollModel;
  23. static bool
  24. widget_element_text_scroll_process_ctrl_symbols(TextScrollLineArray* line, string_t text) {
  25. bool processed = false;
  26. do {
  27. if(string_get_char(text, 0) != '\e') break;
  28. char ctrl_symbol = string_get_char(text, 1);
  29. if(ctrl_symbol == 'c') {
  30. line->horizontal = AlignCenter;
  31. } else if(ctrl_symbol == 'r') {
  32. line->horizontal = AlignRight;
  33. } else if(ctrl_symbol == '#') {
  34. line->font = FontPrimary;
  35. }
  36. string_right(text, 2);
  37. processed = true;
  38. } while(false);
  39. return processed;
  40. }
  41. void widget_element_text_scroll_add_line(WidgetElement* element, TextScrollLineArray* line) {
  42. WidgetElementTextScrollModel* model = element->model;
  43. TextScrollLineArray new_line;
  44. new_line.font = line->font;
  45. new_line.horizontal = line->horizontal;
  46. string_init_set(new_line.text, line->text);
  47. TextScrollLineArray_push_back(model->line_array, new_line);
  48. }
  49. static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* element) {
  50. WidgetElementTextScrollModel* model = element->model;
  51. TextScrollLineArray line_tmp;
  52. bool all_text_processed = false;
  53. string_init(line_tmp.text);
  54. bool reached_new_line = true;
  55. uint16_t total_height = 0;
  56. while(!all_text_processed) {
  57. if(reached_new_line) {
  58. // Set default line properties
  59. line_tmp.font = FontSecondary;
  60. line_tmp.horizontal = AlignLeft;
  61. string_reset(line_tmp.text);
  62. // Process control symbols
  63. while(widget_element_text_scroll_process_ctrl_symbols(&line_tmp, model->text))
  64. ;
  65. }
  66. // Set canvas font
  67. canvas_set_font(canvas, line_tmp.font);
  68. CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font);
  69. total_height += params->height;
  70. if(total_height > model->height) {
  71. model->scroll_pos_total++;
  72. }
  73. uint8_t line_width = 0;
  74. uint16_t char_i = 0;
  75. while(true) {
  76. char next_char = string_get_char(model->text, char_i++);
  77. if(next_char == '\0') {
  78. string_push_back(line_tmp.text, '\0');
  79. widget_element_text_scroll_add_line(element, &line_tmp);
  80. total_height += params->leading_default - params->height;
  81. all_text_processed = true;
  82. break;
  83. } else if(next_char == '\n') {
  84. string_push_back(line_tmp.text, '\0');
  85. widget_element_text_scroll_add_line(element, &line_tmp);
  86. string_right(model->text, char_i);
  87. total_height += params->leading_default - params->height;
  88. reached_new_line = true;
  89. break;
  90. } else {
  91. line_width += canvas_glyph_width(canvas, next_char);
  92. if(line_width > model->width) {
  93. string_push_back(line_tmp.text, '\0');
  94. widget_element_text_scroll_add_line(element, &line_tmp);
  95. string_right(model->text, char_i - 1);
  96. string_reset(line_tmp.text);
  97. total_height += params->leading_default - params->height;
  98. reached_new_line = false;
  99. break;
  100. } else {
  101. string_push_back(line_tmp.text, next_char);
  102. }
  103. }
  104. }
  105. }
  106. string_clear(line_tmp.text);
  107. }
  108. static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* element) {
  109. furi_assert(canvas);
  110. furi_assert(element);
  111. furi_mutex_acquire(element->model_mutex, FuriWaitForever);
  112. WidgetElementTextScrollModel* model = element->model;
  113. if(!model->text_formatted) {
  114. widget_element_text_scroll_fill_lines(canvas, element);
  115. model->text_formatted = true;
  116. }
  117. uint8_t y = model->y;
  118. uint8_t x = model->x;
  119. uint16_t curr_line = 0;
  120. if(TextScrollLineArray_size(model->line_array)) {
  121. TextScrollLineArray_it_t it;
  122. for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);
  123. TextScrollLineArray_next(it), curr_line++) {
  124. if(curr_line < model->scroll_pos_current) continue;
  125. TextScrollLineArray* line = TextScrollLineArray_ref(it);
  126. CanvasFontParameters* params = canvas_get_font_params(canvas, line->font);
  127. if(y + params->descender > model->y + model->height) break;
  128. canvas_set_font(canvas, line->font);
  129. if(line->horizontal == AlignLeft) {
  130. x = model->x;
  131. } else if(line->horizontal == AlignCenter) {
  132. x = (model->x + model->width) / 2;
  133. } else if(line->horizontal == AlignRight) {
  134. x = model->x + model->width;
  135. }
  136. canvas_draw_str_aligned(
  137. canvas, x, y, line->horizontal, AlignTop, string_get_cstr(line->text));
  138. y += params->leading_default;
  139. }
  140. // Draw scroll bar
  141. if(model->scroll_pos_total > 1) {
  142. elements_scrollbar_pos(
  143. canvas,
  144. model->x + model->width + WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET,
  145. model->y,
  146. model->height,
  147. model->scroll_pos_current,
  148. model->scroll_pos_total);
  149. }
  150. }
  151. furi_mutex_release(element->model_mutex);
  152. }
  153. static bool widget_element_text_scroll_input(InputEvent* event, WidgetElement* element) {
  154. furi_assert(event);
  155. furi_assert(element);
  156. furi_mutex_acquire(element->model_mutex, FuriWaitForever);
  157. WidgetElementTextScrollModel* model = element->model;
  158. bool consumed = false;
  159. if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
  160. if(event->key == InputKeyUp) {
  161. if(model->scroll_pos_current > 0) {
  162. model->scroll_pos_current--;
  163. }
  164. consumed = true;
  165. } else if(event->key == InputKeyDown) {
  166. if((model->scroll_pos_total > 1) &&
  167. (model->scroll_pos_current < model->scroll_pos_total - 1)) {
  168. model->scroll_pos_current++;
  169. }
  170. consumed = true;
  171. }
  172. }
  173. furi_mutex_release(element->model_mutex);
  174. return consumed;
  175. }
  176. static void widget_element_text_scroll_free(WidgetElement* text_scroll) {
  177. furi_assert(text_scroll);
  178. WidgetElementTextScrollModel* model = text_scroll->model;
  179. TextScrollLineArray_it_t it;
  180. for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);
  181. TextScrollLineArray_next(it)) {
  182. TextScrollLineArray* line = TextScrollLineArray_ref(it);
  183. string_clear(line->text);
  184. }
  185. TextScrollLineArray_clear(model->line_array);
  186. string_clear(model->text);
  187. free(text_scroll->model);
  188. furi_mutex_free(text_scroll->model_mutex);
  189. free(text_scroll);
  190. }
  191. WidgetElement* widget_element_text_scroll_create(
  192. uint8_t x,
  193. uint8_t y,
  194. uint8_t width,
  195. uint8_t height,
  196. const char* text) {
  197. furi_assert(text);
  198. // Allocate and init model
  199. WidgetElementTextScrollModel* model = malloc(sizeof(WidgetElementTextScrollModel));
  200. model->x = x;
  201. model->y = y;
  202. model->width = width - WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET;
  203. model->height = height;
  204. model->scroll_pos_current = 0;
  205. model->scroll_pos_total = 1;
  206. TextScrollLineArray_init(model->line_array);
  207. string_init_set_str(model->text, text);
  208. WidgetElement* text_scroll = malloc(sizeof(WidgetElement));
  209. text_scroll->parent = NULL;
  210. text_scroll->draw = widget_element_text_scroll_draw;
  211. text_scroll->input = widget_element_text_scroll_input;
  212. text_scroll->free = widget_element_text_scroll_free;
  213. text_scroll->model = model;
  214. text_scroll->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  215. return text_scroll;
  216. }