From 25db42260484e62258c45d6ec34e8e47d4de1129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Sun, 20 Jun 2021 21:19:15 +0200 Subject: [PATCH] feat: add gui text view component --- text-viewer/include/gui_component_text_view.h | 33 ++++ text-viewer/src/gui_component_text_view.c | 178 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 text-viewer/include/gui_component_text_view.h create mode 100644 text-viewer/src/gui_component_text_view.c diff --git a/text-viewer/include/gui_component_text_view.h b/text-viewer/include/gui_component_text_view.h new file mode 100644 index 0000000000000000000000000000000000000000..b74d06914cdfceee3a32133ce6cad73ee6048751 --- /dev/null +++ b/text-viewer/include/gui_component_text_view.h @@ -0,0 +1,33 @@ +#ifndef __GUI_COMPONENT_TEXT_VIEW_H__ +#define __GUI_COMPONENT_TEXT_VIEW_H__ + +#include "display_utils.h" +#include "gui.h" + +typedef struct { + char *line; + uint16_t length; +} line_t; + +typedef struct { + line_t *lines; + uint16_t lines_count; + char *text; + font_t *font; + display_pixel_t color; +} multiline_text_t; + +multiline_text_t *gui_multiline_text_create(font_t *font, display_pixel_t color, char *text); + +component_t gui_text_view_create(gui_t *gui, multiline_text_t *text, int16_t x, int16_t y); +void gui_text_view_register_commands(gui_t *gui, component_t *text_view); + +void gui_text_view_scroll(component_t *text_view, int16_t x, int16_t y); +void gui_text_view_reset_scroll(component_t *text_view); + +void gui_text_view_render(container_t *container, component_t *component, + gui_t *gui); +void gui_text_view_update(container_t *container, component_t *component, + gui_t *gui); + +#endif // __GUI_COMPONENT_TEXT_VIEW_H__ diff --git a/text-viewer/src/gui_component_text_view.c b/text-viewer/src/gui_component_text_view.c new file mode 100644 index 0000000000000000000000000000000000000000..25de1bec53de744cbd4f841570d96e2022aad96c --- /dev/null +++ b/text-viewer/src/gui_component_text_view.c @@ -0,0 +1,178 @@ +#include "gui_component_text_view.h" +#include "display_utils.h" +#include "input.h" +#include "renderer.h" +#include +#include +#include "direction.h" +#include "keyboard_const.h" +#include "rotation_const.h" + +multiline_text_t *gui_multiline_text_create(font_t *font, display_pixel_t color, char *text) { + size_t len = strlen(text) + 1; + size_t lines = 1; + + for (int i = 0; i < len; i++) { + if (text[i] == '\n') { + lines++; + } + } + + void* new = realloc(text, len + sizeof(multiline_text_t) + sizeof(line_t) * lines); + if (new == NULL) { + free(text); + return NULL; + } + + text = new; + + multiline_text_t *multiline_text = (multiline_text_t*)(new + len); + line_t *line_ptr = (line_t*)(new + len + sizeof(multiline_text_t)); + + multiline_text->font = font; + multiline_text->color = color; + multiline_text->lines = line_ptr; + multiline_text->lines_count = lines; + multiline_text->text = text; + + bool new_line = false; + uint32_t line_index = 0; + uint32_t line_length = 0; + char *line_str_ptr = text; + + for (int i = 0; i < len; i++) { + if (new_line) { + line_ptr[line_index].line = line_str_ptr; + line_ptr[line_index++].length = line_length; + line_str_ptr += line_length + 1; + line_length = 0; + new_line = false; + } + + if (text[i] == '\n') { + text[i] = '\0'; + new_line = true; + } else { + line_length++; + } + } + + if (line_index < lines) { + line_ptr[line_index].line = line_str_ptr; + line_ptr[line_index].length = line_length; + } + + return multiline_text; +} + +static void command_handler_move(void *state, direction_t direction, int amount) { + component_t *text_view = (component_t*)state; + if (text_view->focused) { + direction_move_xy(direction, (int32_t*)&text_view->x, (int32_t*)&text_view->y, -amount); + } +} + +static void command_handler_move_down(void *state, int amount) { + command_handler_move(state, DOWN, amount); +} + +static void command_handler_move_up(void *state, int amount) { + command_handler_move(state, UP, amount); +} + +static void command_handler_move_left(void *state, int amount) { + command_handler_move(state, LEFT, amount); +} +static void command_handler_move_right(void *state, int amount) { + command_handler_move(state, RIGHT, amount); +} +static void command_handler_reset(void *state, int amount) { + gui_text_view_reset_scroll((component_t*)state); +} + +component_t gui_text_view_create(gui_t *gui, multiline_text_t *text, int16_t x, int16_t y) { + component_t text_view = + gui_component_create(x, y, 1, 1, gui_text_view_render, gui_text_view_update); + text_view.state = text; + text_view.focusable = true; + + return text_view; +} + +void gui_text_view_register_commands(gui_t *gui, component_t *text_view) { + commands_register(gui->commands, IN_KEYBOARD, KEYBOARD_LEFT, + command_handler_move_left, text_view); + commands_register(gui->commands, IN_KEYBOARD, KEYBOARD_RIGHT, + command_handler_move_right, text_view); + commands_register(gui->commands, IN_KEYBOARD, KEYBOARD_DOWN, + command_handler_move_down, text_view); + commands_register(gui->commands, IN_KEYBOARD, KEYBOARD_UP, + command_handler_move_up, text_view); + commands_register(gui->commands, IN_KEYBOARD, 'r', + command_handler_reset, text_view); + + commands_register(gui->commands, IN_ENCODER_ROTATE, + ROTATION_ENCODER_HORIZONTAL, command_handler_move_right, + text_view); + + commands_register(gui->commands, IN_ENCODER_ROTATE, + ROTATION_ENCODER_VERTICAL, command_handler_move_down, + text_view); + + commands_register(gui->commands, IN_ENCODER_CLICK, + ROTATION_ENCODER_ZOOM, command_handler_reset, + text_view); +} + +void gui_text_view_scroll(component_t *text_view, int16_t x, int16_t y) { + text_view->x -= x; + text_view->y -= y; +} + +void gui_text_view_reset_scroll(component_t *text_view) { + text_view->x = 0; + text_view->y = 0; +} + +void gui_text_view_render(container_t *container, component_t *component, + gui_t *gui) { + multiline_text_t *text = (multiline_text_t*)component->state; + font_t *font = text->font; + int16_t x_offset = component->x, y_offset = component->y; + + if (x_offset > 0) { + x_offset = 0; + } + + if (y_offset > 0) { + y_offset = 0; + } else if (y_offset < -(font->size + font->line_spacing) * (text->lines_count) + gui->size.y - container->y) { + y_offset = -(font->size + font->line_spacing) * (text->lines_count) + gui->size.y - container->y; + } + + component->x = x_offset; + component->y = y_offset; + + int16_t first_line_index = -y_offset / (font->size + font->line_spacing); + int16_t lines_count = gui->size.y / font->size; + if (first_line_index < 0) { + first_line_index = 0; + } + int16_t last_line_index = lines_count + first_line_index; + + y_offset += first_line_index * (font->size + font->line_spacing); + + for (int i = first_line_index; i < last_line_index && i < text->lines_count; + i++) { + line_t *line = &text->lines[i]; + size2d_t size = renderer_write_string(gui->renderer, x_offset, y_offset, + line->length, font, line->line, text->color); + + y_offset += size.y; + } +} + +void gui_text_view_update(container_t *container, component_t *component, + gui_t *gui) { + // do nothing +}