From 70f096abc4c765ad45cfa2997dd25365b5ad5ac0 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Sat, 20 Sep 2025 15:51:55 +0100 Subject: [PATCH] [Add] Bound-checked terminal scrolling. --- client/src/terminal.cpp | 42 +++++++++++++++++++++++++++++++------ client/src/terminal.h | 2 ++ client/src/ui/ui_window.cpp | 11 ++++++++++ client/src/ui/ui_window.h | 1 + 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/client/src/terminal.cpp b/client/src/terminal.cpp index 58ce1f1..b620bdb 100644 --- a/client/src/terminal.cpp +++ b/client/src/terminal.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "gfx/txt_renderer.h" #include "terminal.h" @@ -8,6 +9,7 @@ Terminal::Terminal(void) { /* Placeholder welcome message to history. */ _history.push_back("Welcome to Bettola"); + _scroll_offset = 0; } void Terminal::_on_ret_press(void) { @@ -26,6 +28,15 @@ void Terminal::_on_ret_press(void) { } _input_buffer.clear(); + /* TODO: Ugly hack. Refactor to pass window height + * We need the window height to know if we should + * auto-scroll, but we don't have it here. + * Assume the height of 500 for now. + */ + int visible_lines = (500-5.0f)/20.0f; /* Subtract padding. */ + if((int)_history.size()+1 > visible_lines) { + _scroll_offset = (_history.size()+1) - visible_lines+1; + } } void Terminal::handle_input(SDL_Event* event) { @@ -42,8 +53,27 @@ void Terminal::handle_input(SDL_Event* event) { } } +void Terminal::scroll(int amount, int win_content_height) { + /* amount > 0 = scroll up. amount < 0 = scroll down. */ + _scroll_offset += amount; + + /* Lower bound: Don't scroll below the top of the history. */ + if(_scroll_offset < 0) { + _scroll_offset = 0; + } + /* Upper bound: Don't scroll past the last command. */ + float line_height = 20.0f; + int visible_lines = win_content_height / line_height; + int max_scroll = (_history.size()+1) - visible_lines; + if(max_scroll < 0) max_scroll = 0; + if(_scroll_offset > max_scroll) { + _scroll_offset = max_scroll; + } +} + void Terminal::render(TextRenderer* renderer, int x, int y, int width, int height, bool show_cursor) { + float white[] = { 1.0f, 1.0f, 1.0f }; float green[] = { 0.2f, 1.0f, 0.2f }; float line_height = 20.0f; @@ -53,19 +83,19 @@ void Terminal::render(TextRenderer* renderer, int x, int y, int width, int heigh glEnable(GL_SCISSOR_TEST); glScissor(x, y, width, height); - /* Draw history. */ - for(size_t i = 0; i < _history.size(); ++i) { - float y_pos = (y+height) - padding - line_height - (i*line_height); + /* Draw History. */ + for(size_t i = _scroll_offset; i < _history.size(); ++i) { + float y_pos = (y+height) - padding - line_height - ((i - _scroll_offset) * line_height); renderer->render_text(_history[i].c_str(), x+padding, y_pos, 1.0f, white); } /* Draw current input line. */ - std::string prompt = "> "; - std::string line_to_render = prompt + _input_buffer; + std::string line_to_render = "> " + _input_buffer; if(show_cursor) { line_to_render += "_"; } - float prompt_y_pos = (y+height) - padding - line_height - (_history.size()*line_height); + float prompt_y_pos = (y+height) - padding - line_height + - ((_history.size()-_scroll_offset)*line_height); renderer->render_text(line_to_render.c_str(), x+padding, prompt_y_pos, 1.0f, green); /* Disable scissor test. */ diff --git a/client/src/terminal.h b/client/src/terminal.h index 7c1049b..b68c483 100644 --- a/client/src/terminal.h +++ b/client/src/terminal.h @@ -11,10 +11,12 @@ public: void handle_input(SDL_Event* event); void render(TextRenderer* renderer, int x, int y, int width, int height, bool show_cursor); + void scroll(int amount, int content_height); private: void _on_ret_press(void); std::string _input_buffer; std::vector _history; + int _scroll_offset; }; diff --git a/client/src/ui/ui_window.cpp b/client/src/ui/ui_window.cpp index 294ac88..4ee6dce 100644 --- a/client/src/ui/ui_window.cpp +++ b/client/src/ui/ui_window.cpp @@ -11,6 +11,7 @@ UIWindow::UIWindow(const char* title, int x, int y, int width, int height) { _height = height; _content = nullptr; _is_dragging = false; + _is_hovered = false; _is_focused = false; /* Not focused by default? */ } @@ -88,6 +89,16 @@ void UIWindow::handle_event(SDL_Event* event) { _x = event->motion.x - _drag_offset_x; _y = event->motion.y - _drag_offset_y; } + + /* Check if mouse hovered over window. */ + _is_hovered = is_point_inside(event->motion.x, event->motion.y); + } else if(event->type == SDL_EVENT_MOUSE_WHEEL) { + /* Only scroll window if mouse is hovering. */ + if(_is_hovered && _content) { + /* SDL's wheel motion is negative scroll up on the y. */ + int content_height_gl = _height - title_bar_height; + _content->scroll(-event->wheel.y, content_height_gl); + } } } diff --git a/client/src/ui/ui_window.h b/client/src/ui/ui_window.h index b10a7d1..029b7f3 100644 --- a/client/src/ui/ui_window.h +++ b/client/src/ui/ui_window.h @@ -23,6 +23,7 @@ private: std::string _title; Terminal* _content; bool _is_focused; /* Managed by desktop. */ + bool _is_hovered; /* Send scroll events even if not focused. */ bool _is_dragging; int _drag_offset_x, _drag_offset_y;