Merge pull request #12 from Juk3n/11-removing-the-game-from-a-console-menuUnverified
11 games as buttons
modified
CMakeLists.txt +5 -1 | @@ -8,7 +8,7 @@ include(FetchContent) | ||
| 8 | 8 | set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE) |
| 9 | 9 | FetchContent_Declare(ftxui |
| 10 | 10 | GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui |
| 11 | - GIT_TAG v4.0.0 | |
| 11 | + GIT_TAG v5.0.0 | |
| 12 | 12 | ) |
| 13 | 13 | FetchContent_MakeAvailable(ftxui) |
| 14 | 14 | if(NOT ftxui_POPULATED) |
| @@ -18,6 +18,8 @@ endif() | ||
| 18 | 18 | |
| 19 | 19 | |
| 20 | 20 | |
| 21 | +find_program(CLANG_TIDY NAMES "clang-tidy" REQUIRED) | |
| 22 | +set(CLANG_TIDY_COMMAND "${CLANG_TIDY}" "-checks=-*,modernize-*") | |
| 21 | 23 | |
| 22 | 24 | |
| 23 | 25 | find_package(SQLite3 REQUIRED) |
| @@ -26,12 +28,14 @@ include_directories(${CMAKE_SOURCE_DIR}/include) | ||
| 26 | 28 | |
| 27 | 29 | add_executable(fluffy src/main.cpp) |
| 28 | 30 | |
| 31 | + | |
| 29 | 32 | target_link_libraries(fluffy |
| 30 | 33 | PRIVATE ftxui::screen |
| 31 | 34 | PRIVATE ftxui::dom |
| 32 | 35 | PRIVATE ftxui::component |
| 33 | 36 | PRIVATE SQLite::SQLite3 |
| 34 | 37 | ) |
| 38 | +set_target_properties(fluffy PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}") | |
| 35 | 39 | |
| 36 | 40 | if (EMSCRIPTEN) |
| 37 | 41 | string(APPEND CMAKE_CXX_FLAGS " -s USE_PTHREADS") |
| @@ -8,7 +8,7 @@ include(FetchContent) | |||
| 8 | set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE) | 8 | set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE) |
| 9 | FetchContent_Declare(ftxui | 9 | FetchContent_Declare(ftxui |
| 10 | GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui | 10 | GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui |
| 11 | - GIT_TAG v4.0.0 | 11 | + GIT_TAG v5.0.0 |
| 12 | ) | 12 | ) |
| 13 | FetchContent_MakeAvailable(ftxui) | 13 | FetchContent_MakeAvailable(ftxui) |
| 14 | if(NOT ftxui_POPULATED) | 14 | if(NOT ftxui_POPULATED) |
| @@ -18,6 +18,8 @@ endif() | |||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | +find_program(CLANG_TIDY NAMES "clang-tidy" REQUIRED) | ||
| 22 | +set(CLANG_TIDY_COMMAND "${CLANG_TIDY}" "-checks=-*,modernize-*") | ||
| 21 | 23 | ||
| 22 | 24 | ||
| 23 | find_package(SQLite3 REQUIRED) | 25 | find_package(SQLite3 REQUIRED) |
| @@ -26,12 +28,14 @@ include_directories(${CMAKE_SOURCE_DIR}/include) | |||
| 26 | 28 | ||
| 27 | add_executable(fluffy src/main.cpp) | 29 | add_executable(fluffy src/main.cpp) |
| 28 | 30 | ||
| 31 | + | ||
| 29 | target_link_libraries(fluffy | 32 | target_link_libraries(fluffy |
| 30 | PRIVATE ftxui::screen | 33 | PRIVATE ftxui::screen |
| 31 | PRIVATE ftxui::dom | 34 | PRIVATE ftxui::dom |
| 32 | PRIVATE ftxui::component | 35 | PRIVATE ftxui::component |
| 33 | PRIVATE SQLite::SQLite3 | 36 | PRIVATE SQLite::SQLite3 |
| 34 | ) | 37 | ) |
| 38 | +set_target_properties(fluffy PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}") | ||
| 35 | 39 | ||
| 36 | if (EMSCRIPTEN) | 40 | if (EMSCRIPTEN) |
| 37 | string(APPEND CMAKE_CXX_FLAGS " -s USE_PTHREADS") | 41 | string(APPEND CMAKE_CXX_FLAGS " -s USE_PTHREADS") |
added
include/game.hpp +19 -0 | new file mode 100644 | ||
| @@ -0,0 +1,19 @@ | ||
| 1 | +#pragma once | |
| 2 | + | |
| 3 | +#include <string> | |
| 4 | + | |
| 5 | +class Game { | |
| 6 | +private: | |
| 7 | + int id; | |
| 8 | + std::string name; | |
| 9 | + std::string path; | |
| 10 | + | |
| 11 | +public: | |
| 12 | + Game(int id, std::string name, std::string path) | |
| 13 | + : id{id}, name{name}, path{path} {} | |
| 14 | + ~Game() {} | |
| 15 | + | |
| 16 | + int getId() { return id; } | |
| 17 | + std::string getName() { return name; } | |
| 18 | + std::string getPath() { return path; } | |
| 19 | +}; | |
| new file mode 100644 | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | +#pragma once | ||
| 2 | + | ||
| 3 | +#include <string> | ||
| 4 | + | ||
| 5 | +class Game { | ||
| 6 | +private: | ||
| 7 | + int id; | ||
| 8 | + std::string name; | ||
| 9 | + std::string path; | ||
| 10 | + | ||
| 11 | +public: | ||
| 12 | + Game(int id, std::string name, std::string path) | ||
| 13 | + : id{id}, name{name}, path{path} {} | ||
| 14 | + ~Game() {} | ||
| 15 | + | ||
| 16 | + int getId() { return id; } | ||
| 17 | + std::string getName() { return name; } | ||
| 18 | + std::string getPath() { return path; } | ||
| 19 | +}; | ||
modified
src/main.cpp +176 -165 | @@ -1,208 +1,219 @@ | ||
| 1 | -#include "ftxui/dom/elements.hpp" | |
| 2 | -#include "ftxui/screen/screen.hpp" | |
| 3 | -#include "ftxui/screen/string.hpp" | |
| 4 | 1 | #include "ftxui/component/component.hpp" |
| 2 | +#include "ftxui/component/component_base.hpp" | |
| 5 | 3 | #include "ftxui/component/component_options.hpp" |
| 6 | 4 | #include "ftxui/component/screen_interactive.hpp" |
| 5 | +#include "ftxui/dom/elements.hpp" | |
| 6 | +#include "ftxui/screen/screen.hpp" | |
| 7 | +#include "ftxui/screen/string.hpp" | |
| 7 | 8 | |
| 8 | -#include <iostream> | |
| 9 | -#include <string> | |
| 10 | 9 | #include <cstdlib> |
| 11 | 10 | #include <filesystem> |
| 12 | -#include <sqlite3.h> | |
| 11 | +#include <iostream> | |
| 13 | 12 | #include <map> |
| 13 | +#include <sqlite3.h> | |
| 14 | +#include <string> | |
| 15 | +#include <vector> | |
| 14 | 16 | |
| 17 | +#include <game.hpp> | |
| 15 | 18 | |
| 16 | 19 | using namespace ftxui; |
| 17 | 20 | |
| 18 | -std::map<std::string, std::string> games; | |
| 21 | +std::vector<Game> games; | |
| 19 | 22 | |
| 20 | -std::string database_initialization_command = "CREATE TABLE GAME(" | |
| 21 | - "GAME_ID INT PRIMARY KEY NOT NULL, " | |
| 22 | - "GAME_NAME TEXT NOT NULL, " | |
| 23 | - "GAME_PATH TEXT NOT NULL);"; | |
| 23 | +std::string database_initialization_command = | |
| 24 | + "CREATE TABLE games(" | |
| 25 | + "GAME_ID INTEGER PRIMARY KEY AUTOINCREMENT, " | |
| 26 | + "GAME_NAME TEXT NOT NULL, " | |
| 27 | + "GAME_PATH TEXT NOT NULL);"; | |
| 24 | 28 | |
| 25 | -const char* select_all_games_command = "SELECT * FROM GAME;"; | |
| 29 | +std::string select_all_games_command = "SELECT * FROM games;"; | |
| 26 | 30 | |
| 27 | 31 | bool debugRun = true; |
| 28 | 32 | |
| 29 | 33 | void printMessage(std::string message) { |
| 30 | - if (debugRun) { | |
| 31 | - std::cout << message << std::endl; | |
| 32 | - } | |
| 34 | + if (debugRun) { | |
| 35 | + std::cout << message << std::endl; | |
| 36 | + } | |
| 33 | 37 | } |
| 34 | 38 | |
| 35 | -void execute_sql_command(sqlite3* database, std::string command) { | |
| 36 | - int exit = 0; | |
| 37 | - char* messageError; | |
| 38 | - exit = sqlite3_exec(database, command.c_str(), NULL, 0, &messageError); | |
| 39 | - | |
| 40 | - if (exit != SQLITE_OK) { | |
| 41 | - printMessage("Error with command: " + command + " " + messageError); | |
| 42 | - sqlite3_free(messageError); | |
| 43 | - } | |
| 39 | +void execute_sql_command(sqlite3 *database, std::string command) { | |
| 40 | + int exit = 0; | |
| 41 | + char *messageError; | |
| 42 | + exit = sqlite3_exec(database, command.c_str(), nullptr, nullptr, &messageError); | |
| 43 | + | |
| 44 | + if (exit != SQLITE_OK) { | |
| 45 | + printMessage("Error with command: " + command + " " + messageError); | |
| 46 | + sqlite3_free(messageError); | |
| 47 | + } | |
| 44 | 48 | } |
| 45 | 49 | |
| 46 | -void addGame(sqlite3* database, int id, std::string name, std::string path) { | |
| 47 | - std::string command = "INSERT INTO GAME (GAME_ID, GAME_NAME, GAME_PATH) VALUES ("; | |
| 48 | - command += std::to_string(id) + ", '" + name + "', \"" + path + "\");"; | |
| 49 | - execute_sql_command(database, command); | |
| 50 | +void addGame(sqlite3 *database, std::string name, std::string path) { | |
| 51 | + std::string command = | |
| 52 | + "INSERT INTO games (GAME_NAME, GAME_PATH) VALUES ("; | |
| 53 | + command += "'" + name + "', \"" + path + "\");"; | |
| 54 | + execute_sql_command(database, command); | |
| 50 | 55 | } |
| 51 | 56 | |
| 52 | -void removeGame(sqlite3* database, std::string name) { | |
| 53 | - std::string command = "DELETE FROM GAME WHERE GAME_NAME="; | |
| 54 | - command += "'" + name + "';"; | |
| 55 | - execute_sql_command(database, command); | |
| 57 | +void removeGame(sqlite3 *database, std::string name) { | |
| 58 | + std::string command = "DELETE FROM games WHERE GAME_NAME="; | |
| 59 | + command += "'" + name + "';"; | |
| 60 | + execute_sql_command(database, command); | |
| 56 | 61 | } |
| 57 | 62 | |
| 58 | -void initialize_database(sqlite3* database) { | |
| 59 | - execute_sql_command(database, database_initialization_command); | |
| 60 | - addGame(database, 1, "Devastating Fog", ""); | |
| 61 | - addGame(database, 2, "No Pirates Here!", ""); | |
| 62 | - printMessage("Database Initialized"); | |
| 63 | +void initialize_database(sqlite3 *database) { | |
| 64 | + execute_sql_command(database, database_initialization_command); | |
| 65 | + printMessage("Database Initialized"); | |
| 63 | 66 | } |
| 64 | 67 | |
| 65 | -void runGame(sqlite3* database, std::string gameName) { | |
| 66 | - std::string pathToRun = ""; | |
| 67 | - for (const auto& [name, path] : games) { | |
| 68 | - if (name == gameName) { | |
| 69 | - pathToRun = path; | |
| 70 | - } | |
| 68 | +void runGame(sqlite3 *database, std::string gameName) { | |
| 69 | + std::string pathToRun = ""; | |
| 70 | + for (auto &game : games) { | |
| 71 | + if (gameName == game.getName()) { | |
| 72 | + pathToRun = game.getPath(); | |
| 71 | 73 | } |
| 72 | - system(pathToRun.c_str()); | |
| 74 | + } | |
| 75 | + system(pathToRun.c_str()); | |
| 73 | 76 | } |
| 74 | 77 | |
| 75 | -void handleCommand(int argc, char const *argv[], sqlite3* database) { | |
| 76 | - std::string command{}; | |
| 77 | - switch (argc) { | |
| 78 | - case 2: | |
| 79 | - command = argv[1]; | |
| 80 | - if (command == "show") { | |
| 81 | - for (const auto& [game, path] : games) { | |
| 82 | - std::cout << game << ": " << path << std::endl; | |
| 83 | - } | |
| 84 | - } | |
| 85 | - break; | |
| 86 | - case 3: | |
| 87 | - command = argv[1]; | |
| 88 | - if (command == "rm") { | |
| 89 | - std::string gameName{ argv[2] }; | |
| 90 | - removeGame(database, gameName); | |
| 91 | - } | |
| 92 | - else if(command == "play") { | |
| 93 | - std::string gameName{ argv[2] }; | |
| 94 | - runGame(database, gameName); | |
| 95 | - } | |
| 96 | - case 4: | |
| 97 | - command = argv[1]; | |
| 98 | - if (command == "add") { | |
| 99 | - std::string gameName{ argv[2] }; | |
| 100 | - auto localPath = std::filesystem::path(argv[3]); | |
| 101 | - std::string globalPath = std::filesystem::absolute(localPath).lexically_normal().string(); | |
| 102 | - | |
| 103 | - std::string temp{}; | |
| 104 | - for (const auto a : globalPath) { | |
| 105 | - if (a != ' ') { | |
| 106 | - temp += a; | |
| 107 | - } | |
| 108 | - else { | |
| 109 | - temp += "\' \'"; | |
| 110 | - } | |
| 111 | - } | |
| 112 | - | |
| 113 | - addGame(database, games.size() + 1, gameName, temp); | |
| 114 | - } | |
| 115 | - break; | |
| 78 | +void handleCommand(int argc, char const *argv[], sqlite3 *database) { | |
| 79 | + std::string command{}; | |
| 80 | + switch (argc) { | |
| 81 | + case 2: | |
| 82 | + command = argv[1]; | |
| 83 | + if (command == "show") { | |
| 84 | + for (auto &game : games) { | |
| 85 | + std::cout << game.getName() << ": " << game.getPath() << std::endl; | |
| 86 | + } | |
| 116 | 87 | } |
| 117 | -} | |
| 88 | + break; | |
| 89 | + case 3: | |
| 90 | + command = argv[1]; | |
| 91 | + if (command == "rm") { | |
| 92 | + std::string gameName{argv[2]}; | |
| 93 | + removeGame(database, gameName); | |
| 94 | + } else if (command == "play") { | |
| 95 | + std::string gameName{argv[2]}; | |
| 96 | + runGame(database, gameName); | |
| 97 | + } | |
| 98 | + case 4: | |
| 99 | + command = argv[1]; | |
| 100 | + if (command == "add") { | |
| 101 | + std::string gameName{argv[2]}; | |
| 102 | + auto localPath = std::filesystem::path(argv[3]); | |
| 103 | + std::string globalPath = | |
| 104 | + std::filesystem::absolute(localPath).lexically_normal().string(); | |
| 105 | + | |
| 106 | + std::string temp{}; | |
| 107 | + for (const auto a : globalPath) { | |
| 108 | + if (a != ' ') { | |
| 109 | + temp += a; | |
| 110 | + } else { | |
| 111 | + temp += "\' \'"; | |
| 112 | + } | |
| 113 | + } | |
| 118 | 114 | |
| 119 | -int main(int argc, char const *argv[]) { | |
| 120 | - bool newCreation = not std::filesystem::exists("data.db"); | |
| 115 | + addGame(database, gameName, temp); | |
| 116 | + } | |
| 117 | + break; | |
| 118 | + } | |
| 119 | +} | |
| 121 | 120 | |
| 122 | - char* messageError; | |
| 123 | - sqlite3* database; | |
| 124 | - sqlite3_stmt* stmt; | |
| 125 | - int exit = 0; | |
| 126 | - exit = sqlite3_open("data.db", &database); | |
| 121 | +auto Style() -> ButtonOption { | |
| 122 | + auto option = ButtonOption::Border(); | |
| 123 | + option.transform = [](const EntryState &s) { | |
| 124 | + auto element = text(s.label); | |
| 125 | + if (s.focused) { | |
| 126 | + element |= bold; | |
| 127 | + } | |
| 128 | + return element | size(HEIGHT, LESS_THAN, 10); | |
| 129 | + }; | |
| 130 | + return option; | |
| 131 | +} | |
| 127 | 132 | |
| 128 | - if (exit) { | |
| 129 | - std::cerr << "Error opening database" << sqlite3_errmsg(database) << std::endl; | |
| 130 | - return -1; | |
| 131 | - } | |
| 132 | - | |
| 133 | - std::cout << "Opened database successfully!" << std::endl; | |
| 133 | +auto runConsoleApp(sqlite3* database) -> void { | |
| 134 | + system("clear"); | |
| 135 | + | |
| 136 | + std::vector<std::string> localGames = {}; | |
| 137 | + for (auto &game : games) { | |
| 138 | + localGames.emplace_back(game.getName()); | |
| 139 | + } | |
| 140 | + localGames.emplace_back("Exit"); | |
| 141 | + | |
| 142 | + auto gamesContainer{Container::Vertical({})}; | |
| 143 | + for (auto &game : games) { | |
| 144 | + gamesContainer->Add(Button( | |
| 145 | + game.getName(), [&] { runGame(database, game.getName()); }, Style())); | |
| 146 | + } | |
| 147 | + | |
| 148 | + auto menu_screen = ScreenInteractive::TerminalOutput(); | |
| 149 | + | |
| 150 | + auto quitButton = Button("Exit", menu_screen.ExitLoopClosure(), Style()); | |
| 151 | + gamesContainer->Add(quitButton); | |
| 152 | + | |
| 153 | + int menuOptionSelected{0}; | |
| 154 | + auto appContainer = | |
| 155 | + Container::Tab({gamesContainer}, &menuOptionSelected); | |
| 156 | + | |
| 157 | + auto container = Container::Horizontal({ | |
| 158 | + appContainer, | |
| 159 | + }); | |
| 160 | + auto renderer = Renderer(container, [&] { | |
| 161 | + return vbox({ | |
| 162 | + center(text(L" Fluffy v0.1 ")), | |
| 163 | + appContainer->Render(), | |
| 164 | + }) | | |
| 165 | + border | size(WIDTH, LESS_THAN, 120); | |
| 166 | + }); | |
| 167 | + menu_screen.Loop(renderer); | |
| 134 | 168 | |
| 135 | - if (newCreation) { | |
| 136 | - initialize_database(database); | |
| 137 | - } | |
| 169 | +} | |
| 138 | 170 | |
| 139 | - exit = sqlite3_prepare_v2(database, select_all_games_command, -1, &stmt, 0); | |
| 171 | +auto main(int argc, char const *argv[]) -> int { | |
| 172 | + bool newCreation = not std::filesystem::exists("data.db"); | |
| 173 | + char *messageError; | |
| 174 | + sqlite3 *database; | |
| 175 | + sqlite3_stmt *stmt; | |
| 176 | + int exit = 0; | |
| 177 | + exit = sqlite3_open("data.db", &database); | |
| 140 | 178 | |
| 141 | - if (exit) { | |
| 142 | - std::cerr << "Error retrieving data" << sqlite3_errmsg(database) << std::endl; | |
| 143 | - sqlite3_close(database); | |
| 144 | - return -1; | |
| 145 | - } | |
| 179 | + if (exit) { | |
| 180 | + std::cerr << "Error opening database" << sqlite3_errmsg(database) | |
| 181 | + << std::endl; | |
| 182 | + return -1; | |
| 183 | + } | |
| 146 | 184 | |
| 147 | - while (sqlite3_step(stmt) == SQLITE_ROW) { | |
| 148 | - int id = sqlite3_column_int(stmt, 0); | |
| 149 | - const unsigned char* gameName = sqlite3_column_text(stmt, 1); | |
| 150 | - const unsigned char* gamePath = sqlite3_column_text(stmt, 2); | |
| 185 | + printMessage("Opened database successfully!"); | |
| 151 | 186 | |
| 152 | - std::string name{reinterpret_cast<const char*>(gameName)}; | |
| 153 | - std::string path{reinterpret_cast<const char*>(gamePath)}; | |
| 154 | - games[name] = path; | |
| 155 | - } | |
| 156 | - sqlite3_finalize(stmt); | |
| 187 | + if (newCreation) { | |
| 188 | + initialize_database(database); | |
| 189 | + } | |
| 157 | 190 | |
| 158 | - if (argc > 1) { | |
| 159 | - handleCommand(argc, argv, database); | |
| 160 | - return 0; | |
| 161 | - } | |
| 191 | + exit = sqlite3_prepare_v2(database, select_all_games_command.c_str(), -1, &stmt, nullptr); | |
| 162 | 192 | |
| 163 | - int exit_id = games.size(); | |
| 164 | - int choice = 0; | |
| 165 | - do { | |
| 166 | - system("clear"); | |
| 167 | - auto document = vbox({ | |
| 168 | - center(text(L" Fluffy v0.1 ")), | |
| 169 | - }); | |
| 170 | - | |
| 171 | - document = document | size(WIDTH, LESS_THAN, 120); | |
| 172 | - | |
| 173 | - auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); | |
| 174 | - Render(screen, document); | |
| 175 | - | |
| 176 | - std::cout << screen.ToString() << '\0' << std::endl; | |
| 177 | - | |
| 178 | - | |
| 179 | - auto menu_screen = ScreenInteractive::TerminalOutput(); | |
| 180 | - | |
| 181 | - std::vector<std::string> localGames = {}; | |
| 182 | - for (const auto& [name, path] : games) { | |
| 183 | - localGames.push_back(name); | |
| 184 | - } | |
| 185 | - localGames.push_back("Exit"); | |
| 186 | - | |
| 187 | - MenuOption option; | |
| 188 | - option.on_enter = menu_screen.ExitLoopClosure(); | |
| 189 | - auto menu = Menu(&localGames, &choice, option); | |
| 190 | - menu_screen.Loop(menu); | |
| 191 | - | |
| 192 | - if (choice != exit_id) { | |
| 193 | - int i{ 0 }; | |
| 194 | - std::string gameToRun = ""; | |
| 195 | - for (const auto& [name, path] : games) { | |
| 196 | - | |
| 197 | - if (i == choice) { | |
| 198 | - gameToRun = name; | |
| 199 | - | |
| 200 | - } | |
| 201 | - i++; | |
| 202 | - } | |
| 203 | - runGame(database, gameToRun); | |
| 204 | - } | |
| 205 | - } while (choice != exit_id); | |
| 193 | + if (exit) { | |
| 194 | + std::cerr << "Error retrieving data" << sqlite3_errmsg(database) | |
| 195 | + << std::endl; | |
| 206 | 196 | sqlite3_close(database); |
| 207 | - return EXIT_SUCCESS; | |
| 197 | + return -1; | |
| 198 | + } | |
| 199 | + | |
| 200 | + while (sqlite3_step(stmt) == SQLITE_ROW) { | |
| 201 | + int id = sqlite3_column_int(stmt, 0); | |
| 202 | + const unsigned char *gameName = sqlite3_column_text(stmt, 1); | |
| 203 | + const unsigned char *gamePath = sqlite3_column_text(stmt, 2); | |
| 204 | + | |
| 205 | + games.emplace_back(Game{id, reinterpret_cast<const char *>(gameName), | |
| 206 | + reinterpret_cast<const char *>(gamePath)}); | |
| 207 | + } | |
| 208 | + sqlite3_finalize(stmt); | |
| 209 | + | |
| 210 | + if (argc > 1) { | |
| 211 | + handleCommand(argc, argv, database); | |
| 212 | + } | |
| 213 | + else { | |
| 214 | + runConsoleApp(database); | |
| 215 | + } | |
| 216 | + | |
| 217 | + sqlite3_close(database); | |
| 218 | + return EXIT_SUCCESS; | |
| 208 | 219 | } |
| \ No newline at end of file | ||
| @@ -1,208 +1,219 @@ | |||
| 1 | -#include "ftxui/dom/elements.hpp" | ||
| 2 | -#include "ftxui/screen/screen.hpp" | ||
| 3 | -#include "ftxui/screen/string.hpp" | ||
| 4 | #include "ftxui/component/component.hpp" | 1 | #include "ftxui/component/component.hpp" |
| 2 | +#include "ftxui/component/component_base.hpp" | ||
| 5 | #include "ftxui/component/component_options.hpp" | 3 | #include "ftxui/component/component_options.hpp" |
| 6 | #include "ftxui/component/screen_interactive.hpp" | 4 | #include "ftxui/component/screen_interactive.hpp" |
| 5 | +#include "ftxui/dom/elements.hpp" | ||
| 6 | +#include "ftxui/screen/screen.hpp" | ||
| 7 | +#include "ftxui/screen/string.hpp" | ||
| 7 | 8 | ||
| 8 | -#include <iostream> | ||
| 9 | -#include <string> | ||
| 10 | #include <cstdlib> | 9 | #include <cstdlib> |
| 11 | #include <filesystem> | 10 | #include <filesystem> |
| 12 | -#include <sqlite3.h> | 11 | +#include <iostream> |
| 13 | #include <map> | 12 | #include <map> |
| 13 | +#include <sqlite3.h> | ||
| 14 | +#include <string> | ||
| 15 | +#include <vector> | ||
| 14 | 16 | ||
| 17 | +#include <game.hpp> | ||
| 15 | 18 | ||
| 16 | using namespace ftxui; | 19 | using namespace ftxui; |
| 17 | 20 | ||
| 18 | -std::map<std::string, std::string> games; | 21 | +std::vector<Game> games; |
| 19 | 22 | ||
| 20 | -std::string database_initialization_command = "CREATE TABLE GAME(" | 23 | +std::string database_initialization_command = |
| 21 | - "GAME_ID INT PRIMARY KEY NOT NULL, " | 24 | + "CREATE TABLE games(" |
| 22 | - "GAME_NAME TEXT NOT NULL, " | 25 | + "GAME_ID INTEGER PRIMARY KEY AUTOINCREMENT, " |
| 23 | - "GAME_PATH TEXT NOT NULL);"; | 26 | + "GAME_NAME TEXT NOT NULL, " |
| 27 | + "GAME_PATH TEXT NOT NULL);"; | ||
| 24 | 28 | ||
| 25 | -const char* select_all_games_command = "SELECT * FROM GAME;"; | 29 | +std::string select_all_games_command = "SELECT * FROM games;"; |
| 26 | 30 | ||
| 27 | bool debugRun = true; | 31 | bool debugRun = true; |
| 28 | 32 | ||
| 29 | void printMessage(std::string message) { | 33 | void printMessage(std::string message) { |
| 30 | - if (debugRun) { | 34 | + if (debugRun) { |
| 31 | - std::cout << message << std::endl; | 35 | + std::cout << message << std::endl; |
| 32 | - } | 36 | + } |
| 33 | } | 37 | } |
| 34 | 38 | ||
| 35 | -void execute_sql_command(sqlite3* database, std::string command) { | 39 | +void execute_sql_command(sqlite3 *database, std::string command) { |
| 36 | - int exit = 0; | 40 | + int exit = 0; |
| 37 | - char* messageError; | 41 | + char *messageError; |
| 38 | - exit = sqlite3_exec(database, command.c_str(), NULL, 0, &messageError); | 42 | + exit = sqlite3_exec(database, command.c_str(), nullptr, nullptr, &messageError); |
| 39 | - | 43 | + |
| 40 | - if (exit != SQLITE_OK) { | 44 | + if (exit != SQLITE_OK) { |
| 41 | - printMessage("Error with command: " + command + " " + messageError); | 45 | + printMessage("Error with command: " + command + " " + messageError); |
| 42 | - sqlite3_free(messageError); | 46 | + sqlite3_free(messageError); |
| 43 | - } | 47 | + } |
| 44 | } | 48 | } |
| 45 | 49 | ||
| 46 | -void addGame(sqlite3* database, int id, std::string name, std::string path) { | 50 | +void addGame(sqlite3 *database, std::string name, std::string path) { |
| 47 | - std::string command = "INSERT INTO GAME (GAME_ID, GAME_NAME, GAME_PATH) VALUES ("; | 51 | + std::string command = |
| 48 | - command += std::to_string(id) + ", '" + name + "', \"" + path + "\");"; | 52 | + "INSERT INTO games (GAME_NAME, GAME_PATH) VALUES ("; |
| 49 | - execute_sql_command(database, command); | 53 | + command += "'" + name + "', \"" + path + "\");"; |
| 54 | + execute_sql_command(database, command); | ||
| 50 | } | 55 | } |
| 51 | 56 | ||
| 52 | -void removeGame(sqlite3* database, std::string name) { | 57 | +void removeGame(sqlite3 *database, std::string name) { |
| 53 | - std::string command = "DELETE FROM GAME WHERE GAME_NAME="; | 58 | + std::string command = "DELETE FROM games WHERE GAME_NAME="; |
| 54 | - command += "'" + name + "';"; | 59 | + command += "'" + name + "';"; |
| 55 | - execute_sql_command(database, command); | 60 | + execute_sql_command(database, command); |
| 56 | } | 61 | } |
| 57 | 62 | ||
| 58 | -void initialize_database(sqlite3* database) { | 63 | +void initialize_database(sqlite3 *database) { |
| 59 | - execute_sql_command(database, database_initialization_command); | 64 | + execute_sql_command(database, database_initialization_command); |
| 60 | - addGame(database, 1, "Devastating Fog", ""); | 65 | + printMessage("Database Initialized"); |
| 61 | - addGame(database, 2, "No Pirates Here!", ""); | ||
| 62 | - printMessage("Database Initialized"); | ||
| 63 | } | 66 | } |
| 64 | 67 | ||
| 65 | -void runGame(sqlite3* database, std::string gameName) { | 68 | +void runGame(sqlite3 *database, std::string gameName) { |
| 66 | - std::string pathToRun = ""; | 69 | + std::string pathToRun = ""; |
| 67 | - for (const auto& [name, path] : games) { | 70 | + for (auto &game : games) { |
| 68 | - if (name == gameName) { | 71 | + if (gameName == game.getName()) { |
| 69 | - pathToRun = path; | 72 | + pathToRun = game.getPath(); |
| 70 | - } | ||
| 71 | } | 73 | } |
| 72 | - system(pathToRun.c_str()); | 74 | + } |
| 75 | + system(pathToRun.c_str()); | ||
| 73 | } | 76 | } |
| 74 | 77 | ||
| 75 | -void handleCommand(int argc, char const *argv[], sqlite3* database) { | 78 | +void handleCommand(int argc, char const *argv[], sqlite3 *database) { |
| 76 | - std::string command{}; | 79 | + std::string command{}; |
| 77 | - switch (argc) { | 80 | + switch (argc) { |
| 78 | - case 2: | 81 | + case 2: |
| 79 | - command = argv[1]; | 82 | + command = argv[1]; |
| 80 | - if (command == "show") { | 83 | + if (command == "show") { |
| 81 | - for (const auto& [game, path] : games) { | 84 | + for (auto &game : games) { |
| 82 | - std::cout << game << ": " << path << std::endl; | 85 | + std::cout << game.getName() << ": " << game.getPath() << std::endl; |
| 83 | - } | 86 | + } |
| 84 | - } | ||
| 85 | - break; | ||
| 86 | - case 3: | ||
| 87 | - command = argv[1]; | ||
| 88 | - if (command == "rm") { | ||
| 89 | - std::string gameName{ argv[2] }; | ||
| 90 | - removeGame(database, gameName); | ||
| 91 | - } | ||
| 92 | - else if(command == "play") { | ||
| 93 | - std::string gameName{ argv[2] }; | ||
| 94 | - runGame(database, gameName); | ||
| 95 | - } | ||
| 96 | - case 4: | ||
| 97 | - command = argv[1]; | ||
| 98 | - if (command == "add") { | ||
| 99 | - std::string gameName{ argv[2] }; | ||
| 100 | - auto localPath = std::filesystem::path(argv[3]); | ||
| 101 | - std::string globalPath = std::filesystem::absolute(localPath).lexically_normal().string(); | ||
| 102 | - | ||
| 103 | - std::string temp{}; | ||
| 104 | - for (const auto a : globalPath) { | ||
| 105 | - if (a != ' ') { | ||
| 106 | - temp += a; | ||
| 107 | - } | ||
| 108 | - else { | ||
| 109 | - temp += "\' \'"; | ||
| 110 | - } | ||
| 111 | - } | ||
| 112 | - | ||
| 113 | - addGame(database, games.size() + 1, gameName, temp); | ||
| 114 | - } | ||
| 115 | - break; | ||
| 116 | } | 87 | } |
| 117 | -} | 88 | + break; |
| 89 | + case 3: | ||
| 90 | + command = argv[1]; | ||
| 91 | + if (command == "rm") { | ||
| 92 | + std::string gameName{argv[2]}; | ||
| 93 | + removeGame(database, gameName); | ||
| 94 | + } else if (command == "play") { | ||
| 95 | + std::string gameName{argv[2]}; | ||
| 96 | + runGame(database, gameName); | ||
| 97 | + } | ||
| 98 | + case 4: | ||
| 99 | + command = argv[1]; | ||
| 100 | + if (command == "add") { | ||
| 101 | + std::string gameName{argv[2]}; | ||
| 102 | + auto localPath = std::filesystem::path(argv[3]); | ||
| 103 | + std::string globalPath = | ||
| 104 | + std::filesystem::absolute(localPath).lexically_normal().string(); | ||
| 105 | + | ||
| 106 | + std::string temp{}; | ||
| 107 | + for (const auto a : globalPath) { | ||
| 108 | + if (a != ' ') { | ||
| 109 | + temp += a; | ||
| 110 | + } else { | ||
| 111 | + temp += "\' \'"; | ||
| 112 | + } | ||
| 113 | + } | ||
| 118 | 114 | ||
| 119 | -int main(int argc, char const *argv[]) { | 115 | + addGame(database, gameName, temp); |
| 120 | - bool newCreation = not std::filesystem::exists("data.db"); | 116 | + } |
| 117 | + break; | ||
| 118 | + } | ||
| 119 | +} | ||
| 121 | 120 | ||
| 122 | - char* messageError; | 121 | +auto Style() -> ButtonOption { |
| 123 | - sqlite3* database; | 122 | + auto option = ButtonOption::Border(); |
| 124 | - sqlite3_stmt* stmt; | 123 | + option.transform = [](const EntryState &s) { |
| 125 | - int exit = 0; | 124 | + auto element = text(s.label); |
| 126 | - exit = sqlite3_open("data.db", &database); | 125 | + if (s.focused) { |
| 126 | + element |= bold; | ||
| 127 | + } | ||
| 128 | + return element | size(HEIGHT, LESS_THAN, 10); | ||
| 129 | + }; | ||
| 130 | + return option; | ||
| 131 | +} | ||
| 127 | 132 | ||
| 128 | - if (exit) { | 133 | +auto runConsoleApp(sqlite3* database) -> void { |
| 129 | - std::cerr << "Error opening database" << sqlite3_errmsg(database) << std::endl; | 134 | + system("clear"); |
| 130 | - return -1; | 135 | + |
| 131 | - } | 136 | + std::vector<std::string> localGames = {}; |
| 132 | - | 137 | + for (auto &game : games) { |
| 133 | - std::cout << "Opened database successfully!" << std::endl; | 138 | + localGames.emplace_back(game.getName()); |
| 139 | + } | ||
| 140 | + localGames.emplace_back("Exit"); | ||
| 141 | + | ||
| 142 | + auto gamesContainer{Container::Vertical({})}; | ||
| 143 | + for (auto &game : games) { | ||
| 144 | + gamesContainer->Add(Button( | ||
| 145 | + game.getName(), [&] { runGame(database, game.getName()); }, Style())); | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + auto menu_screen = ScreenInteractive::TerminalOutput(); | ||
| 149 | + | ||
| 150 | + auto quitButton = Button("Exit", menu_screen.ExitLoopClosure(), Style()); | ||
| 151 | + gamesContainer->Add(quitButton); | ||
| 152 | + | ||
| 153 | + int menuOptionSelected{0}; | ||
| 154 | + auto appContainer = | ||
| 155 | + Container::Tab({gamesContainer}, &menuOptionSelected); | ||
| 156 | + | ||
| 157 | + auto container = Container::Horizontal({ | ||
| 158 | + appContainer, | ||
| 159 | + }); | ||
| 160 | + auto renderer = Renderer(container, [&] { | ||
| 161 | + return vbox({ | ||
| 162 | + center(text(L" Fluffy v0.1 ")), | ||
| 163 | + appContainer->Render(), | ||
| 164 | + }) | | ||
| 165 | + border | size(WIDTH, LESS_THAN, 120); | ||
| 166 | + }); | ||
| 167 | + menu_screen.Loop(renderer); | ||
| 134 | 168 | ||
| 135 | - if (newCreation) { | 169 | +} |
| 136 | - initialize_database(database); | ||
| 137 | - } | ||
| 138 | 170 | ||
| 139 | - exit = sqlite3_prepare_v2(database, select_all_games_command, -1, &stmt, 0); | 171 | +auto main(int argc, char const *argv[]) -> int { |
| 172 | + bool newCreation = not std::filesystem::exists("data.db"); | ||
| 173 | + char *messageError; | ||
| 174 | + sqlite3 *database; | ||
| 175 | + sqlite3_stmt *stmt; | ||
| 176 | + int exit = 0; | ||
| 177 | + exit = sqlite3_open("data.db", &database); | ||
| 140 | 178 | ||
| 141 | - if (exit) { | 179 | + if (exit) { |
| 142 | - std::cerr << "Error retrieving data" << sqlite3_errmsg(database) << std::endl; | 180 | + std::cerr << "Error opening database" << sqlite3_errmsg(database) |
| 143 | - sqlite3_close(database); | 181 | + << std::endl; |
| 144 | - return -1; | 182 | + return -1; |
| 145 | - } | 183 | + } |
| 146 | 184 | ||
| 147 | - while (sqlite3_step(stmt) == SQLITE_ROW) { | 185 | + printMessage("Opened database successfully!"); |
| 148 | - int id = sqlite3_column_int(stmt, 0); | ||
| 149 | - const unsigned char* gameName = sqlite3_column_text(stmt, 1); | ||
| 150 | - const unsigned char* gamePath = sqlite3_column_text(stmt, 2); | ||
| 151 | 186 | ||
| 152 | - std::string name{reinterpret_cast<const char*>(gameName)}; | 187 | + if (newCreation) { |
| 153 | - std::string path{reinterpret_cast<const char*>(gamePath)}; | 188 | + initialize_database(database); |
| 154 | - games[name] = path; | 189 | + } |
| 155 | - } | ||
| 156 | - sqlite3_finalize(stmt); | ||
| 157 | 190 | ||
| 158 | - if (argc > 1) { | 191 | + exit = sqlite3_prepare_v2(database, select_all_games_command.c_str(), -1, &stmt, nullptr); |
| 159 | - handleCommand(argc, argv, database); | ||
| 160 | - return 0; | ||
| 161 | - } | ||
| 162 | 192 | ||
| 163 | - int exit_id = games.size(); | 193 | + if (exit) { |
| 164 | - int choice = 0; | 194 | + std::cerr << "Error retrieving data" << sqlite3_errmsg(database) |
| 165 | - do { | 195 | + << std::endl; |
| 166 | - system("clear"); | ||
| 167 | - auto document = vbox({ | ||
| 168 | - center(text(L" Fluffy v0.1 ")), | ||
| 169 | - }); | ||
| 170 | - | ||
| 171 | - document = document | size(WIDTH, LESS_THAN, 120); | ||
| 172 | - | ||
| 173 | - auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); | ||
| 174 | - Render(screen, document); | ||
| 175 | - | ||
| 176 | - std::cout << screen.ToString() << '\0' << std::endl; | ||
| 177 | - | ||
| 178 | - | ||
| 179 | - auto menu_screen = ScreenInteractive::TerminalOutput(); | ||
| 180 | - | ||
| 181 | - std::vector<std::string> localGames = {}; | ||
| 182 | - for (const auto& [name, path] : games) { | ||
| 183 | - localGames.push_back(name); | ||
| 184 | - } | ||
| 185 | - localGames.push_back("Exit"); | ||
| 186 | - | ||
| 187 | - MenuOption option; | ||
| 188 | - option.on_enter = menu_screen.ExitLoopClosure(); | ||
| 189 | - auto menu = Menu(&localGames, &choice, option); | ||
| 190 | - menu_screen.Loop(menu); | ||
| 191 | - | ||
| 192 | - if (choice != exit_id) { | ||
| 193 | - int i{ 0 }; | ||
| 194 | - std::string gameToRun = ""; | ||
| 195 | - for (const auto& [name, path] : games) { | ||
| 196 | - | ||
| 197 | - if (i == choice) { | ||
| 198 | - gameToRun = name; | ||
| 199 | - | ||
| 200 | - } | ||
| 201 | - i++; | ||
| 202 | - } | ||
| 203 | - runGame(database, gameToRun); | ||
| 204 | - } | ||
| 205 | - } while (choice != exit_id); | ||
| 206 | sqlite3_close(database); | 196 | sqlite3_close(database); |
| 207 | - return EXIT_SUCCESS; | 197 | + return -1; |
| 198 | + } | ||
| 199 | + | ||
| 200 | + while (sqlite3_step(stmt) == SQLITE_ROW) { | ||
| 201 | + int id = sqlite3_column_int(stmt, 0); | ||
| 202 | + const unsigned char *gameName = sqlite3_column_text(stmt, 1); | ||
| 203 | + const unsigned char *gamePath = sqlite3_column_text(stmt, 2); | ||
| 204 | + | ||
| 205 | + games.emplace_back(Game{id, reinterpret_cast<const char *>(gameName), | ||
| 206 | + reinterpret_cast<const char *>(gamePath)}); | ||
| 207 | + } | ||
| 208 | + sqlite3_finalize(stmt); | ||
| 209 | + | ||
| 210 | + if (argc > 1) { | ||
| 211 | + handleCommand(argc, argv, database); | ||
| 212 | + } | ||
| 213 | + else { | ||
| 214 | + runConsoleApp(database); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + sqlite3_close(database); | ||
| 218 | + return EXIT_SUCCESS; | ||
| 208 | } | 219 | } |
| \ No newline at end of file | \ No newline at end of file | ||