Implement vertical scrolling for text viewer

This commit is contained in:
Mateusz Faderewski 2024-04-27 13:12:09 +02:00
parent a189e139b2
commit 23af174811
7 changed files with 133 additions and 63 deletions

View File

@ -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, ...);

View File

@ -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

View File

@ -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. */

View File

@ -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;
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -1,17 +1,66 @@
#include <stdio.h>
#include <sys/stat.h>
#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) {