From 8b1f403c87adf62009421af67c20e0c3c06d5e9c Mon Sep 17 00:00:00 2001 From: Bobby Lucero Date: Tue, 1 Oct 2024 20:52:51 -0400 Subject: [PATCH] Initial cursor implementation --- src/Graphics/Graphics.cpp | 35 +++++++++ src/Graphics/Graphics.h | 1 + src/Pycron.cpp | 13 +++ src/StateManager.cpp | 8 ++ src/StateManager.h | 3 + src/States/Editor/EditorState.cpp | 109 +++++++++++++++++++------- src/States/Editor/EditorState.h | 14 +++- src/States/Editor/PythonTokenizer.cpp | 4 +- 8 files changed, 154 insertions(+), 33 deletions(-) diff --git a/src/Graphics/Graphics.cpp b/src/Graphics/Graphics.cpp index 7bd07ee..7726b3f 100644 --- a/src/Graphics/Graphics.cpp +++ b/src/Graphics/Graphics.cpp @@ -247,6 +247,41 @@ void Graphics::Pixel(int x, int y, int paletteIndex) { m_virtualScreenColorBuffer[y * m_screenWidth + x] = paletteIndex; } +void Graphics::Line(int x0, int y0, int x1, int y1, int paletteIndex) { + int dx = x1 - x0; // Change in x + int dy = y1 - y0; // Change in y + int abs_dx = std::abs(dx); + int abs_dy = std::abs(dy); + + int sx = (dx > 0) ? 1 : -1; // Step direction in x + int sy = (dy > 0) ? 1 : -1; // Step direction in y + + // Choose the primary axis for iteration + if (abs_dx > abs_dy) { + int err = abs_dx / 2; // Error value + for (int i = 0; i <= abs_dx; i++) { + Pixel(x0, y0, paletteIndex); // Plot the pixel + err -= abs_dy; // Update error term + if (err < 0) { + y0 += sy; // Move in y direction + err += abs_dx; // Update error term + } + x0 += sx; // Always move in x direction + } + } else { + int err = abs_dy / 2; // Error value + for (int i = 0; i <= abs_dy; i++) { + Pixel(x0, y0, paletteIndex); // Plot the pixel + err -= abs_dx; // Update error term + if (err < 0) { + x0 += sx; // Move in x direction + err += abs_dy; // Update error term + } + y0 += sy; // Always move in y direction + } + } +} + void Graphics::Circle(int x, int y, int radius, int paletteIndex) { Ellipse(x, y, radius, radius, paletteIndex); } diff --git a/src/Graphics/Graphics.h b/src/Graphics/Graphics.h index 7b38ee7..970064a 100644 --- a/src/Graphics/Graphics.h +++ b/src/Graphics/Graphics.h @@ -70,6 +70,7 @@ public: void Clear(int paletteIndex); void Pixel(int x, int y, int paletteIndex); + void Line(int x0, int y0, int x1, int y1, int paletteIndex); void Circle(int x, int y, int radius, int paletteIndex); void Ellipse(int x, int y, int width, int height, int paletteIndex); void EllipseBorder(int x, int y, int width, int height, int paletteIndex); diff --git a/src/Pycron.cpp b/src/Pycron.cpp index 86fca65..fcff316 100644 --- a/src/Pycron.cpp +++ b/src/Pycron.cpp @@ -45,6 +45,19 @@ void Pycron::StartGameLoop() { } m_graphics->updateVMVars(m_vm); m_graphics->draw(this->m_stateManager); + + char c = (char)GetCharPressed(); + + while(c > 0){ + m_stateManager->OnCharPressed(c); + c = (char)GetCharPressed(); + } + + for (int key = KeyboardKey::KEY_BACK; key < KeyboardKey::KEY_KP_EQUAL; ++key) { + if(IsKeyPressed(key) || IsKeyPressedRepeat(key)){ + m_stateManager->OnKeyPressed(key); + } + } } } diff --git a/src/StateManager.cpp b/src/StateManager.cpp index 590e10e..576a122 100644 --- a/src/StateManager.cpp +++ b/src/StateManager.cpp @@ -67,3 +67,11 @@ void StateManager::Draw() { } } +void StateManager::OnKeyPressed(int key) { + m_currentState->OnKeyPressed(key); +} + +void StateManager::OnCharPressed(char c) { + m_currentState->OnCharPressed(c); +} + diff --git a/src/StateManager.h b/src/StateManager.h index 64767cc..3da2f38 100644 --- a/src/StateManager.h +++ b/src/StateManager.h @@ -31,6 +31,9 @@ public: void ChangeState(StateManager::StateType state); + void OnKeyPressed(int key); + void OnCharPressed(char c); + void RequestLoadGame(); void RequestRunGame(); void RequestStopGame(); diff --git a/src/States/Editor/EditorState.cpp b/src/States/Editor/EditorState.cpp index 3118456..a92322f 100644 --- a/src/States/Editor/EditorState.cpp +++ b/src/States/Editor/EditorState.cpp @@ -8,6 +8,7 @@ #include "EditorState.h" #include "../../Graphics/PycronImage.h" #include "../../Pycron.h" +#include EditorState::EditorState(pkpy::VM *vm, Graphics *graphics) : m_vm(vm), m_graphics(graphics){ @@ -15,14 +16,15 @@ EditorState::EditorState(pkpy::VM *vm, Graphics *graphics) : m_vm(vm), m_graphic Token a(TokenType::Keyword, "Test"); - std::string randomSource = Pycron::loadFileToString("../python/syntaxTest.py"); + std::string randomSource = Pycron::loadFileToString("../python/main.py"); m_baseBackgroundColor = 56; - m_baseTextColor = 63; + m_shadowColor = 28; + m_baseTextColor = 42; m_lineNumberBackgroundColor = 57; m_lineNumberTextColor = 6; m_unknownTextColor = 4; - m_identifierTextColor = 43; + m_identifierTextColor = 63; m_keywordTextColor = 31; m_builtinTextColor = 23; m_numericalLiteralTextColor = 32; @@ -31,23 +33,30 @@ EditorState::EditorState(pkpy::VM *vm, Graphics *graphics) : m_vm(vm), m_graphic m_operatorTextColor = 45; m_commentTextColor = 60; - m_topLetterSpacing = 0; m_bottomLetterSpacing = 1; m_leftLetterSpacing = 0; m_rightLetterSpacing = 1; + m_cursorX = 0; + m_cursorY = 0; + + m_cursorBlinkTimer = 0; + m_cursorBlinkInterval = 0.5; + m_charWidth = m_leftLetterSpacing + m_graphics->GetCurrentFontWidth() + m_rightLetterSpacing; // Final size of char with spacing. If the literal width of the font is n, the final width is n + spacing. m_charHeight = m_topLetterSpacing + m_graphics->GetCurrentFontHeight() + m_bottomLetterSpacing; - m_textWidth = (int)(m_graphics->m_screenWidth / m_charWidth); - m_textHeight = (int)(m_graphics->m_screenHeight / m_charHeight); + m_width = (int)(m_graphics->m_screenWidth / m_charWidth); + m_height = (int)(m_graphics->m_screenHeight / m_charHeight); - m_characterBuffer = std::vector(m_textWidth * m_textHeight); - m_foregroundIndexBuffer = std::vector(m_textWidth * m_textHeight); - m_backgroundIndexBuffer = std::vector(m_textWidth * m_textHeight); + m_characterBuffer = std::vector(m_width * m_height); + m_foregroundIndexBuffer = std::vector(m_width * m_height); + m_backgroundIndexBuffer = std::vector(m_width * m_height); - m_dirtyFlags = std::vector(m_textHeight); + m_dirtyFlags = std::vector(m_height); + + m_drawShadows = true; Clear(); @@ -62,28 +71,20 @@ EditorState::~EditorState() { } void EditorState::Draw() { - m_graphics->Clear(0); - - int bg = 28; - - for (int i = 0; i < m_characterBuffer.size(); ++i) { - int x = (i % m_textWidth) * m_charWidth; - int y = (i / m_textWidth) * m_charHeight; - m_graphics->Rect(x, y, m_charWidth, m_charHeight, m_backgroundIndexBuffer[i]); - m_graphics->Char(m_characterBuffer[i], x + m_leftLetterSpacing + 1, y + m_topLetterSpacing + 1, bg); - m_graphics->Char(m_characterBuffer[i], x + m_leftLetterSpacing, y + m_topLetterSpacing, m_foregroundIndexBuffer[i]); - } + // Set text and color buffers if needed if(m_dirty){ Clear(); m_dirty = false; for (int i = 0; i < m_text.size(); ++i) { // Line numbers TODO: maybe not have this as part of the buffer, instead as a custom bar. (Allows for more custom functionality such as bookmarks) +// std::string lineNumber = std::to_string(std::abs(m_cursorY - i)); +// if(i == m_cursorY) lineNumber = std::to_string(m_cursorY); std::string lineNumber = std::to_string(i); int size = 2; int diff = size - (int)lineNumber.size(); if(diff > 0) lineNumber = std::string(diff, ' ') + lineNumber; - Text(lineNumber, 0, i, m_lineNumberTextColor, m_lineNumberBackgroundColor); + Text(lineNumber, 0, i, i == m_cursorY ? m_lineNumberTextColor : m_commentTextColor, m_lineNumberBackgroundColor); // Text handling auto tokens = m_pythonTokenizer->tokenizeLine(m_text[i]); @@ -117,6 +118,37 @@ void EditorState::Draw() { } } + m_graphics->Clear(0); + + // draw text with info from color buffers + for (int i = 0; i < m_characterBuffer.size(); ++i) { + int x = (i % m_width) * m_charWidth; + int y = (i / m_width) * m_charHeight; + m_graphics->Rect(x, y, m_charWidth, m_charHeight, m_backgroundIndexBuffer[i]); + if(m_drawShadows) m_graphics->Char(m_characterBuffer[i], x + m_leftLetterSpacing + 1, y + m_topLetterSpacing + 1, m_shadowColor); + m_graphics->Char(m_characterBuffer[i], x + m_leftLetterSpacing, y + m_topLetterSpacing, m_foregroundIndexBuffer[i]); + } + + + if(m_cursorVisible) { + int x = (m_cursorX + 2) * m_charWidth; + int y = m_cursorY * m_charHeight; + int index = m_cursorY * m_width + (m_cursorX + 2); + + if(m_drawShadows) m_graphics->Rect(x, y, m_charWidth + 1, m_charHeight + 1, m_shadowColor); + m_graphics->Rect(x - 1, y - 1, m_charWidth + 1, m_charHeight + 1, m_foregroundIndexBuffer[index]); +// m_graphics->Line(x - 1, y, x - 1, y + m_charHeight - 1, 63); + std::string s(1, m_characterBuffer[index]); + m_graphics->Text(s, x, y, m_backgroundIndexBuffer[index]); + } + + // Cursor blink + m_cursorBlinkTimer += GetFrameTime(); + if(m_cursorBlinkTimer > m_cursorBlinkInterval){ + m_cursorVisible = !m_cursorVisible; + m_cursorBlinkTimer = 0.0; + } + } void EditorState::OnEnter() { @@ -128,11 +160,33 @@ void EditorState::OnExit() { } void EditorState::OnKeyPressed(int key) { - std::cout << key << ". \n"; + if(key == KEY_LEFT && m_cursorX > 0){ + m_cursorX--; + } + if(key == KEY_RIGHT && m_cursorX < m_width - 1 - 2){ + m_cursorX++; + } + if(key == KEY_UP && m_cursorY > 0){ + m_cursorY--; + } + if(key == KEY_DOWN && m_cursorY < m_height - 1){ + m_cursorY++; + } + + m_cursorVisible = true; + m_cursorBlinkTimer = 0; + + m_dirty = true; + + if(key == KEY_GRAVE) m_drawShadows = !m_drawShadows; +} + +void EditorState::OnCharPressed(char character){ + } void EditorState::Clear() { - for (int i = 0; i < m_textWidth * m_textHeight; ++i) { + for (int i = 0; i < m_width * m_height; ++i) { m_characterBuffer[i] = ' '; m_foregroundIndexBuffer[i] = m_baseTextColor; m_backgroundIndexBuffer[i] = m_baseBackgroundColor; @@ -143,11 +197,11 @@ void EditorState::Text(const std::string &text, int x, int y, int fg, int bg) { for (int i = 0; i < text.size(); ++i) { int charX = x + i; int charY = y; - if(charX < 0 || charY < 0 || charX >= m_textWidth || charY >= m_textHeight){ + if(charX < 0 || charY < 0 || charX >= m_width || charY >= m_height){ return; } char c = text[i]; - int index = y * m_textWidth + (x + i); + int index = y * m_width + (x + i); m_characterBuffer[index] = c; m_backgroundIndexBuffer[index] = bg; m_foregroundIndexBuffer[index] = fg; @@ -156,9 +210,6 @@ void EditorState::Text(const std::string &text, int x, int y, int fg, int bg) { } } -void EditorState::OnCharPressed(char character) { - -} void EditorState::LoadStringToBuffer(const std::string &text) { m_text.clear(); diff --git a/src/States/Editor/EditorState.h b/src/States/Editor/EditorState.h index 0d30933..5312b73 100644 --- a/src/States/Editor/EditorState.h +++ b/src/States/Editor/EditorState.h @@ -24,8 +24,12 @@ private: Graphics* m_graphics; PythonTokenizer* m_pythonTokenizer; + // Size of the character in pixels uint8_t m_charWidth, m_charHeight; - uint8_t m_textWidth, m_textHeight; + + // Size of the editor in characters + uint8_t m_width, m_height; + uint8_t m_topLetterSpacing, m_bottomLetterSpacing, m_leftLetterSpacing, m_rightLetterSpacing; // Text Buffer std::vector m_characterBuffer; @@ -38,6 +42,7 @@ private: // Theming uint8_t m_baseBackgroundColor; + uint8_t m_shadowColor; uint8_t m_baseTextColor; uint8_t m_lineNumberBackgroundColor; uint8_t m_lineNumberTextColor; @@ -51,9 +56,16 @@ private: uint8_t m_operatorTextColor; uint8_t m_commentTextColor; + int m_cursorX; + int m_cursorY; + bool m_cursorVisible; + float_t m_cursorBlinkTimer; + float_t m_cursorBlinkInterval; bool m_dirty; + bool m_drawShadows; + void Clear(); void Text(const std::string &text, int x, int y, int fg = 0, int bg = 63); diff --git a/src/States/Editor/PythonTokenizer.cpp b/src/States/Editor/PythonTokenizer.cpp index eafe9d0..d4d6ce0 100644 --- a/src/States/Editor/PythonTokenizer.cpp +++ b/src/States/Editor/PythonTokenizer.cpp @@ -117,7 +117,7 @@ bool PythonTokenizer::isPunctuation(char c) { } bool PythonTokenizer::isOperator(char c) { - return c == '+' || c == '-' || c == '*' || c == '/' || c == '=' || c == '<' || c == '>' || c == '!'; + return c == '+' || c == '-' || c == '*' || c == '/' || c == '=' || c == '<' || c == '>' || c == '!' || c == ':'; } std::string PythonTokenizer::readNumericLiteral(const std::string &line) { @@ -165,8 +165,6 @@ std::string PythonTokenizer::readStringLiteral(const std::string &line) { m_currentPos++; while (m_currentPos < line.length() && line[m_currentPos] != '"') { m_currentPos++; - std::cout << m_currentPos; - } if(line[m_currentPos] == '"') m_currentPos++;