From 23af1748117653f01e84bda58f433122632315e1 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sat, 27 Apr 2024 13:12:09 +0200 Subject: [PATCH] Implement vertical scrolling for text viewer --- src/menu/components.h | 2 +- src/menu/components/common.c | 10 +-- src/menu/components/constants.h | 12 +-- src/menu/components/file_list.c | 20 ++--- src/menu/views/browser.c | 1 + src/menu/views/error.c | 1 - src/menu/views/text_viewer.c | 150 +++++++++++++++++++++++--------- 7 files changed, 133 insertions(+), 63 deletions(-) diff --git a/src/menu/components.h b/src/menu/components.h index cff0f4c2..4925ee5b 100644 --- a/src/menu/components.h +++ b/src/menu/components.h @@ -24,7 +24,7 @@ void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress) void component_seekbar_draw (float progress); void component_loader_draw (float position); void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items); -void component_file_list_scrollbar_draw (int position, int items, int visible_items); +void component_list_scrollbar_draw (int position, int items, int visible_items); void component_dialog_draw (int width, int height); void component_messagebox_draw (char *fmt, ...); void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...); diff --git a/src/menu/components/common.c b/src/menu/components/common.c index 66db56eb..6bf708f8 100644 --- a/src/menu/components/common.c +++ b/src/menu/components/common.c @@ -80,12 +80,12 @@ void component_scrollbar_draw (int x, int y, int width, int height, int position } } -void component_file_list_scrollbar_draw (int position, int items, int visible_items) { +void component_list_scrollbar_draw (int position, int items, int visible_items) { component_scrollbar_draw( - FILE_LIST_SCROLLBAR_X, - FILE_LIST_SCROLLBAR_Y, - FILE_LIST_SCROLLBAR_WIDTH, - FILE_LIST_SCROLLBAR_HEIGHT, + LIST_SCROLLBAR_X, + LIST_SCROLLBAR_Y, + LIST_SCROLLBAR_WIDTH, + LIST_SCROLLBAR_HEIGHT, position, items, visible_items diff --git a/src/menu/components/constants.h b/src/menu/components/constants.h index 7522d461..f3eb3e39 100644 --- a/src/menu/components/constants.h +++ b/src/menu/components/constants.h @@ -77,19 +77,19 @@ #define BOXART_Y (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT - 24) /** @brief The scroll bar width. */ -#define FILE_LIST_SCROLLBAR_WIDTH (12) +#define LIST_SCROLLBAR_WIDTH (12) /** @brief The scroll bar height. */ -#define FILE_LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT) +#define LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT) /** @brief The scroll bar position on the X axis. */ -#define FILE_LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - FILE_LIST_SCROLLBAR_WIDTH) +#define LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - LIST_SCROLLBAR_WIDTH) /** @brief The scroll bar position on the Y axis. */ -#define FILE_LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) +#define LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) /** @brief The maximum amount of file list entries. */ -#define FILE_LIST_ENTRIES (20) +#define LIST_ENTRIES (20) /** @brief The maximum width available for a file list entry. */ #define FILE_LIST_MAX_WIDTH (480) -#define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - FILE_LIST_SCROLLBAR_WIDTH) +#define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - LIST_SCROLLBAR_WIDTH) #define FILE_LIST_HIGHLIGHT_X (VISIBLE_AREA_X0) /** @brief The default background colour. */ diff --git a/src/menu/components/file_list.c b/src/menu/components/file_list.c index 300b46ff..b00ef329 100644 --- a/src/menu/components/file_list.c +++ b/src/menu/components/file_list.c @@ -28,14 +28,14 @@ static int format_file_size (char *buffer, int64_t size) { void component_file_list_draw (entry_t *list, int entries, int selected) { int starting_position = 0; - if (entries > FILE_LIST_ENTRIES && selected >= (FILE_LIST_ENTRIES / 2)) { - starting_position = selected - (FILE_LIST_ENTRIES / 2); - if (starting_position >= entries - FILE_LIST_ENTRIES) { - starting_position = entries - FILE_LIST_ENTRIES; + if (entries > LIST_ENTRIES && selected >= (LIST_ENTRIES / 2)) { + starting_position = selected - (LIST_ENTRIES / 2); + if (starting_position >= entries - LIST_ENTRIES) { + starting_position = entries - LIST_ENTRIES; } } - component_file_list_scrollbar_draw(selected, entries, FILE_LIST_ENTRIES); + component_list_scrollbar_draw(selected, entries, LIST_ENTRIES); if (entries == 0) { component_main_text_draw( @@ -47,10 +47,10 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { rdpq_paragraph_t *file_list_layout; rdpq_paragraph_t *layout; - size_t name_lengths[FILE_LIST_ENTRIES]; + size_t name_lengths[LIST_ENTRIES]; size_t total_length = 1; - for (int i = 0; i < FILE_LIST_ENTRIES; i++) { + for (int i = 0; i < LIST_ENTRIES; i++) { int entry_index = starting_position + i; if (entry_index >= entries) { @@ -76,7 +76,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { file_list_layout ); - for (int i = 0; i < FILE_LIST_ENTRIES; i++) { + for (int i = 0; i < LIST_ENTRIES; i++) { int entry_index = starting_position + i; entry_t *entry = &list[entry_index]; @@ -134,7 +134,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { rdpq_paragraph_builder_begin( &(rdpq_textparms_t) { - .width = VISIBLE_AREA_WIDTH - FILE_LIST_SCROLLBAR_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), + .width = VISIBLE_AREA_WIDTH - LIST_SCROLLBAR_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), .height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2), .align = ALIGN_RIGHT, .wrap = WRAP_ELLIPSES, @@ -152,7 +152,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { rdpq_paragraph_builder_span(file_size, format_file_size(file_size, entry->size)); } - if ((i + 1) == (starting_position + FILE_LIST_ENTRIES)) { + if ((i + 1) == (starting_position + LIST_ENTRIES)) { break; } diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index b5c46e0d..c8481f4a 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -230,6 +230,7 @@ static void delete_entry (menu_t *menu, void *arg) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); if (remove(path_get(path))) { + menu->browser.valid = false; if (menu->browser.entry->type == ENTRY_TYPE_DIR) { menu_show_error(menu, "Couldn't delete directory\nDirectory might not be empty"); } else { diff --git a/src/menu/views/error.c b/src/menu/views/error.c index 8e565a55..a5c0b84a 100644 --- a/src/menu/views/error.c +++ b/src/menu/views/error.c @@ -50,5 +50,4 @@ void view_error_display (menu_t *menu, surface_t *display) { void menu_show_error (menu_t *menu, char *error_message) { menu->next_mode = MENU_MODE_ERROR; menu->error_message = error_message; - menu->browser.valid = false; } diff --git a/src/menu/views/text_viewer.c b/src/menu/views/text_viewer.c index 277eeba5..6b688479 100644 --- a/src/menu/views/text_viewer.c +++ b/src/menu/views/text_viewer.c @@ -1,17 +1,66 @@ #include #include -#include "utils/fs.h" +#include "../components/constants.h" +#include "../fonts.h" #include "utils/utils.h" #include "views.h" -static char *text; +#define MAX_FILE_SIZE KiB(128) + + +typedef struct { + FILE *f; + char *contents; + size_t length; + int lines; + int current_line; + int offset; + bool vertical_scroll_possible; +} text_file_t; + +static text_file_t *text; + + +static void perform_vertical_scroll (int lines) { + if (!text->vertical_scroll_possible) { + return; + } + + int direction = (lines < 0) ? -1 : 1; + int next_offset = text->offset; + + for (int i = 0; i < abs(lines); i++) { + while (true) { + next_offset += direction; + if (next_offset <= 0) { + text->current_line = 0; + text->offset = 0; + return; + } + if (next_offset > text->length) { + return; + } + if (text->contents[next_offset - 1] == '\n') { + break; + } + } + text->current_line += direction; + text->offset = next_offset; + } +} static void process (menu_t *menu) { if (menu->actions.back) { menu->next_mode = MENU_MODE_BROWSER; + } else if (text) { + if (menu->actions.go_up) { + perform_vertical_scroll(menu->actions.go_fast ? -10 : -1); + } else if (menu->actions.go_down) { + perform_vertical_scroll(menu->actions.go_fast ? 10 : 1); + } } } @@ -22,20 +71,19 @@ static void draw (menu_t *menu, surface_t *d) { component_layout_draw(); - if (text) { - component_main_text_draw( - ALIGN_LEFT, VALIGN_TOP, - "%s\n", - text - ); - } else { - component_messagebox_draw("Text file is empty"); - } + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "%s\n", + text->contents + text->offset + ); + + component_list_scrollbar_draw(text->current_line, text->lines, LIST_ENTRIES); component_actions_bar_text_draw( ALIGN_LEFT, VALIGN_TOP, - "\n" - "B: Back" + "^%02XUp / Down: Scroll^00\n" + "B: Back", + text->vertical_scroll_possible ? STL_DEFAULT : STL_GRAY ); rdpq_detach_show(); @@ -43,6 +91,12 @@ static void draw (menu_t *menu, surface_t *d) { static void deinit (void) { if (text) { + if (text->f) { + fclose(text->f); + } + if (text->contents) { + free(text->contents); + } free(text); text = NULL; } @@ -50,45 +104,61 @@ static void deinit (void) { void view_text_viewer_init (menu_t *menu) { - path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - - FILE *f; - - if ((f = fopen(path_get(path), "r")) == NULL) { - path_free(path); - menu_show_error(menu, "Couldn't open text file"); - return; + if ((text = calloc(1, sizeof(text_file_t))) == NULL) { + return menu_show_error(menu, "Couldn't allocate memory for the text file"); } + path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); + text->f = fopen(path_get(path), "r"); path_free(path); + if (text->f == NULL) { + deinit(); + return menu_show_error(menu, "Couldn't open text file"); + } + struct stat st; - if (fstat(fileno(f), &st)) { - fclose(f); - menu_show_error(menu, "Couldn't get text file size"); - return; + if (fstat(fileno(text->f), &st)) { + deinit(); + return menu_show_error(menu, "Couldn't get text file size"); + } + text->length = st.st_size; + + if (text->length <= 0) { + deinit(); + return menu_show_error(menu, "Text file is empty"); } - // TODO: Implement proper text file viewer with both vertical and horizontal scrolling - size_t size = MIN(st.st_size, 1024); + if (text->length > MAX_FILE_SIZE) { + deinit(); + return menu_show_error(menu, "Text file is too big to be displayed"); + } - if (size) { - if ((text = calloc(sizeof(char), size)) == NULL) { - fclose(f); - menu_show_error(menu, "Couldn't allocate memory for the text file contents"); - return; - } + if ((text->contents = malloc((text->length + 1) * sizeof(char))) == NULL) { + deinit(); + return menu_show_error(menu, "Couldn't allocate memory for the text file contents"); + } - if (fread(text, size, 1, f) != 1) { - fclose(f); - menu_show_error(menu, "Couldn't read text file contents"); - return; + if (fread(text->contents, text->length, 1, text->f) != 1) { + deinit(); + return menu_show_error(menu, "Couldn't read text file contents"); + } + text->contents[text->length] = '\0'; + + if (fclose(text->f)) { + deinit(); + return menu_show_error(menu, "Couldn't close text file"); + } + text->f = NULL; + + text->lines = 1; + for (size_t i = 0; i < text->length; i++) { + if (text->contents[i] == '\n') { + text->lines += 1; } } - if (fclose(f)) { - menu_show_error(menu, "Couldn't close text file"); - } + text->vertical_scroll_possible = (text->lines > LIST_ENTRIES); } void view_text_viewer_display (menu_t *menu, surface_t *display) {