1
0
Fork 0
mirror of https://gitlab.com/niansa/llama_nds.git synced 2025-03-06 20:53:28 +01:00
llama_nds/NDSUI.cpp
2023-04-07 18:43:33 +02:00

215 lines
6.3 KiB
C++

#include "NDSUI.hpp"
#include "font.h"
#include <exception>
#include <gl2d.h>
#include <nds.h>
#include <nds/touch.h>
#include <nds/input.h>
#define SCREEN_BORDER_PADDING 4
#define FONT_PADDING 1
#define INPUT_LINE_HEIGHT (FONT_HEIGHT+FONT_PADDING)
NDSUI *NDSUI::currentInstance;
consteval
unsigned NDSUI::calcMaxCharsInLine(unsigned int width) {
return width / (FONT_WIDTH + FONT_PADDING);
}
consteval
unsigned NDSUI::calcMaxLinesInRow(unsigned height) {
return height / (FONT_HEIGHT + FONT_PADDING);
}
void NDSUI::kbCallback(int key) {
if (key > 0) currentInstance->handleInput(static_cast<char>(key));
}
consteval
Box2D NDSUI::calcMessageLogDimensions() {
return {
{SCREEN_BORDER_PADDING, 0},
{SCREEN_WIDTH-SCREEN_BORDER_PADDING, SCREEN_HEIGHT-(SCREEN_BORDER_PADDING*2)-INPUT_LINE_HEIGHT}
};
}
void NDSUI::renderMessageLog() {
constexpr Box2D boxDims = calcMessageLogDimensions();
// Draw box
glBoxFilled(boxDims.topLeft.x, boxDims.topLeft.y, boxDims.bottomRight.x, boxDims.bottomRight.y, RGB15(6, 2, 14));
// Log messages renderer
auto renderLine = [this, y = int(boxDims.bottomRight.y - FONT_PADDING - FONT_HEIGHT), boxDims] (std::string_view line) mutable {
print(line, {boxDims.topLeft.x + FONT_PADDING, unsigned(y)}, RGB15(28, 28, 28));
y -= FONT_PADDING + FONT_HEIGHT;
if (y < 0) {
throw std::exception(); // Stop rendering any more lines
}
};
// Display line by line
try {
for (auto message = messages.rbegin(); message != messages.rend(); message++) {
const auto& lines = message->getLines();
for (auto line = lines.rbegin(); line != lines.rend(); line++) {
renderLine(*line);
}
}
} catch (...) {}
}
consteval
Box2D NDSUI::calcInputLineDimensions() {
return {
{SCREEN_BORDER_PADDING, SCREEN_HEIGHT-SCREEN_BORDER_PADDING-INPUT_LINE_HEIGHT},
{SCREEN_WIDTH-SCREEN_BORDER_PADDING, SCREEN_HEIGHT-SCREEN_BORDER_PADDING}
};
}
void NDSUI::renderInputLine() {
constexpr Box2D boxDims = calcInputLineDimensions();
constexpr Position2D textTopLeft{boxDims.topLeft.x+FONT_PADDING, boxDims.topLeft.y+FONT_PADDING};
// Draw box
glBoxFilled(boxDims.topLeft.x, boxDims.topLeft.y, boxDims.bottomRight.x, boxDims.bottomRight.y, RGB15(13, 8, 3));
// Measure amount of character that fit in
constexpr auto maxChars = calcMaxCharsInLine(boxDims.bottomRight.x - boxDims.topLeft.x);
// Mease amount of characters that don't fit in
unsigned startIdx = 0;
if (inputLineBufHead > maxChars) {
startIdx = inputLineBufHead - maxChars;
}
// Get that amount of characters
print(&inputLineBuf[startIdx], textTopLeft, RGB15(28, 28, 28));
}
bool NDSUI::printChar(const char character, Position2D *topleftpos, const int color) {
const bool *fontChar = font_get_character(character);
int x = 0;
int y = 0;
unsigned no = 0;
for (const bool *thispixel = fontChar; ; thispixel++) {
if (x == FONT_WIDTH) {
x = 0;
y++;
} if (y == FONT_HEIGHT) {
break;
} if (*thispixel) {
// Calculate spareout
unsigned spareout = font_pixels / 500;
// Skip pixel for spareout
if (!(no++ % spareout)) {
// Draw pixel with melonDS fallback for debug builds
# ifdef NDEBUG
glPutPixel(topleftpos->x + x, topleftpos->y + y, color);
# else
glBoxFilled(topleftpos->x + x, topleftpos->y + y, topleftpos->x + x, topleftpos->y + y, color);
# endif
font_pixels++;
}
}
x++;
}
return fontChar != fontBitmapUnk;
}
void NDSUI::print(std::string_view str, Position2D topleftpos, const int color) {
for (const char c : str) {
printChar(c, &topleftpos, color);
topleftpos.x += FONT_WIDTH + FONT_PADDING;
}
}
void NDSUI::handleInput(char c) {
//TODO: Check length
switch (c) {
case '\b': {
// Remove character if not at start
if (inputLineBufHead != 0) {
inputLineBuf[--inputLineBufHead] = '\0';
}
} break;
case '\n': {
if (userInputHandler && inputLineBuf[0]) {
auto msg = createLogMessage("User", inputLineBuf.data());
addLogMessage(std::move(msg));
userInputHandler->set(inputLineBuf.data());
resetInputLine();
//userInputHandler = nullptr;
}
} break;
default: {
// Add character if not at end
if (inputLineBufHead != inputLineBuf.size() - 1) {
inputLineBuf[inputLineBufHead] = c;
inputLineBuf[++inputLineBufHead] = '\0';
}
}
}
}
NDSUI::NDSUI() {
resetInputLine();
glScreen2D();
auto swkbd = keyboardDemoInit();
swkbd->OnKeyReleased = kbCallback;
keyboardShow();
}
void NDSUI::run() {
currentInstance = this;
font_pixels = 0;
// Update keyboard
keyboardUpdate();
// Draw
glBegin2D(); {
renderInputLine();
renderMessageLog();
} glEnd2D();
// Apply changes
glFlush(0);
swiWaitForVBlank();
}
LineWrappedString NDSUI::createLogMessage(const char *username, std::string_view message) {
// Do line-wrapping, create and return Message instance
return LineWrappedString(" "+std::string(username)+"\n"+std::string(message)+"\n", calcMaxCharsInLine(calcMessageLogDimensions().width()));
}
void LineWrappedString::split(std::string_view str, unsigned maxCharsPerLine) {
lines.clear();
const char *currStrStart = str.data();
const char *strEnd = str.data() + str.size();
for (size_t currStrLen = 0; ;) {
const char& c = currStrStart[currStrLen];
bool brk = false;
bool next = false;
bool minusOne = false;
if (c == '\n') {
next = true;
minusOne = true;
} else if (&c == strEnd) {
next = true;
brk = true;
minusOne = true;
} else if (currStrLen > maxCharsPerLine) {
next = true;
}
if (next) {
lines.push_back({currStrStart, currStrLen});
if (brk) break;
currStrStart += currStrLen + minusOne;
currStrLen = 0;
} else {
currStrLen++;
}
}
}