diff --git a/src/core/PieceTable.cc b/src/core/PieceTable.cc index 91310a5..f74c697 100644 --- a/src/core/PieceTable.cc +++ b/src/core/PieceTable.cc @@ -118,22 +118,27 @@ void PieceTable::Iterator::go_prev_piece() PieceTable::Cursor::Cursor(PieceTable * pt) : iterator(pt) { - line = iterator.valid() ? 1u : 0u; + line = 0u; init_column(); } void PieceTable::Cursor::go_start_of_line() { - while (iterator.valid() && (!iterator.piece->prev->eol())) + while (iterator.valid() && + (!iterator.piece->prev->eol()) && + (iterator.piece->prev != iterator.piece_table->start_piece)) { iterator.go_prev_piece(); } + iterator.offset = 0u; init_column(); } void PieceTable::Cursor::go_end_of_line() { - while (iterator.valid() && (!iterator.piece->eol())) + while (iterator.valid() && + (!iterator.piece->eol()) && + (iterator.piece->next != iterator.piece_table->end_piece)) { iterator.go_next_piece(); } @@ -201,8 +206,8 @@ bool PieceTable::Cursor::go_right(int n) uint32_t chr = *iterator; if (chr == '\t') { - column += iterator.piece_table->tabstop; - column -= column % iterator.piece_table->tabstop; + uint8_t tabstop = iterator.piece_table->tabstop; + column += tabstop - (column + 1u) % tabstop; } else { @@ -216,14 +221,15 @@ bool PieceTable::Cursor::is_start_of_line() { return iterator.valid() && (iterator.offset == 0u) && - (iterator.piece->prev->eol()); + ((iterator.piece->prev == iterator.piece_table->start_piece) || + iterator.piece->prev->eol()); } bool PieceTable::Cursor::is_end_of_line() { return iterator.valid() && ((iterator.offset + iterator.num_bytes_in_code_point()) >= iterator.piece->length) && - iterator.piece->eol(); + (iterator.piece->eol() || (iterator.piece->next == iterator.piece_table->end_piece)); } PieceTable::Piece * PieceTable::Cursor::prev_line() const @@ -268,7 +274,7 @@ void PieceTable::Cursor::init_column() { uint32_t chr = *iterator; /* TODO: handle multi-column characters */ - column = (chr == 0xFFFFFFFFu) ? 0u : (chr == '\t') ? iterator.piece_table->tabstop : 1u; + column = (chr == '\t') ? iterator.piece_table->tabstop - 1u : 0u; } void PieceTable::Cursor::calculate_column() @@ -286,6 +292,8 @@ void PieceTable::Cursor::calculate_column() void PieceTable::Cursor::forward_to_column(uint32_t c) { int32_t diff = abs((int32_t)(c - column)); + if (diff == 0) + return; while (go_right(1)) { int32_t new_diff = abs((int32_t)(c - column)); diff --git a/src/core/PieceTable.h b/src/core/PieceTable.h index d4f0a37..9b7c092 100644 --- a/src/core/PieceTable.h +++ b/src/core/PieceTable.h @@ -116,6 +116,10 @@ public: bool go_down(int n, uint32_t desired_column); bool go_left(int n); bool go_right(int n); + bool operator==(const Cursor & c) + { + return (c.line == line) && (c.column == column); + } protected: bool is_start_of_line(); diff --git a/src/gui/Window.cc b/src/gui/Window.cc index 01b7db3..48afc24 100644 --- a/src/gui/Window.cc +++ b/src/gui/Window.cc @@ -133,6 +133,7 @@ bool Window::create(std::shared_ptr buffer) m_buffer = buffer; m_cursor = m_buffer->piece_table->add_cursor(); m_start_piece = m_buffer->piece_table->start_piece->next; + m_cursor_row = 0; resize(); @@ -232,15 +233,45 @@ void Window::handle_key(uint32_t scancode, uint32_t mod) case SDL_SCANCODE_ESCAPE: m_exit_requested = true; break; + case SDL_SCANCODE_H: + cursor_left(); + break; case SDL_SCANCODE_J: - scroll_down(); + cursor_down(); break; case SDL_SCANCODE_K: - scroll_up(); + cursor_up(); + break; + case SDL_SCANCODE_L: + cursor_right(); break; } } +void Window::cursor_left() +{ + m_cursor->go_left(1); + redraw(); +} + +void Window::cursor_right() +{ + m_cursor->go_right(1); + redraw(); +} + +void Window::cursor_up() +{ + m_cursor->go_up(1, m_cursor->column); + redraw(); +} + +void Window::cursor_down() +{ + m_cursor->go_down(1, m_cursor->column); + redraw(); +} + void Window::scroll_down() { if ((m_start_piece != m_buffer->piece_table->end_piece) && @@ -261,6 +292,71 @@ void Window::scroll_up() } } +std::pair Window::calculate_start_position() +{ + m_cursor_row = std::min(m_cursor_row, m_rows - 1); + int row = m_cursor_row - m_cursor->column / m_columns; + PieceTable::Cursor cursor = *m_cursor; + cursor.go_start_of_line(); + while (row > 0) + { + if (cursor.go_up(1, 0u)) + { + PieceTable::Cursor c = cursor; + c.go_end_of_line(); + row -= c.column / m_columns + 1; + } + else + { + m_cursor_row -= row; + row = 0; + } + } + return std::pair(row, cursor); +} + +void Window::draw_text() +{ + auto start_position = calculate_start_position(); + int row = start_position.first; + PieceTable::Cursor cursor = start_position.second; + + for (;;) + { + int row_offset = cursor.column / m_columns; + int screen_row = row + row_offset; + if (screen_row >= m_rows) + break; + int screen_column = cursor.column % m_columns; + if (cursor == *m_cursor) + draw_cursor(screen_column, screen_row); + if (screen_row >= 0) + { + uint32_t character = *cursor; + if (character != 0xFFFFFFFFu) + draw_character(screen_column, screen_row, character); + } + if (!cursor.go_right(1)) + { + if (!cursor.go_down(1, 0)) + break; + row = screen_row + 1; + } + } +} + +void Window::draw_character(int screen_column, int screen_row, uint32_t character) +{ + m_shaders.text->use(); + int advance = m_font.get_advance(); + int line_height = m_font.get_line_height(); + int x = screen_column * advance; + int y = m_height - (screen_row + 1) * line_height; + auto g = m_font.get_glyph(character); + m_shaders.text->set_position(x, y + m_font.get_baseline_offset()); + g->render(); +} + void Window::resize() { SDL_GetWindowSize(m_window, &m_width, &m_height); @@ -280,52 +376,20 @@ void Window::redraw() glClearColor (0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); - draw_cursor(); - m_shaders.text->use(); - - int advance = m_font.get_advance(); - int line_height = m_font.get_line_height(); - int x = 0; - int y = m_height - line_height; - for (PieceTable::Piece * piece = m_start_piece; - piece != m_buffer->piece_table->end_piece; - piece = piece->next) - { - for (int i = 0, len = piece->length; i < len; i++) - { - uint8_t c = piece->start[i]; - auto g = m_font.get_glyph(c); - m_shaders.text->set_position(x, y + m_font.get_baseline_offset()); - g->render(); - if ((i + 1) % m_columns == 0) - { - y -= line_height; - x = 0; - } - else - { - x += advance; - } - } - if (piece->eol()) - { - y -= line_height; - x = 0; - } - if (y + line_height < 0) - { - break; - } - } + draw_text(); SDL_GL_SwapWindow(m_window); } -void Window::draw_cursor() +void Window::draw_cursor(int screen_column, int screen_row) { + int advance = m_font.get_advance(); + int line_height = m_font.get_line_height(); + int x = screen_column * advance; + int y = m_height - (screen_row + 1) * line_height; m_cursor_array->bind(); m_shaders.flat->use(); m_shaders.flat->set_color(1.0, 0.2, 1.0, 1.0); - m_shaders.flat->set_position(0, m_height - m_font.get_line_height()); + m_shaders.flat->set_position(x, y); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } diff --git a/src/gui/Window.h b/src/gui/Window.h index 50cbc18..1e1bf4f 100644 --- a/src/gui/Window.h +++ b/src/gui/Window.h @@ -1,6 +1,7 @@ #ifndef WINDOW_H #define WINDOW_H +#include #include #include "TextShader.h" #include "FlatShader.h" @@ -17,11 +18,18 @@ public: protected: void resize(); void redraw(); - void draw_cursor(); + void draw_cursor(int screen_column, int screen_row); void handle_event(SDL_Event & event); void handle_key(uint32_t scancode, uint32_t mod); + void cursor_left(); + void cursor_right(); + void cursor_up(); + void cursor_down(); void scroll_down(); void scroll_up(); + std::pair calculate_start_position(); + void draw_text(); + void draw_character(int screen_column, int screen_row, uint32_t character); SDL_Window * m_window; bool m_exit_requested; @@ -36,6 +44,7 @@ protected: Font m_font; int m_columns; int m_rows; + int m_cursor_row; std::shared_ptr m_buffer;