diff --git a/CMakeLists.txt b/CMakeLists.txt index 2003e5a..3556932 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ set(LUNARIUM_SRC "src/input/keyboard.cpp" "src/input/inputManager.cpp" "src/gui/gui.cpp" +"src/gui/fileBrowser.cpp" "src/gui/logGui.cpp" "src/gui/luaConsole.cpp" "src/scripting/scriptManager.cpp" diff --git a/docs/core.todo b/docs/core.todo index 495d2ac..4c9c36e 100644 --- a/docs/core.todo +++ b/docs/core.todo @@ -26,12 +26,18 @@ Core: ✔ Add Roboto-Regular.ttf as an internal font @high @done (11/3/2021, 8:35:51 PM) - GUI: - ✔ Dear ImGui class with basic initialization @done (9/10/2021, 1:42:19 PM) - ✔ Debug log window @done (9/10/2021, 4:44:48 PM) - ✔ Add key to show debug log window @done (9/13/2021, 6:47:44 PM) - ☐ Add checkboxes to disable log categories and levels - ✔ Add LUA Console window @done (10/26/2021, 4:43:41 PM) + GUI: + ✔ Dear ImGui class with basic initialization @done (9/10/2021, 1:42:19 PM) + ✔ Debug log window @done (9/10/2021, 4:44:48 PM) + ✔ Add key to show debug log window @done (9/13/2021, 6:47:44 PM) + ☐ Add checkboxes to disable log categories and levels + ✔ Add LUA Console window @done (10/26/2021, 4:43:41 PM) + FileBrowser: + ✔ Allow opening of listed directories @done (11/8/2021, 3:16:26 PM) + ✔ Add indication that an item is directory @done (11/8/2021, 6:19:20 PM) + ✔ Sort items by type (Directories should come first) @done (11/8/2021, 6:26:01 PM) + ☐ Allow the user to type in a filename + ✔ Add a "New Directory" button @done (11/8/2021, 7:15:51 PM) Input: ✔ Port over the Element2D input system and adjust it to use glfw @done (9/8/2021, 8:20:07 PM) @@ -59,6 +65,7 @@ Core: Utils: ✔ Make Logger fully static (no need to ever GetInstance) @done (10/26/2021, 4:43:55 PM) ✔ Need to add a static initialize method @done (10/26/2021, 4:43:57 PM) + ☐ Add a templated return value to the OK variant of OpRes @low Assets: diff --git a/docs/project_structure.txt b/docs/project_structure.txt index 61dee2c..c25bd7c 100644 --- a/docs/project_structure.txt +++ b/docs/project_structure.txt @@ -4,7 +4,7 @@ The root project folder needs to contain a project.xml file that describes the p Project directory structure (this should be auto-generated by the editor when creating a new project): Project root - ├── project.xml + ├── project.lproj (this is actually an xml file but the lproj extension allows it to be identified as a project file without opening it) ├── engine/ │ └── Lunarium_NE.exe (non-editor version of the engine. Used for creating a release build of the project) │ diff --git a/src/gui/fileBrowser.cpp b/src/gui/fileBrowser.cpp new file mode 100644 index 0000000..627a6df --- /dev/null +++ b/src/gui/fileBrowser.cpp @@ -0,0 +1,273 @@ +/****************************************************************************** +* File - fileBrowser.cpp +* Author - Joey Pollack +* Date - 2021/11/04 (y/m/d) +* Mod Date - 2021/11/04 (y/m/d) +* Description - File browser dialog window. Can be used for opening +* and saving files. +******************************************************************************/ + +#include "fileBrowser.h" + +#include +#include +#include +#include +#include + +namespace lunarium +{ + FileBrowser::FileBrowser() + : mIsOpen(false), mSelectionMode(SelectionMode::FILES_ONLY), mWarnOnExisting(false), mpFolderIcon(nullptr), mResult(Result::CANCEL), + mpNewFolderIcon(nullptr), mpUpFolderIcon(nullptr) + { + int x,y,n; + unsigned char* pData = stbi_load("dir_icon.png", &x, &y, &n, 0); + mpFolderIcon = new Image(pData, x, y, ImageFormat::RGBA); + Core::Graphics().RegisterImage(*mpFolderIcon); + mpFolderIcon->FreeRawData(); + + pData = stbi_load("new_dir_icon.png", &x, &y, &n, 0); + mpNewFolderIcon = new Image(pData, x, y, ImageFormat::RGBA); + Core::Graphics().RegisterImage(*mpNewFolderIcon); + mpNewFolderIcon->FreeRawData(); + + pData = stbi_load("up_arrow_icon.png", &x, &y, &n, 0); + mpUpFolderIcon = new Image(pData, x, y, ImageFormat::RGBA); + Core::Graphics().RegisterImage(*mpUpFolderIcon); + mpUpFolderIcon->FreeRawData(); + } + + /// If the given path does not exist this will default to the + /// current working directory + bool FileBrowser::OpenInDirectory(std::filesystem::path path) + { + if (mIsOpen) + return false; + + if (!std::filesystem::exists(path)) + path = std::filesystem::current_path(); + + if (!std::filesystem::is_directory(path)) + return false; + + mCurrentDirectory = path; + + // Get list of items in the current directory + ReloadItems(); + + mIsOpen = true; + return true; + } + + bool FileBrowser::DoFrame() + { + if (!mIsOpen) + return false; + + ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Appearing); + if (!ImGui::Begin("File Browser", &mIsOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking)) + { + ImGui::End(); + return false; + } + + ImVec2 iconSize(mpNewFolderIcon->GetWidth(), mpNewFolderIcon->GetHeight()); + if (ImGui::ImageButton((ImTextureID) mpNewFolderIcon->GetGLTextureID(), iconSize)) + { + ImGui::OpenPopup("New Folder Name"); + memset(mInputBuffer, 0, mBufferSize); + } + + if (ImGui::BeginPopup("New Folder Name")) + { + if (ImGui::InputText("Folder Name", mInputBuffer, mBufferSize, ImGuiInputTextFlags_EnterReturnsTrue)) + { + std::filesystem::create_directory(mInputBuffer); + ImGui::CloseCurrentPopup(); + ReloadItems(); + } + ImGui::EndPopup(); + } + + ImGui::SameLine(); + iconSize = ImVec2(mpUpFolderIcon->GetWidth(), mpUpFolderIcon->GetHeight()); + if (ImGui::ImageButton((ImTextureID) mpUpFolderIcon->GetGLTextureID(), iconSize)) + { + mCurrentDirectory = mCurrentDirectory.parent_path(); + ReloadItems(); + } + + ImGui::SameLine(); + ImGui::TextUnformatted(mCurrentDirectory.string().c_str()); + + DoListBox(); + + ImGui::Separator(); + if (mpSelectedItem) + { + ImGui::Text("Selected: %s", mpSelectedItem->filename().string().c_str()); + } + else + { + ImGui::Text("Selected: "); + } + + ImGui::Separator(); + + bool res = DoButtons(); + + ImGui::End(); + return res; + } + + void FileBrowser::AddExtensionFilter(std::string ext) + { + mExtensionsFilter.push_back(ext); + } + + void FileBrowser::SetSelectionMode(SelectionMode mode) + { + mSelectionMode = mode; + } + + void FileBrowser::WarnOnExistingFileSelection(bool warn) + { + mWarnOnExisting = warn; + } + + + /// returns nullptr if the dialog has not been opened yet + const std::filesystem::path* FileBrowser::GetSelectedItem() const + { + return mpSelectedItem; + } + + FileBrowser::Result FileBrowser::GetResult() const + { + return mResult; + } + + bool FileBrowser::IsOpen() const + { + return mIsOpen; + } + + void FileBrowser::ReloadItems() + { + mpSelectedItem = nullptr; + mItemsInDir.clear(); + for(auto const& dir_entry: std::filesystem::directory_iterator{mCurrentDirectory}) + { + mItemsInDir.push_back(dir_entry.path()); + } + + // Sort by type and then by name + std::sort(mItemsInDir.begin(), mItemsInDir.end(), [](std::filesystem::path& lhs, std::filesystem::path& rhs) + { + if (std::filesystem::is_directory(lhs) && std::filesystem::is_directory(rhs)) + { + return strcmpi(lhs.filename().string().c_str(), rhs.filename().string().c_str()) < 0; + } + + if (std::filesystem::is_directory(lhs) && !std::filesystem::is_directory(rhs)) + { + return true; + } + + return false; + }); + } + + //////////////////////////////////////////////////////////// + // RENDER HELPER METHODS + //////////////////////////////////////////////////////////// + void FileBrowser::DoListBox() + { + if (ImGui::BeginListBox("", ImVec2(ImGui::GetWindowWidth(), ImGui::GetWindowHeight() * .65f))) + { + for (int i = 0; i < mItemsInDir.size(); i++) + { + if (std::filesystem::is_directory(mItemsInDir[i])) + { + ImVec2 size(mpFolderIcon->GetWidth(), mpFolderIcon->GetHeight()); + ImTextureID id = (ImTextureID) mpFolderIcon->GetGLTextureID(); + ImGui::Image(id, size); + ImGui::SameLine(); + } + if (ImGui::Selectable(mItemsInDir[i].filename().string().c_str())) + { + if (std::filesystem::is_directory(mItemsInDir[i])) + { + mCurrentDirectory /= mItemsInDir[i]; + ReloadItems(); + + if (mSelectionMode == SelectionMode::DIRECTORIES_ONLY || mSelectionMode == SelectionMode::ANY) + { + mpSelectedItem = &mCurrentDirectory; + } + } + else + { + if (mSelectionMode == SelectionMode::FILES_ONLY || mSelectionMode == SelectionMode::ANY) + { + mpSelectedItem = &mItemsInDir[i]; + } + } + } + } + + ImGui::EndListBox(); + } + } + + bool FileBrowser::DoButtons() + { + ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 100.0f); + if (ImGui::Button("OK")) + { + if (std::filesystem::exists(*mpSelectedItem) && mWarnOnExisting) + { + ImGui::OpenPopup("Warn"); + } + else + { + mResult = Result::OK; + mIsOpen = false; + return false; + } + } + + if (ImGui::BeginPopup("Warn")) + { + ImGui::Text("This file already exists. Are you sure you want to overwrite it?"); + if (ImGui::Button("Yes")) + { + mResult = Result::OK; + mIsOpen = false; + ImGui::EndPopup(); + return false; + } + + ImGui::SameLine(); + if (ImGui::Button("No")) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + ImGui::SameLine(); + + if (ImGui::Button("Cancel")) + { + mResult = Result::CANCEL; + mIsOpen = false; + return false; + } + + return true; + } +} + diff --git a/src/gui/fileBrowser.h b/src/gui/fileBrowser.h new file mode 100644 index 0000000..5f6e7d4 --- /dev/null +++ b/src/gui/fileBrowser.h @@ -0,0 +1,81 @@ +/****************************************************************************** +* File - fileBrowser.h +* Author - Joey Pollack +* Date - 2021/11/04 (y/m/d) +* Mod Date - 2021/11/04 (y/m/d) +* Description - File browser dialog window. Can be used for opening +* and saving files. +******************************************************************************/ + +// Some useful links: +// https://en.cppreference.com/w/cpp/filesystem/is_directory +// https://en.cppreference.com/w/cpp/filesystem/path + +#ifndef FILE_SELECT_H_ +#define FILE_SELECT_H_ + +#include + +#include +#include + +namespace lunarium +{ + class Image; + class FileBrowser + { + public: + enum Result + { + OK, + CANCEL + }; + + enum SelectionMode + { + FILES_ONLY, + DIRECTORIES_ONLY, + ANY, + }; + + public: + + FileBrowser(); + bool OpenInDirectory(std::filesystem::path path); + bool DoFrame(); + bool IsOpen() const; + + void AddExtensionFilter(std::string ext); + void SetSelectionMode(SelectionMode mode); + void WarnOnExistingFileSelection(bool warn); + + // returns nullptr if the dialog has not been opened yet + const std::filesystem::path* GetSelectedItem() const; + Result GetResult() const; + + private: + + bool mIsOpen; + SelectionMode mSelectionMode; + bool mWarnOnExisting; + Result mResult; + std::filesystem::path mCurrentDirectory; + std::vector mItemsInDir; + std::vector mExtensionsFilter; // Show only these extensions. If empty show all files. + std::filesystem::path* mpSelectedItem; + + Image* mpFolderIcon; + Image* mpNewFolderIcon; + Image* mpUpFolderIcon; + static const int mBufferSize = 256; + char mInputBuffer[mBufferSize]; + + private: + void ReloadItems(); + void DoListBox(); + bool DoButtons(); + }; +} + + +#endif // FILE_SELECT_H_ \ No newline at end of file diff --git a/src/internal_libs/assets/types/image.cpp b/src/internal_libs/assets/types/image.cpp index 189520a..f3b44fe 100644 --- a/src/internal_libs/assets/types/image.cpp +++ b/src/internal_libs/assets/types/image.cpp @@ -18,6 +18,21 @@ namespace lunarium { } + Image::Image(unsigned char* data, int width, int height, ImageFormat format) + : Asset(AssetType::ASSET_TYPE_IMAGE), mRawData(data), mRawDataSize(width * height), mWidth(width), mHeight(height), mGLTextureID((unsigned)-1) + { + mFormat = format; + if (mFormat == ImageFormat::RGB) + { + mRawDataSize *= 3; + } + else if (mFormat == ImageFormat::RGBA) + { + mRawDataSize *= 4; + } + + } + Image::~Image() { delete[] mRawData; diff --git a/src/internal_libs/assets/types/image.h b/src/internal_libs/assets/types/image.h index 53ef031..4e69307 100644 --- a/src/internal_libs/assets/types/image.h +++ b/src/internal_libs/assets/types/image.h @@ -19,6 +19,7 @@ namespace lunarium { public: Image(); + Image(unsigned char* data, int width, int height, ImageFormat format); ~Image(); unsigned int GetGLTextureID() const; diff --git a/src/internal_libs/utils/helpers.cpp b/src/internal_libs/utils/helpers.cpp index eff3e16..ff2ca2b 100644 --- a/src/internal_libs/utils/helpers.cpp +++ b/src/internal_libs/utils/helpers.cpp @@ -7,7 +7,9 @@ ******************************************************************************/ #include "helpers.h" +#include "logger.h" #include +#include #ifdef WIN32 #include @@ -38,6 +40,28 @@ namespace lunarium return glsl_version; } + //////////////////////////////////////////////////////////// + // FILE SYSTEM FUNCTIONS + //////////////////////////////////////////////////////////// + std::vector FileSystem::GetFilesInDirectory(std::string path) + { + std::vector files; + for (auto &p : std::filesystem::recursive_directory_iterator(path)) + { + files.push_back(p.path().filename().string()); + } + + return files; + } + + bool FileSystem::MakeDir(std::string path) + { + if (std::filesystem::exists(path)) + return false; + + return std::filesystem::create_directory(path); + } + //////////////////////////////////////////////////////////// // MATH FUNCTIONS //////////////////////////////////////////////////////////// diff --git a/src/internal_libs/utils/helpers.h b/src/internal_libs/utils/helpers.h index ec3785b..19f58dc 100644 --- a/src/internal_libs/utils/helpers.h +++ b/src/internal_libs/utils/helpers.h @@ -23,6 +23,13 @@ namespace lunarium static std::string GetGLSLVersionString(); }; + class FileSystem + { + public: + static std::vector GetFilesInDirectory(std::string path); + static bool MakeDir(std::string path); + }; + class Math { public: diff --git a/src/run_modes/editor/editor.cpp b/src/run_modes/editor/editor.cpp index 065fe49..c3528f0 100644 --- a/src/run_modes/editor/editor.cpp +++ b/src/run_modes/editor/editor.cpp @@ -11,6 +11,7 @@ #include "panels/mainPanel.h" #include #include +#include // Panels #include "panels/about.h" @@ -18,7 +19,7 @@ namespace lunarium { Editor::Editor() - : mLogCat(-1), mpMainPanel(nullptr), mDoNewProject(false), mDoOpenProject(false), + : mLogCat(-1), mpMainPanel(nullptr), mpFileBrowser(nullptr), mpPath(nullptr), mDoNewProject(false), mDoOpenProject(false), mDoSaveProject(false), mDoSaveAs(false) { } @@ -55,6 +56,15 @@ namespace lunarium iter->second->DoFrame(); } } + + + if (mpFileBrowser) + { + if (!mpFileBrowser->DoFrame()) + { + mpPath = mpFileBrowser->GetSelectedItem(); + } + } } uint32_t Editor::GetLogCat() const @@ -76,8 +86,40 @@ namespace lunarium // FILE if (mDoNewProject) { - - mDoNewProject = false; + if (!mpFileBrowser) + { + mpFileBrowser = new FileBrowser; + // mpFileBrowser->WarnOnExistingFileSelection(true); + mpFileBrowser->SetSelectionMode(FileBrowser::SelectionMode::DIRECTORIES_ONLY); + if (!mpFileBrowser->OpenInDirectory("")) + { + delete mpFileBrowser; + mpFileBrowser = nullptr; + Logger::Log(mLogCat, LogLevel::ERROR, "Could not open the File Browser"); + } + } + else + { + if (!mpFileBrowser->IsOpen()) + { + if (mpFileBrowser->GetResult() == FileBrowser::Result::OK) + { + Logger::Log(mLogCat, LogLevel::INFO, "Generating new project at %s", mpPath->string().c_str()); + + // TODO: Generate new project at mpPath + } + else + { + Logger::Log(mLogCat, LogLevel::INFO, "New Project operation cancelled"); + } + + mpPath = nullptr; + delete mpFileBrowser; + mpFileBrowser = nullptr; + mDoNewProject = false; + } + } + } if (mDoOpenProject) diff --git a/src/run_modes/editor/editor.h b/src/run_modes/editor/editor.h index 250218e..80958b8 100644 --- a/src/run_modes/editor/editor.h +++ b/src/run_modes/editor/editor.h @@ -13,10 +13,12 @@ #include #include "panels/iPanel.h" +#include #include namespace lunarium { + class FileBrowser; class MainPanel; class Editor : public iRunMode { @@ -48,6 +50,9 @@ namespace lunarium MainPanel* mpMainPanel; std::map mPanels; + FileBrowser* mpFileBrowser; + const std::filesystem::path* mpPath; + // Menu Bar Events // Don't want to handles these events during rendering bool mDoNewProject; diff --git a/src/run_modes/editor/panels/iPanel.h b/src/run_modes/editor/panels/iPanel.h index 7731c71..d8a1607 100644 --- a/src/run_modes/editor/panels/iPanel.h +++ b/src/run_modes/editor/panels/iPanel.h @@ -15,6 +15,9 @@ namespace lunarium { PT_MAIN, PT_ABOUT, + PT_SCENE_TREE, + PT_CONTENT_BROWSER, + PT_CONSOLE, PT_UNKNOWN, }; diff --git a/src/run_modes/editor/panels/sceneTree.cpp b/src/run_modes/editor/panels/sceneTree.cpp new file mode 100644 index 0000000..38395f0 --- /dev/null +++ b/src/run_modes/editor/panels/sceneTree.cpp @@ -0,0 +1,14 @@ +/****************************************************************************** +* File - sceneTree.cpp +* Author - Joey Pollack +* Date - 2021/11/04 (y/m/d) +* Mod Date - 2021/11/04 (y/m/d) +* Description - The tree view listing all objects in the scene +******************************************************************************/ + +#include "sceneTree.h" + +namespace lunarium +{ + +} \ No newline at end of file diff --git a/src/run_modes/editor/panels/sceneTree.h b/src/run_modes/editor/panels/sceneTree.h new file mode 100644 index 0000000..ee16416 --- /dev/null +++ b/src/run_modes/editor/panels/sceneTree.h @@ -0,0 +1,22 @@ +/****************************************************************************** +* File - sceneTree.h +* Author - Joey Pollack +* Date - 2021/11/04 (y/m/d) +* Mod Date - 2021/11/04 (y/m/d) +* Description - The tree view listing all objects in the scene +******************************************************************************/ + +#ifndef SCENE_TREE_H_ +#define SCENE_TREE_H_ + +#include "iPanel.h" + +namespace lunarium +{ + class SceneTree : public Panel + { + + }; +} + +#endif // SCENE_TREE_H_ \ No newline at end of file diff --git a/test_data/dir_icon.png b/test_data/dir_icon.png new file mode 100644 index 0000000..d4acfd0 Binary files /dev/null and b/test_data/dir_icon.png differ diff --git a/test_data/dir_icon.xcf b/test_data/dir_icon.xcf new file mode 100644 index 0000000..f1e2411 Binary files /dev/null and b/test_data/dir_icon.xcf differ diff --git a/test_data/dir_icon1.png b/test_data/dir_icon1.png new file mode 100644 index 0000000..0130f5a Binary files /dev/null and b/test_data/dir_icon1.png differ diff --git a/test_data/dir_icon2.png b/test_data/dir_icon2.png new file mode 100644 index 0000000..92382a8 Binary files /dev/null and b/test_data/dir_icon2.png differ diff --git a/test_data/new_dir_icon.png b/test_data/new_dir_icon.png new file mode 100644 index 0000000..2d6876f Binary files /dev/null and b/test_data/new_dir_icon.png differ diff --git a/test_data/new_dir_icon.xcf b/test_data/new_dir_icon.xcf new file mode 100644 index 0000000..8ded43e Binary files /dev/null and b/test_data/new_dir_icon.xcf differ diff --git a/test_data/up_arrow_icon.png b/test_data/up_arrow_icon.png new file mode 100644 index 0000000..efbca06 Binary files /dev/null and b/test_data/up_arrow_icon.png differ diff --git a/test_data/up_dir_icon.png b/test_data/up_dir_icon.png new file mode 100644 index 0000000..e8b4fec Binary files /dev/null and b/test_data/up_dir_icon.png differ diff --git a/test_data/up_dir_icon.xcf b/test_data/up_dir_icon.xcf new file mode 100644 index 0000000..10a3d7c Binary files /dev/null and b/test_data/up_dir_icon.xcf differ