[Refactor] Abstract text editing into reusable components.
This commit is contained in:
parent
43907509eb
commit
4a6d33292a
@ -1,4 +1,5 @@
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <GL/glew.h>
|
||||
#include <SDL3/SDL_events.h>
|
||||
@ -8,11 +9,10 @@
|
||||
#include "gfx/txt_renderer.h"
|
||||
#include "gfx/types.h"
|
||||
|
||||
Terminal::Terminal(ClientNetwork* network) : _network(network) {
|
||||
/* Placeholder welcome message to history. */
|
||||
_should_close = false;
|
||||
_scroll_offset = 0;
|
||||
_prompt = "";
|
||||
Terminal::Terminal(ClientNetwork* network)
|
||||
: _network(network), _should_close(false), _scroll_offset(0),
|
||||
_prompt("") {
|
||||
_input_view = std::make_unique<TextView>(&_input_buffer);
|
||||
}
|
||||
|
||||
Terminal::~Terminal(void) {}
|
||||
@ -45,8 +45,8 @@ void Terminal::set_prompt(const std::string& prompt) {
|
||||
bool Terminal::close(void) { return _should_close; }
|
||||
|
||||
void Terminal::_on_ret_press(void) {
|
||||
std::string command = _input_buffer;
|
||||
_input_buffer.clear(); /* Clear the input buffer immediately. */
|
||||
std::string command = _input_buffer.get_line(0);
|
||||
_input_buffer.clear();
|
||||
|
||||
/* Add the command to history. */
|
||||
_history.push_back(_prompt + "> " + command);
|
||||
@ -70,18 +70,11 @@ void Terminal::_on_ret_press(void) {
|
||||
}
|
||||
|
||||
void Terminal::handle_input(SDL_Event* event) {
|
||||
if(event->type == SDL_EVENT_TEXT_INPUT) {
|
||||
/* Append chars to the input buffer. */
|
||||
_input_buffer += event->text.text;
|
||||
} else if(event->type == SDL_EVENT_KEY_DOWN) {
|
||||
/* Handle special keys. */
|
||||
if(event->key.key == SDLK_BACKSPACE && _input_buffer.length() > 0) {
|
||||
_input_buffer.pop_back();
|
||||
} else if(event->key.key == SDLK_RETURN) {
|
||||
/* Pass input to TextView; if true, RET was pressed. */
|
||||
if(_input_view->handle_event(event)) {
|
||||
_on_ret_press();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::scroll(int amount, int win_content_height) {
|
||||
/* amount > 0 = scroll up. amount < 0 = scroll down. */
|
||||
@ -120,14 +113,16 @@ void Terminal::render(TextRenderer* renderer, int x, int y, int width, int heigh
|
||||
}
|
||||
|
||||
/* Draw current input line. */
|
||||
std::string line_to_render = _prompt + "> " + _input_buffer;
|
||||
if(show_cursor) {
|
||||
line_to_render += "_";
|
||||
}
|
||||
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);
|
||||
|
||||
/* Render prompt string. */
|
||||
std::string prompt_str = _prompt + "> ";
|
||||
renderer->render_text(prompt_str.c_str(), x + padding, prompt_y_pos, 1.0f, green);
|
||||
|
||||
/* Render text view for the input right after prompt. */
|
||||
float input_x_pos = x + padding + (prompt_str.length() * 8.5f); /* Estimate width */
|
||||
_input_view->render(renderer, input_x_pos, prompt_y_pos, show_cursor);
|
||||
/* Disable scissor test. */
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "gfx/txt_renderer.h"
|
||||
#include "client_network.h"
|
||||
#include "ui/text_buffer.h"
|
||||
#include "ui/text_view.h"
|
||||
|
||||
class Terminal {
|
||||
public:
|
||||
@ -22,10 +25,11 @@ public:
|
||||
private:
|
||||
void _on_ret_press(void);
|
||||
|
||||
std::string _input_buffer;
|
||||
bool _should_close;
|
||||
std::vector<std::string> _history;
|
||||
int _scroll_offset;
|
||||
std::string _prompt;
|
||||
ClientNetwork* _network;
|
||||
TextBuffer _input_buffer;
|
||||
std::unique_ptr<TextView> _input_view;
|
||||
};
|
||||
|
||||
77
client/src/ui/text_view.cpp
Normal file
77
client/src/ui/text_view.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include "text_view.h"
|
||||
#include "gfx/txt_renderer.h"
|
||||
#include "gfx/types.h"
|
||||
#include "ui/text_buffer.h"
|
||||
|
||||
TextView::TextView(TextBuffer* buffer) : _buffer(buffer) {}
|
||||
|
||||
TextView::~TextView(void) {}
|
||||
|
||||
bool TextView::handle_event(SDL_Event* event) {
|
||||
if(!_buffer) return false;
|
||||
|
||||
if(event->type == SDL_EVENT_TEXT_INPUT) {
|
||||
_buffer->insert_char(event->text.text[0]);
|
||||
} else if(event->type == SDL_EVENT_KEY_DOWN) {
|
||||
switch(event->key.key) {
|
||||
case SDLK_BACKSPACE:
|
||||
_buffer->backspace();
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
/* Instead of adding newline, signal that input was submitted. */
|
||||
return true;
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
_buffer->move_cursor(-1,0);
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
_buffer->move_cursor(1,0);
|
||||
break;
|
||||
case SDLK_UP:
|
||||
_buffer->move_cursor(0,-1);
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
_buffer->move_cursor(0,1);
|
||||
break;
|
||||
case SDLK_HOME:
|
||||
_buffer->move_cursor_home();
|
||||
break;
|
||||
case SDLK_END:
|
||||
_buffer->move_cursor_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextView::render(TextRenderer* renderer, int x, int y, bool show_cursor) {
|
||||
if(!_buffer) return;
|
||||
|
||||
const Color text_color = { 1.0f, 1.0f, 1.0f };
|
||||
float line_height = 20.0f; /* TODO: Get font metrics? */
|
||||
|
||||
/*
|
||||
* Just render first line (for term input) for now.
|
||||
* The code editor would need to iterate through _buffer->get_line_count()
|
||||
*/
|
||||
std::string line = _buffer->get_line(0);
|
||||
Point cursor_pos = _buffer->get_cursor_pos();
|
||||
|
||||
if(show_cursor) {
|
||||
/*
|
||||
* This hacky. we should calculate the text width
|
||||
* up to the cursor pos to draw correctly.
|
||||
* For now, just append an underscore.
|
||||
*/
|
||||
if(cursor_pos.col == line.length()) {
|
||||
line += "_";
|
||||
} else {
|
||||
line.insert(cursor_pos.col, 1, '_');
|
||||
}
|
||||
}
|
||||
|
||||
renderer->render_text(line.c_str(), x, y, 1.0f, text_color);
|
||||
}
|
||||
19
client/src/ui/text_view.h
Normal file
19
client/src/ui/text_view.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
|
||||
#include "ui/text_buffer.h"
|
||||
|
||||
class TextRenderer;
|
||||
|
||||
class TextView {
|
||||
public:
|
||||
TextView(TextBuffer* buffer);
|
||||
~TextView(void);
|
||||
|
||||
bool handle_event(SDL_Event* event);
|
||||
void render(TextRenderer* renderer, int x, int y, bool show_cursor);
|
||||
|
||||
private:
|
||||
TextBuffer* _buffer;
|
||||
};
|
||||
86
common/src/ui/text_buffer.cpp
Normal file
86
common/src/ui/text_buffer.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include "ui/text_buffer.h"
|
||||
|
||||
TextBuffer::TextBuffer(void) : _cursor_row(0), _cursor_col(0) {
|
||||
_lines.emplace_back(""); /* Start with a single empty line. */
|
||||
}
|
||||
|
||||
TextBuffer::~TextBuffer(void) {}
|
||||
|
||||
void TextBuffer::insert_char(char c) {
|
||||
_lines[_cursor_row].insert(_cursor_col, 1, c);
|
||||
_cursor_col++;
|
||||
}
|
||||
|
||||
void TextBuffer::backspace(void) {
|
||||
if(_cursor_col > 0) {
|
||||
_lines[_cursor_row].erase(_cursor_col - 1, 1);
|
||||
_cursor_col--;
|
||||
} else if(_cursor_row > 0) {
|
||||
/* If start of line, merge with previous. */
|
||||
_cursor_col = _lines[_cursor_row-1].length();
|
||||
_lines[_cursor_row-1] += _lines[_cursor_row];
|
||||
_lines.erase(_lines.begin() + _cursor_row);
|
||||
_cursor_row--;
|
||||
}
|
||||
}
|
||||
|
||||
void TextBuffer::newline(void) {
|
||||
std::string current_line = _lines[_cursor_row];
|
||||
std::string new_line = current_line.substr(_cursor_col);
|
||||
_lines[_cursor_row] = current_line.substr(0, _cursor_col);
|
||||
|
||||
_cursor_row++;
|
||||
_lines.insert(_lines.begin() + _cursor_row, new_line);
|
||||
_cursor_col = 0;
|
||||
}
|
||||
|
||||
void TextBuffer::move_cursor(int dx, int dy) {
|
||||
_cursor_col += dx;
|
||||
_cursor_row += dy;
|
||||
|
||||
/* Clamp cursor row. */
|
||||
_cursor_row = std::max(0, std::min((int)_lines.size()-1, _cursor_row));
|
||||
/* Clamp cursor col. */
|
||||
_cursor_col = std::max(0, std::min((int)_lines[_cursor_row].length(), _cursor_col));
|
||||
}
|
||||
|
||||
void TextBuffer::move_cursor_home(void) {
|
||||
_cursor_col = 0;
|
||||
}
|
||||
|
||||
void TextBuffer::move_cursor_end(void) {
|
||||
_cursor_col = _lines[_cursor_row].length();
|
||||
}
|
||||
|
||||
const std::string& TextBuffer::get_line(int row) const {
|
||||
return _lines[row];
|
||||
}
|
||||
|
||||
size_t TextBuffer::get_line_count(void) const {
|
||||
return _lines.size();
|
||||
}
|
||||
|
||||
Point TextBuffer::get_cursor_pos(void) const {
|
||||
return { _cursor_row, _cursor_col };
|
||||
}
|
||||
|
||||
std::string TextBuffer::get_text(void) const {
|
||||
std::stringstream ss;
|
||||
for(size_t i = 0; i < _lines.size(); ++i) {
|
||||
ss << _lines[i];
|
||||
if(i < _lines.size()-1) {
|
||||
ss << "\n"; /* Or another separator? */
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void TextBuffer::clear(void) {
|
||||
_lines.clear();
|
||||
_lines.emplace_back("");
|
||||
_cursor_row = 0;
|
||||
_cursor_col = 0;
|
||||
}
|
||||
33
common/src/ui/text_buffer.h
Normal file
33
common/src/ui/text_buffer.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Point {
|
||||
int row;
|
||||
int col;
|
||||
};
|
||||
|
||||
class TextBuffer {
|
||||
public:
|
||||
TextBuffer(void);
|
||||
~TextBuffer(void);
|
||||
|
||||
void insert_char(char c);
|
||||
void backspace(void);
|
||||
void newline(void);
|
||||
void move_cursor(int dx, int dy);
|
||||
void move_cursor_home(void);
|
||||
void move_cursor_end(void);
|
||||
|
||||
const std::string& get_line(int row) const;
|
||||
size_t get_line_count(void) const;
|
||||
Point get_cursor_pos(void) const;
|
||||
std::string get_text(void) const;
|
||||
void clear(void);
|
||||
|
||||
private:
|
||||
std::vector<std::string> _lines;
|
||||
int _cursor_row;
|
||||
int _cursor_col;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user