readme
This commit is contained in:
+212
@@ -0,0 +1,212 @@
|
||||
#include "Console.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
#include "Gui.h"
|
||||
|
||||
static constexpr float PANEL_HEIGHT = 360.0f;
|
||||
static constexpr float INPUT_HEIGHT = 36.0f;
|
||||
static constexpr float MARGIN = 16.0f;
|
||||
static constexpr int VISIBLE_LINES = 13;
|
||||
static constexpr int MAX_LOG_LINES = 300;
|
||||
static constexpr int MAX_INPUT_LENGTH = 200;
|
||||
static constexpr int SCROLL_STEP = 5;
|
||||
|
||||
static std::string Lowercase(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { return (char)std::tolower(c); });
|
||||
return text;
|
||||
}
|
||||
|
||||
Console::Console(Font* newFont) : font(newFont)
|
||||
{
|
||||
Register("help", "help", "lists every command", [this](const Arguments&) {
|
||||
for (const auto& [name, entry] : commands)
|
||||
Print(" " + entry.usage + " - " + entry.description);
|
||||
});
|
||||
Register("clear", "clear", "empties the console", [this](const Arguments&) {
|
||||
log.clear();
|
||||
scroll = 0;
|
||||
});
|
||||
}
|
||||
|
||||
// GUI draws in the order things were made, so this waits until the first Update to land on top of everything
|
||||
void Console::CreateDisplay()
|
||||
{
|
||||
background = new Box();
|
||||
background->Color = { 0.02f, 0.03f, 0.05f };
|
||||
background->Opacity = 0.0f;
|
||||
background->t2d.position = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
background->t2d.size = { 1.0f, 0.0f, 0.0f, PANEL_HEIGHT };
|
||||
background->BindToRenderSystem();
|
||||
|
||||
inputBackground = new Box();
|
||||
inputBackground->Color = { 0.10f, 0.12f, 0.16f };
|
||||
inputBackground->Opacity = 0.0f;
|
||||
inputBackground->t2d.position = { 0.0f, 0.0f, 0.0f, PANEL_HEIGHT - INPUT_HEIGHT };
|
||||
inputBackground->t2d.size = { 1.0f, 0.0f, 0.0f, INPUT_HEIGHT };
|
||||
inputBackground->BindToRenderSystem();
|
||||
|
||||
// Bottom aligned, so the newest line sits just above the input and old ones get cut off at the top
|
||||
logText = new TextBox();
|
||||
logText->font = font;
|
||||
logText->fontsize = 0.42f;
|
||||
logText->Color = { 0.85f, 0.88f, 0.93f };
|
||||
logText->Opacity = 0.0f;
|
||||
logText->t2d.position = { 0.0f, 0.0f, MARGIN, 8.0f };
|
||||
logText->t2d.size = { 1.0f, 0.0f, -2.0f * MARGIN, PANEL_HEIGHT - INPUT_HEIGHT - 12.0f };
|
||||
logText->textCenter = { 0.0f, 1.0f };
|
||||
logText->BindToRenderSystem();
|
||||
|
||||
inputText = new TextBox();
|
||||
inputText->font = font;
|
||||
inputText->fontsize = 0.45f;
|
||||
inputText->Color = { 0.96f, 0.58f, 0.16f };
|
||||
inputText->Opacity = 0.0f;
|
||||
inputText->t2d.position = { 0.0f, 0.0f, MARGIN, PANEL_HEIGHT - INPUT_HEIGHT };
|
||||
inputText->t2d.size = { 1.0f, 0.0f, -2.0f * MARGIN, INPUT_HEIGHT };
|
||||
inputText->textCenter = { 0.0f, 0.5f };
|
||||
inputText->BindToRenderSystem();
|
||||
}
|
||||
|
||||
void Console::Register(const std::string& name, const std::string& usage, const std::string& description, Command run)
|
||||
{
|
||||
commands[Lowercase(name)] = { usage, description, std::move(run) };
|
||||
}
|
||||
|
||||
void Console::Execute(const std::string& line)
|
||||
{
|
||||
std::istringstream words(line);
|
||||
std::string name;
|
||||
if (!(words >> name))
|
||||
return;
|
||||
|
||||
Arguments arguments;
|
||||
for (std::string word; words >> word; )
|
||||
arguments.push_back(word);
|
||||
|
||||
auto command = commands.find(Lowercase(name));
|
||||
if (command == commands.end()) {
|
||||
Print("Unknown command '" + name + "', type help for a list");
|
||||
return;
|
||||
}
|
||||
command->second.run(arguments);
|
||||
}
|
||||
|
||||
void Console::Print(const std::string& message)
|
||||
{
|
||||
std::istringstream lines(message);
|
||||
for (std::string line; std::getline(lines, line); ) {
|
||||
log.push_back(line);
|
||||
if ((int)log.size() > MAX_LOG_LINES)
|
||||
log.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void Console::Open()
|
||||
{
|
||||
open = true;
|
||||
historyIndex = (int)history.size();
|
||||
caretTimer = 0.0f;
|
||||
}
|
||||
|
||||
void Console::Close()
|
||||
{
|
||||
open = false;
|
||||
}
|
||||
|
||||
void Console::OnChar(unsigned int codepoint)
|
||||
{
|
||||
// The font only has printable ASCII, and the toggle key shouldn't end up in the line
|
||||
if (codepoint < 32 || codepoint > 126 || codepoint == '`' || codepoint == '~')
|
||||
return;
|
||||
if ((int)input.size() < MAX_INPUT_LENGTH)
|
||||
input += (char)codepoint;
|
||||
caretTimer = 0.0f;
|
||||
}
|
||||
|
||||
void Console::OnKey(int key)
|
||||
{
|
||||
caretTimer = 0.0f;
|
||||
switch (key) {
|
||||
case GLFW_KEY_ENTER:
|
||||
case GLFW_KEY_KP_ENTER:
|
||||
if (input.empty())
|
||||
break;
|
||||
Print("> " + input);
|
||||
if (history.empty() || history.back() != input)
|
||||
history.push_back(input);
|
||||
historyIndex = (int)history.size();
|
||||
scroll = 0;
|
||||
{
|
||||
// Copied first, a command is allowed to touch the console's own state
|
||||
const std::string line = input;
|
||||
input.clear();
|
||||
Execute(line);
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_BACKSPACE:
|
||||
if (!input.empty())
|
||||
input.pop_back();
|
||||
break;
|
||||
case GLFW_KEY_UP:
|
||||
if (historyIndex > 0) {
|
||||
historyIndex--;
|
||||
input = history[historyIndex];
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_DOWN:
|
||||
if (historyIndex < (int)history.size()) {
|
||||
historyIndex++;
|
||||
input = historyIndex < (int)history.size() ? history[historyIndex] : std::string();
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_PAGE_UP:
|
||||
Scroll(SCROLL_STEP);
|
||||
break;
|
||||
case GLFW_KEY_PAGE_DOWN:
|
||||
Scroll(-SCROLL_STEP);
|
||||
break;
|
||||
case GLFW_KEY_ESCAPE:
|
||||
Close();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Console::Scroll(int lines)
|
||||
{
|
||||
scroll = glm::clamp(scroll + lines, 0, std::max((int)log.size() - VISIBLE_LINES, 0));
|
||||
}
|
||||
|
||||
void Console::Update(float dt)
|
||||
{
|
||||
if (!background)
|
||||
CreateDisplay();
|
||||
|
||||
const float opacity = open ? 1.0f : 0.0f;
|
||||
background->Opacity = open ? 0.88f : 0.0f;
|
||||
inputBackground->Opacity = open ? 0.9f : 0.0f;
|
||||
logText->Opacity = opacity;
|
||||
inputText->Opacity = opacity;
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
const int newest = (int)log.size() - scroll;
|
||||
const int oldest = std::max(newest - VISIBLE_LINES, 0);
|
||||
std::string visible;
|
||||
for (int i = oldest; i < newest; i++) {
|
||||
visible += log[i];
|
||||
if (i + 1 < newest)
|
||||
visible += '\n';
|
||||
}
|
||||
logText->text = visible;
|
||||
|
||||
caretTimer += dt;
|
||||
const bool caretOn = std::fmod(caretTimer, 1.0f) < 0.5f;
|
||||
inputText->text = "> " + input + (caretOn ? "_" : "");
|
||||
}
|
||||
Reference in New Issue
Block a user