From 9ebd4eb6ad062b3c9f8103f2188e30ce233183ff Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 28 Jan 2023 21:01:30 -0800 Subject: [PATCH] Fixed CSV Parsing on tools --- src/dawn/scene/components/ui/UICanvas.cpp | 21 ++++ src/dawn/scene/components/ui/UICanvas.hpp | 25 ++++- src/dawn/ui/CMakeLists.txt | 1 + src/dawn/ui/UIComponent.cpp | 4 +- src/dawn/ui/UIComponent.hpp | 2 +- src/dawn/ui/UIGrid.cpp | 16 +++ src/dawn/ui/UIGrid.hpp | 2 + src/dawn/ui/UIMenu.cpp | 96 +++++++++++++++++ src/dawn/ui/UIMenu.hpp | 125 ++++++++++++++++++++++ src/dawn/util/memory.hpp | 11 ++ src/dawnpokergame/CMakeLists.txt | 2 - src/dawnpokergame/scenes/TestUIScene.cpp | 11 ++ src/dawnpokergame/scenes/TestUIScene.hpp | 25 +++++ src/dawntools/locale/languagegen/main.c | 8 +- src/dawntools/utils/csv.c | 13 +-- src/dawntools/utils/csv.h | 1 + 16 files changed, 345 insertions(+), 18 deletions(-) create mode 100644 src/dawn/ui/UIMenu.cpp create mode 100644 src/dawn/ui/UIMenu.hpp diff --git a/src/dawn/scene/components/ui/UICanvas.cpp b/src/dawn/scene/components/ui/UICanvas.cpp index 76dede11..59ba8c41 100644 --- a/src/dawn/scene/components/ui/UICanvas.cpp +++ b/src/dawn/scene/components/ui/UICanvas.cpp @@ -7,6 +7,7 @@ #include "scene/Scene.hpp" #include "ui/UIComponent.hpp" #include "game/DawnGame.hpp" +#include "ui/UIMenu.hpp" using namespace Dawn; @@ -26,6 +27,12 @@ void UICanvas::onRenderTargetResize(float_t w, float_t h){ } } +void UICanvas::onSceneUpdate() { + if(this->currentMenu != nullptr) { + this->currentMenu->onTick(); + } +} + void UICanvas::setCamera(Camera *camera) { assertTrue(camera != this->camera); @@ -58,11 +65,23 @@ float_t UICanvas::getHeight() { return this->camera->getRenderTarget()->getHeight(); } +struct UIMenu * UICanvas::getCurrentMenu() { + return this->currentMenu; +} + +void UICanvas::setCurrentMenu(struct UIMenu *menu) { + if(this->currentMenu != nullptr) this->currentMenu->onInactive(); + this->currentMenu = menu; + if(menu != nullptr) menu->onActive(); +} + void UICanvas::onStart() { if(this->camera == nullptr) { auto camera = this->getScene()->findComponent(); this->setCamera(camera); } + + this->getScene()->eventSceneUpdate.addListener(this, &UICanvas::onSceneUpdate); } void UICanvas::onDispose() { @@ -71,6 +90,8 @@ void UICanvas::onDispose() { this, &UICanvas::onRenderTargetResize ); } + + this->getScene()->eventSceneUpdate.removeListener(this, &UICanvas::onSceneUpdate); auto it = this->children.begin(); while(it != this->children.end()) { diff --git a/src/dawn/scene/components/ui/UICanvas.hpp b/src/dawn/scene/components/ui/UICanvas.hpp index 5b779c84..b48d9d32 100644 --- a/src/dawn/scene/components/ui/UICanvas.hpp +++ b/src/dawn/scene/components/ui/UICanvas.hpp @@ -16,12 +16,15 @@ namespace Dawn { }; class UIComponent; + struct UIMenu; class UICanvas : public SceneItemComponent { protected: Camera *camera = nullptr; + struct UIMenu *currentMenu = nullptr; void onRenderTargetResize(float_t w, float_t h); + void onSceneUpdate(); public: /** @@ -33,7 +36,7 @@ namespace Dawn { */ static UICanvas * create(Scene *scene); - // + //======================================================================// std::vector children; UIDrawType drawType = UI_DRAW_TYPE_WORLD_CAMERA_RELATIVE; @@ -64,6 +67,12 @@ namespace Dawn { return item; } + /** + * Find a UI Element attached to this canvas. + * + * @tparam Type of the UI item to find. + * @return Pointer to first matching element of type T. + */ template T * findElement() { auto it = this->children.begin(); @@ -90,6 +99,20 @@ namespace Dawn { */ float_t getHeight(); + /** + * Returns the currently active menu for this UI Canvas. + * + * @return The currently active menu, or nullptr if there is none. + */ + struct UIMenu * getCurrentMenu(); + + /** + * Sets the currently active menu, and ticks it appropriately. + * + * @param menu Menu to set as the current active menu. + */ + void setCurrentMenu(struct UIMenu *menu); + void onStart() override; void onDispose() override; }; diff --git a/src/dawn/ui/CMakeLists.txt b/src/dawn/ui/CMakeLists.txt index 3ae4ab2d..1c86017b 100644 --- a/src/dawn/ui/CMakeLists.txt +++ b/src/dawn/ui/CMakeLists.txt @@ -12,4 +12,5 @@ target_sources(${DAWN_TARGET_NAME} UISprite.cpp UIEmpty.cpp UIGrid.cpp + UIMenu.cpp ) \ No newline at end of file diff --git a/src/dawn/ui/UIComponent.cpp b/src/dawn/ui/UIComponent.cpp index 018d3607..3cd5b23b 100644 --- a/src/dawn/ui/UIComponent.cpp +++ b/src/dawn/ui/UIComponent.cpp @@ -91,9 +91,7 @@ void UIComponent::updatePositions() { } // Fire event - eventAlignmentUpdated.invoke( - this->width, this->height, this->relativeX, this->relativeY - ); + eventAlignmentUpdated.invoke(this); } float_t UIComponent::getWidth() { diff --git a/src/dawn/ui/UIComponent.hpp b/src/dawn/ui/UIComponent.hpp index ff070dd2..d87966c8 100644 --- a/src/dawn/ui/UIComponent.hpp +++ b/src/dawn/ui/UIComponent.hpp @@ -38,7 +38,7 @@ namespace Dawn { UIComponent *parent = nullptr; // Events - Event eventAlignmentUpdated; + Event eventAlignmentUpdated; // I currently don't support rotation or scale. Not because I can't but // because it's basically un-necessary. Unity does support rotation but diff --git a/src/dawn/ui/UIGrid.cpp b/src/dawn/ui/UIGrid.cpp index 651ef076..2a7f51b9 100644 --- a/src/dawn/ui/UIGrid.cpp +++ b/src/dawn/ui/UIGrid.cpp @@ -36,7 +36,18 @@ std::vector UIGrid::getSelfPassItems( return std::vector(); } +void UIGrid::onChildAligned(UIComponent *child) { + assertNotNull(child); + assertMapHasKey(this->gridChildren, child); + this->alignChild(child, this->gridChildren[child]); +} + void UIGrid::alignChild(UIComponent *child, struct UIGridPosition pos) { + assertNotNull(child); + + // Remove event listener + child->eventAlignmentUpdated.addListener(this, &UIGrid::onChildAligned); + float_t gridX = (this->sizeCol * pos.x) + (this->gutterX * pos.x); float_t gridY = (this->sizeRow * pos.y) + (this->gutterY * pos.y); @@ -68,6 +79,9 @@ void UIGrid::alignChild(UIComponent *child, struct UIGridPosition pos) { glm::vec4(gridX + x, gridY + y, sizeX, sizeY), 0.0f ); + + // Re-Add event listener + child->eventAlignmentUpdated.addListener(this, &UIGrid::onChildAligned); } void UIGrid::setGridSize( @@ -79,6 +93,8 @@ void UIGrid::setGridSize( this->gutterX = gutterX; this->gutterY = gutterY; + // TODO: Need to fix children here. + this->gridChildren.clear(); this->updatePositions(); } diff --git a/src/dawn/ui/UIGrid.hpp b/src/dawn/ui/UIGrid.hpp index 93476b9e..02b5e873 100644 --- a/src/dawn/ui/UIGrid.hpp +++ b/src/dawn/ui/UIGrid.hpp @@ -33,6 +33,8 @@ namespace Dawn { */ void alignChild(UIComponent *child, struct UIGridPosition pos); + void onChildAligned(UIComponent *child); + protected: void updatePositions() override; std::vector getSelfPassItems( diff --git a/src/dawn/ui/UIMenu.cpp b/src/dawn/ui/UIMenu.cpp new file mode 100644 index 00000000..fada72a2 --- /dev/null +++ b/src/dawn/ui/UIMenu.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "UIMenu.hpp" + +using namespace Dawn; + +UIMenu::UIMenu(UICanvas *canvas, int32_t columns, int32_t rows) { + assertNotNull(canvas); + assertTrue(columns > 0); + assertTrue(rows > 0); + + this->canvas = canvas; + this->rows = rows; + this->columns = columns; + this->items = (UIMenuItem**)memoryFillWithZero(sizeof(UIMenuItem*)*columns*rows); +} + +void UIMenu::setSize(int32_t cols, int32_t rows) { + assertNotNull(this->items); + assertTrue(columns > 0); + assertTrue(rows > 0); + assertTrue(columns != this->columns); + assertTrue(rows != this->rows); + + memoryFree(this->items); + this->items = (UIMenuItem**)memoryFillWithZero(sizeof(UIMenuItem*)*columns*rows); +} + +UIMenuItem * UIMenu::getItem(int32_t x, int32_t y) { + return this->items[x * this->columns + y]; +} + +void UIMenu::setPosition(int32_t x, int32_t y) { + assertTrue(x >= 0 && x < this->columns); + assertTrue(y >= 0 && y < this->rows); + UIMenuItem *item; + + if(this->x != -1) { + assertTrue(this->y != -1); + item = this->getItem(this->x, this->y); + if(item != nullptr) { + item->onItemOff(); + } + } + + this->eventCursorChange.invoke(this->x, this->y, x, y); + + this->x = x; + this->y = y; + item = this->getItem(x, y); + if(item != nullptr && item->canBeOvered()) { + item->onItemOver(); + } +} + +void UIMenu::moveRelative(int32_t x, int32_t y) { + int32_t x2 = this->x + x; + if(x2 < 0 || x2 >= this->columns) return; + int32_t y2 = this->y + y; + if(y2 < 0 || y2 >= this->rows) return; + this->setPosition(x2, y2); +} + +void UIMenu::setItem(int32_t x, int32_t y, UIMenuItem *item) { + assertTrue(x >= 0); + assertTrue(y >= 0); + assertTrue(x < this->columns); + assertTrue(y < this->rows); + assertNotNull(item); + assertNotNull(this->items); + assertNull(this->getItem(x, y)); + + this->items[x * this->columns + y] = item; +} + +void UIMenu::onInactive() { + this->eventMenuInactive.invoke(); +} + +void UIMenu::onActive() { + this->eventMenuActive.invoke(); +} + +void UIMenu::onTick() { + std::cout << "Tick menu" << std::endl; +} + +UIMenu::~UIMenu() { + if(this->canvas->getCurrentMenu() == this) { + this->canvas->setCurrentMenu(nullptr); + } + memoryFree(this->items); +} \ No newline at end of file diff --git a/src/dawn/ui/UIMenu.hpp b/src/dawn/ui/UIMenu.hpp new file mode 100644 index 00000000..e3357f76 --- /dev/null +++ b/src/dawn/ui/UIMenu.hpp @@ -0,0 +1,125 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "event/Event.hpp" +#include "ui/UIComponent.hpp" +#include "util/memory.hpp" + +namespace Dawn { + class UIMenuItem { + public: + /** + * Called when the item is selected (Accepted) on. + */ + virtual void onItemSelected() = 0; + + /** + * Called when either the mouse or the user controller input is removed + * off this item. + */ + virtual void onItemOver() = 0; + + /** + * Called when either the mouth or the user controller input is put over + * this item. + */ + virtual void onItemOff() = 0; + + /** + * Returns whether the UI item can be hovered overed or not. + * @return Whether it can be overed or not. + */ + virtual bool_t canBeOvered() = 0; + + /** + * Returns whether or not the item can be selected or not. + * @return Whether it can be selected or not. + */ + virtual bool_t canBeSelected() = 0; + }; + + struct UIMenu { + private: + UICanvas *canvas; + int32_t x = -1; + int32_t y = -1; + int32_t rows = 1; + int32_t columns = 1; + UIMenuItem **items = nullptr; + + protected: + /** Invoked by UICanvas when this menu has been made inactive. */ + void onInactive(); + /** Invoked by UICanvas when this menu has been made active. */ + void onActive(); + /** Invoked by UICanvas every tick this menu is active. */ + void onTick(); + + public: + Event<> eventMenuActive; + Event<> eventMenuInactive; + Event eventCursorChange; + Event eventItemSelected; + + /** + * Construct a new UI Menu Host. + * + * @param canvas Canvas that this menu belongs to. + * @param columns Iniitial size of the menu X axis. + * @param rows Initial size of the menu Y axis. + */ + UIMenu(UICanvas *canvas, int32_t columns, int32_t rows); + + /** + * Sets the size of the UI Menu. + * + * @param columns How many columns in the menu. + * @param rows How many rows in the menu. + */ + void setSize(int32_t columns, int32_t rows); + + /** + * Returns the UI Item at the given position. + * + * @param x X coordinate of the item to get. + * @param y Y coordinate of the item to get. + * @return The pointer to the menu item, or null if invalid. + */ + UIMenuItem * getItem(int32_t x, int32_t y); + + /** + * Sets the position of the cursor in the grid. + * + * @param x X position of the cursor. + * @param y Y position of the cursor. + */ + void setPosition(int32_t x, int32_t y); + + /** + * Move the cursor relative to the current position. + * + * @param x X position to move relative. + * @param y Y position to move relative. + */ + void moveRelative(int32_t x, int32_t y); + + /** + * Adds/Sets an item onto the menu. + * + * @param x X coordinate to set the item. + * @param y Y coordinate to set the item. + * @param item Item to set. + */ + void setItem(int32_t x, int32_t y, UIMenuItem *item); + + /** + * Cleans up the menu items, doesn't free the children themselves. + */ + ~UIMenu(); + + friend class UICanvas; + }; +} \ No newline at end of file diff --git a/src/dawn/util/memory.hpp b/src/dawn/util/memory.hpp index e12e05ad..4388b705 100644 --- a/src/dawn/util/memory.hpp +++ b/src/dawn/util/memory.hpp @@ -20,6 +20,17 @@ static inline void * memoryAllocate(const size_t size) { return (void *)malloc(size); } +/** + * Allocate space in memory, where all values are set to 0 (in binary space). + * + * @param size Size of the array. + * @return Pointer to the space in memory to use. + */ +static inline void * memoryFillWithZero(const size_t size) { + return (void *)calloc(1, size); +} + + /** * Free some previously allocated memory space. * @param pointer Pointer in memory to free. diff --git a/src/dawnpokergame/CMakeLists.txt b/src/dawnpokergame/CMakeLists.txt index b3b28e96..0cf3b496 100644 --- a/src/dawnpokergame/CMakeLists.txt +++ b/src/dawnpokergame/CMakeLists.txt @@ -30,7 +30,6 @@ set(DIR_GAME_ASSETS games/pokergame) tool_texture(texture_test texture_test.png) tool_language(language_en ${DIR_GAME_ASSETS}/locale/en.csv) -tool_language(language_jp ${DIR_GAME_ASSETS}/locale/jp.csv) tool_tileset(tileset_death texture_death ${DIR_GAME_ASSETS}/characters/death/sheet.png 1 3) @@ -40,7 +39,6 @@ tool_audio(audio_test borrowed/sample_short.wav) add_dependencies(${DAWN_TARGET_NAME} language_en - language_jp tileset_death diff --git a/src/dawnpokergame/scenes/TestUIScene.cpp b/src/dawnpokergame/scenes/TestUIScene.cpp index e9919de2..050e8db6 100644 --- a/src/dawnpokergame/scenes/TestUIScene.cpp +++ b/src/dawnpokergame/scenes/TestUIScene.cpp @@ -46,6 +46,8 @@ void TestUIScene::stage() { auto grid = this->canvas->addElement(); grid->setTransform(UI_COMPONENT_ALIGN_STRETCH, UI_COMPONENT_ALIGN_STRETCH, glm::vec4(0, 0, 0, 0), 0); grid->setGridSize(4, 4, 8, 8); + + auto menu = new UIMenu(this->canvas, grid->getColumns(), grid->getRows()); for(int32_t x = 0; x < grid->getColumns(); x++) { for(int32_t y = 0; y < grid->getRows(); y++) { @@ -54,6 +56,15 @@ void TestUIScene::stage() { label->setText("test.1"); label->setFontSize(24); grid->addToGrid(label, x, y, UI_COMPONENT_ALIGN_END, UI_COMPONENT_ALIGN_END); + + auto menuItem = new TestMenuItem; + menuItem->label = label; + menu->setItem(x, y, menuItem); } + + menu->setPosition(0, 0); + this->canvas->setCurrentMenu(menu); } + + this->canvas->setCurrentMenu(menu); } \ No newline at end of file diff --git a/src/dawnpokergame/scenes/TestUIScene.hpp b/src/dawnpokergame/scenes/TestUIScene.hpp index ad76acc6..86568bb0 100644 --- a/src/dawnpokergame/scenes/TestUIScene.hpp +++ b/src/dawnpokergame/scenes/TestUIScene.hpp @@ -12,8 +12,33 @@ #include "ui/UIGrid.hpp" #include "ui/UISprite.hpp" #include "prefabs/SimpleSpinningCubePrefab.hpp" +#include "ui/UIMenu.hpp" namespace Dawn { + class TestMenuItem : public UIMenuItem { + public: + UILabel *label; + void onItemSelected() { + + } + + void onItemOver() { + this->label->setText("test.2"); + } + + void onItemOff() { + this->label->setText("test.1"); + } + + bool_t canBeOvered() { + return true; + } + + bool_t canBeSelected() { + return true; + } + }; + class TestUIScene : public Scene { private: Camera *camera = nullptr; diff --git a/src/dawntools/locale/languagegen/main.c b/src/dawntools/locale/languagegen/main.c index a3d3c102..a37aedd9 100644 --- a/src/dawntools/locale/languagegen/main.c +++ b/src/dawntools/locale/languagegen/main.c @@ -56,23 +56,22 @@ int main(int argc, char *argv[]) { size_t readSize = fread(buffer, 1, fileSize, file); fclose(file); if(readSize < fileSize) { - free(buffer); printf("Failed to read all data from CSV\n"); + free(buffer); return 1; } buffer[fileSize] = '\0'; csvParse(buffer, &csv); free(buffer); - printf("Parsed\n"); // Prepare output file for writing. sprintf(path, "%s.language", out); fileMkdirp(path); file = fopen(path, "wb"); if(file == NULL) { - csvDispose(&csv); printf("Failed to create output language file\n"); + csvDispose(&csv); return 1; } @@ -85,14 +84,13 @@ int main(int argc, char *argv[]) { csvDispose(&csv); return 1; } - + char *key = csvGetCell(&csv, y, 0); char *value = csvGetCell(&csv, y, 1); // 23/01/14 - Replace \r in CSV. stringRemoveAll(key, '\r'); stringRemoveAll(value, '\r'); - if(strlen(key) <= 0 || strlen(value) <= 0) { printf("Failed to parse language. Line %i has an invalid string\n", y); fclose(file); diff --git a/src/dawntools/utils/csv.c b/src/dawntools/utils/csv.c index 9e58482b..a7a616a1 100644 --- a/src/dawntools/utils/csv.c +++ b/src/dawntools/utils/csv.c @@ -8,7 +8,7 @@ #include "csv.h" char * csvGetCell(csv_t *csv, int32_t row, int32_t cell) { - return csv->rows[(row * CSV_ROW_COUNT_MAX) + cell]; + return csv->rows[(row * CSV_COLUMN_COUNT_MAX) + cell]; } void csvParse(char *string, csv_t *csv) { @@ -18,9 +18,9 @@ void csvParse(char *string, csv_t *csv) { int32_t rowCellCount; length = strlen(string); - csv->buffer = malloc(sizeof(char) * length * 2); + csv->buffer = malloc(sizeof(char) * (length+1) * CSV_COLUMN_COUNT_MAX * CSV_ROW_COUNT_MAX); csv->cellCounts = malloc(sizeof(int32_t) * CSV_ROW_COUNT_MAX); - csv->rows = malloc(sizeof(char *) * 32 * CSV_ROW_COUNT_MAX); + csv->rows = malloc(sizeof(char*) * CSV_ROW_COUNT_MAX * CSV_COLUMN_COUNT_MAX); i = 0; j = 0; @@ -35,7 +35,7 @@ void csvParse(char *string, csv_t *csv) { case CSV_PARSE_STATE_FIND_CELL: if(c == '"') { state = CSV_PARSE_STATE_PARSE_CELL_WITH_QUOTES; - csv->rows[(csv->rowCount * CSV_ROW_COUNT_MAX) + rowCellCount] = csv->buffer + j; + csv->rows[(csv->rowCount * CSV_COLUMN_COUNT_MAX) + rowCellCount] = csv->buffer + j; rowCellCount++; continue; } else if(c == '\r' || c == '\n') { @@ -43,13 +43,13 @@ void csvParse(char *string, csv_t *csv) { state = CSV_PARSE_STATE_LINE_END; continue; } else if(c == ',') { - csv->rows[(csv->rowCount * CSV_ROW_COUNT_MAX) + rowCellCount] = csv->buffer + j; + csv->rows[(csv->rowCount * CSV_COLUMN_COUNT_MAX) + rowCellCount] = csv->buffer + j; csv->buffer[j++] = '\0'; rowCellCount++; continue; } else { state = CSV_PARSE_STATE_PARSE_CELL; - csv->rows[(csv->rowCount * CSV_ROW_COUNT_MAX) + rowCellCount] = csv->buffer + j; + csv->rows[(csv->rowCount * CSV_COLUMN_COUNT_MAX) + rowCellCount] = csv->buffer + j; csv->buffer[j++] = c; rowCellCount++; continue; @@ -104,6 +104,7 @@ void csvParse(char *string, csv_t *csv) { free(NULL); } } + csv->buffer[j++] = '\0'; if(rowCellCount != 0) { diff --git a/src/dawntools/utils/csv.h b/src/dawntools/utils/csv.h index 340e2cfe..8ccd561b 100644 --- a/src/dawntools/utils/csv.h +++ b/src/dawntools/utils/csv.h @@ -10,6 +10,7 @@ #include "string.h" #define CSV_ROW_COUNT_MAX 128 +#define CSV_COLUMN_COUNT_MAX 16 typedef enum { CSV_PARSE_STATE_FIND_CELL,//0