From d14e063ba60936938b9493b5acee6ac8aaa86327 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 --- assets/games/pokergame/locale/en.csv | 3 +- 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 + 17 files changed, 347 insertions(+), 19 deletions(-) create mode 100644 src/dawn/ui/UIMenu.cpp create mode 100644 src/dawn/ui/UIMenu.hpp diff --git a/assets/games/pokergame/locale/en.csv b/assets/games/pokergame/locale/en.csv index bbdeb68c..dad5cdd7 100644 --- a/assets/games/pokergame/locale/en.csv +++ b/assets/games/pokergame/locale/en.csv @@ -29,4 +29,5 @@ scene.22.1,"King Fight" scene.23.1,"Post King Fight, angry he leaves, town rallies around you" scene.24.1,"Go to one of the kingsmen who's working with the big bad, he's worried that the townspeople know more than they let on." unknown3,"Something here has to bring the big bad to town." -test.1,"Test" \ No newline at end of file +test.1,"Test " +test.2,"Test*" \ No newline at end of file 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