From cac24ffa13dc969244fb171b9aad565311c69f16 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Fri, 27 Nov 2020 09:40:03 -0500 Subject: scrolling working --- buffer.c | 115 +++++++++++++++++++++++++++++++++++++++++++++------------------ main.c | 31 ++++++++--------- util.c | 6 ++-- 3 files changed, 101 insertions(+), 51 deletions(-) diff --git a/buffer.c b/buffer.c index f98e3f3..f06ca00 100644 --- a/buffer.c +++ b/buffer.c @@ -21,16 +21,17 @@ typedef struct { } TextBlock; typedef struct { - u32 nblocks; // number of text blocks double scroll_x, scroll_y; // number of characters scrolled in the x/y direction - TextBlock *blocks; Font *font; + u8 tab_width; float x1, y1, x2, y2; + u32 nblocks; // number of text blocks + TextBlock *blocks; } TextBuffer; // Returns a new block added at index `where`, // or NULL if there's not enough memory. -static TextBlock *text_buffer_add_block(TextBuffer *buffer, u32 where) { +static TextBlock *buffer_add_block(TextBuffer *buffer, u32 where) { // make sure we can actually allocate enough memory for the contents of the block // before doing anything char *block_contents = calloc(1, TEXT_BLOCK_MAX_SIZE); @@ -63,12 +64,13 @@ static TextBlock *text_buffer_add_block(TextBuffer *buffer, u32 where) { return block; } -void text_buffer_create(TextBuffer *buffer, Font *font) { +void buffer_create(TextBuffer *buffer, Font *font) { util_zero_memory(buffer, sizeof *buffer); buffer->font = font; + buffer->tab_width = 4; } -Status text_buffer_load_file(TextBuffer *buffer, FILE *fp) { +Status buffer_load_file(TextBuffer *buffer, FILE *fp) { char block_contents[TEXT_BLOCK_DEFAULT_SIZE]; size_t bytes_read; // @TODO: @TODO: IMPORTANT! make this work if buffer already has a file @@ -77,7 +79,7 @@ Status text_buffer_load_file(TextBuffer *buffer, FILE *fp) { do { bytes_read = fread(block_contents, 1, sizeof block_contents, fp); if (bytes_read > 0) { - TextBlock *block = text_buffer_add_block(buffer, buffer->nblocks); + TextBlock *block = buffer_add_block(buffer, buffer->nblocks); if (!block) { return false; } @@ -90,8 +92,8 @@ Status text_buffer_load_file(TextBuffer *buffer, FILE *fp) { return true; } -static void text_buffer_print_internal(TextBuffer *buffer, bool debug) { - printf("\033[2J\033[;H"); +static void buffer_print_internal(TextBuffer *buffer, bool debug) { + printf("\033[2J\033[;H"); // clear terminal screen TextBlock *blocks = buffer->blocks; u32 nblocks = buffer->nblocks; @@ -105,17 +107,17 @@ static void text_buffer_print_internal(TextBuffer *buffer, bool debug) { } // print the contents of a buffer to stdout -void text_buffer_print(TextBuffer *buffer) { - text_buffer_print_internal(buffer, false); +void buffer_print(TextBuffer *buffer) { + buffer_print_internal(buffer, false); } // print the contents of a buffer to stdout, along with debugging information -void text_buffer_print_debug(TextBuffer *buffer) { - text_buffer_print_internal(buffer, true); +void buffer_print_debug(TextBuffer *buffer) { + buffer_print_internal(buffer, true); } // Does not free the pointer `buffer` (buffer might not have even been allocated with malloc) -void text_buffer_free(TextBuffer *buffer) { +void buffer_free(TextBuffer *buffer) { TextBlock *blocks = buffer->blocks; u32 nblocks = buffer->nblocks; for (u32 i = 0; i < nblocks; ++i) { @@ -124,30 +126,87 @@ void text_buffer_free(TextBuffer *buffer) { free(blocks); } +// returns the number of lines of text in the buffer into *lines (if not NULL), +// and the number of columns of text, i.e. the length of the longest line, into *cols (if not NULL) +static void buffer_text_dimensions(TextBuffer *buffer, u64 *lines, u64 *cols) { + u64 nlines = 1; + u64 maxcol = 0; + u64 col = 0; + for (u32 i = 0; i < buffer->nblocks; ++i) { + TextBlock *block = &buffer->blocks[i]; + for (char *p = block->contents, *end = p + block->len; p != end; ++p) { + switch (*p) { + case '\n': + ++nlines; + col = 0; + break; + case '\r': break; + case '\t': + do + ++col; + while (col % buffer->tab_width); + break; + default: + ++col; + break; + } + if (col > maxcol) + maxcol = col; + } + } + if (lines) *lines = nlines; + if (cols) *cols = maxcol; +} + + +// returns the number of rows of text that can fit in the buffer, rounded down. +int buffer_display_rows(TextBuffer *buffer) { + return (int)((buffer->y2 - buffer->y1) / text_font_char_height(buffer->font)); +} + +// returns the number of columns of text that can fit in the buffer, rounded down. +int buffer_display_cols(TextBuffer *buffer) { + return (int)((buffer->x2 - buffer->x1) / text_font_char_width(buffer->font)); +} // make sure we don't scroll too far -static void text_buffer_correct_scroll(TextBuffer *buffer) { +static void buffer_correct_scroll(TextBuffer *buffer) { if (buffer->scroll_x < 0) buffer->scroll_x = 0; if (buffer->scroll_y < 0) buffer->scroll_y = 0; - // @TODO: maximum scroll_x and scroll_y + u64 nlines, ncols; + buffer_text_dimensions(buffer, &nlines, &ncols); + double max_scroll_x = (double)ncols - buffer_display_cols(buffer); + double max_scroll_y = (double)nlines - buffer_display_rows(buffer); + if (max_scroll_x <= 0) { + buffer->scroll_x = 0; + } else if (buffer->scroll_x > max_scroll_x) { + buffer->scroll_x = max_scroll_x; + } + + if (max_scroll_y <= 0) { + buffer->scroll_y = 0; + } else if (buffer->scroll_y > max_scroll_y) { + buffer->scroll_y = max_scroll_y; + } } -void text_buffer_scroll(TextBuffer *buffer, double dx, double dy) { +void buffer_scroll(TextBuffer *buffer, double dx, double dy) { buffer->scroll_x += dx; buffer->scroll_y += dy; - text_buffer_correct_scroll(buffer); + buffer_correct_scroll(buffer); } // Render the text buffer in the given rectangle // NOTE: also corrects scroll -void text_buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { +void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { buffer->x1 = x1; buffer->y1 = y1; buffer->x2 = x2; buffer->y2 = y2; Font *font = buffer->font; mbstate_t mbstate = {0}; - uint nblocks = buffer->nblocks; + u32 nblocks = buffer->nblocks; TextBlock *blocks = buffer->blocks; - float char_height = text_font_char_height(font); + float char_width = text_font_char_width(font), + char_height = text_font_char_height(font); glColor3f(0.5f,0.5f,0.5f); glBegin(GL_LINE_STRIP); glVertex2f(x1,y1); @@ -161,7 +220,7 @@ void text_buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float text_chars_begin(font); // what x coordinate to start rendering the text from - float render_start_x = x1 - (float)buffer->scroll_x * char_height; + float render_start_x = x1 - (float)buffer->scroll_x * char_width; u32 column = 0; @@ -174,7 +233,7 @@ void text_buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float // @TODO: make this better (we should figure out where to start rendering, etc.) text_state.y -= (float)buffer->scroll_y * char_height; - for (uint block_idx = 0; block_idx < nblocks; ++block_idx) { + for (u32 block_idx = 0; block_idx < nblocks; ++block_idx) { TextBlock *block = &blocks[block_idx]; char *p = block->contents, *end = p + block->len; while (p != end) { @@ -208,7 +267,7 @@ void text_buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float do { text_render_char(font, L' ', &text_state); ++column; - } while (column % 4); + } while (column % buffer->tab_width); break; default: text_render_char(font, c, &text_state); @@ -219,13 +278,3 @@ void text_buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float } text_chars_end(font); } - -// returns the number of rows of text that can fit in the buffer, rounded down. -int text_buffer_num_rows(TextBuffer *buffer) { - return (int)((buffer->y2 - buffer->y1) / text_font_char_height(buffer->font)); -} - -// returns the number of columns of text that can fit in the buffer, rounded down. -int text_buffer_num_cols(TextBuffer *buffer) { - return (int)((buffer->x2 - buffer->x1) / text_font_char_width(buffer->font)); -} diff --git a/main.c b/main.c index 6831229..de72bbd 100644 --- a/main.c +++ b/main.c @@ -35,7 +35,7 @@ INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, int main(void) { #endif setlocale(LC_ALL, ""); // allow unicode - SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); // if this program is sent a SIGTERM/SIGINT, don't turn it into a quit event + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); // if this program is sent a SIGTERM/SIGINT, don't turn it into a quit event if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) die("%s", SDL_GetError()); @@ -66,12 +66,12 @@ int main(void) { bool quit = false; TextBuffer text_buffer; - text_buffer_create(&text_buffer, font); + buffer_create(&text_buffer, font); { - FILE *fp = fopen("test.txt", "r"); + FILE *fp = fopen("main.c", "r"); assert(fp); - bool success = text_buffer_load_file(&text_buffer, fp); + bool success = buffer_load_file(&text_buffer, fp); fclose(fp); if (!success) die("Error loading file."); @@ -94,15 +94,15 @@ int main(void) { // scroll with mouse wheel Sint32 dx = event.wheel.x, dy = -event.wheel.y; double scroll_speed = 2.5; - text_buffer_scroll(&text_buffer, dx * scroll_speed, dy * scroll_speed); + buffer_scroll(&text_buffer, dx * scroll_speed, dy * scroll_speed); } break; case SDL_KEYDOWN: { switch (event.key.keysym.sym) { case SDLK_PAGEUP: - text_buffer_scroll(&text_buffer, 0, -text_buffer_num_rows(&text_buffer)); + buffer_scroll(&text_buffer, 0, -buffer_display_rows(&text_buffer)); break; case SDLK_PAGEDOWN: - text_buffer_scroll(&text_buffer, 0, +text_buffer_num_rows(&text_buffer)); + buffer_scroll(&text_buffer, 0, +buffer_display_rows(&text_buffer)); break; } } break; @@ -122,15 +122,16 @@ int main(void) { if (control_key_down) { // control + arrow keys to scroll double scroll_speed = 20.0; - double scroll_amount = scroll_speed * frame_dt; + double scroll_amount_x = scroll_speed * frame_dt * 1.5; // characters are taller than they are wide + double scroll_amount_y = scroll_speed * frame_dt; if (keyboard_state[SDL_SCANCODE_UP]) - text_buffer_scroll(&text_buffer, 0, -scroll_amount); + buffer_scroll(&text_buffer, 0, -scroll_amount_y); if (keyboard_state[SDL_SCANCODE_DOWN]) - text_buffer_scroll(&text_buffer, 0, +scroll_amount); + buffer_scroll(&text_buffer, 0, +scroll_amount_y); if (keyboard_state[SDL_SCANCODE_LEFT]) - text_buffer_scroll(&text_buffer, -scroll_amount, 0); + buffer_scroll(&text_buffer, -scroll_amount_x, 0); if (keyboard_state[SDL_SCANCODE_RIGHT]) - text_buffer_scroll(&text_buffer, +scroll_amount, 0); + buffer_scroll(&text_buffer, +scroll_amount_x, 0); } @@ -151,14 +152,14 @@ int main(void) { { float x1 = 50, y1 = 50, x2 = window_widthf-50, y2 = window_heightf-50; - text_buffer_render(&text_buffer, x1, y1, x2, y2); + buffer_render(&text_buffer, x1, y1, x2, y2); if (text_has_err()) { printf("Text error: %s\n", text_get_err()); break; } } - //text_buffer_print_debug(&text_buffer); + //buffer_print_debug(&text_buffer); SDL_GL_SwapWindow(window); } @@ -166,7 +167,7 @@ int main(void) { SDL_GL_DeleteContext(glctx); SDL_DestroyWindow(window); SDL_Quit(); - text_buffer_free(&text_buffer); + buffer_free(&text_buffer); text_font_free(font); return 0; diff --git a/util.c b/util.c index 6f252c6..c1446a8 100644 --- a/util.c +++ b/util.c @@ -3,11 +3,11 @@ #include "base.h" -static uint util_popcount(u64 x) { +static u32 util_popcount(u64 x) { #ifdef __GNUC__ - return (uint)__builtin_popcountll(x); + return (u32)__builtin_popcountll(x); #else - uint count = 0; + u32 count = 0; while (x) { x &= x-1; ++count; -- cgit v1.2.3