summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2021-03-30 19:40:11 -0700
committerrtk0c <[email protected]>2021-03-30 19:40:11 -0700
commit31950890c939862f79c817053c106bf711c63a64 (patch)
tree4e02abf37d69ab7d4f988f143b340cfd3d93331c
parente75e26da92424528e190a2111acfcc49c657e894 (diff)
Product items and misc stuff
-rw-r--r--core/CMakeLists.txt26
-rw-r--r--core/locale/zh_CN.json16
-rw-r--r--core/src/Entrypoint/main.cpp6
-rw-r--r--core/src/Model/Items.cpp83
-rw-r--r--core/src/Model/Items.hpp146
-rw-r--r--core/src/Model/Project.cpp22
-rw-r--r--core/src/Model/Project.hpp13
-rw-r--r--core/src/Model/fwd.hpp8
-rw-r--r--core/src/UI/Localization.hpp22
-rw-r--r--core/src/UI/States.cpp9
-rw-r--r--core/src/UI/States.hpp1
-rw-r--r--core/src/UI/UI.hpp8
-rw-r--r--core/src/UI/UI_Items.cpp117
-rw-r--r--core/src/UI/UI_MainWindow.cpp67
-rw-r--r--core/src/UI/UI_Utils.cpp21
15 files changed, 520 insertions, 45 deletions
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index d8e9db1..f71cdd4 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -17,7 +17,7 @@ endif()
function(add_source_group GROUP_NAME)
set(${GROUP_NAME} ${ARGN} PARENT_SCOPE)
set_source_files_properties(${ARGN}
- PROPERTIES
+ PROPERTIES
UNITY_GROUP "${GROUP_NAME}"
)
endfunction()
@@ -35,6 +35,7 @@ set(ENTRYPOINT_MODULE_SOURCES
add_source_group(MODEL_MODULE_SOURCES
src/Model/GlobalStates.cpp
+ src/Model/Items.cpp
src/Model/Project.cpp
src/Model/Stock.cpp
)
@@ -136,15 +137,32 @@ function(add_executable_variant TARGET_NAME)
endif()
if(WIN32)
- # No console window when targetting windows
- if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ # No console window when targeting windows
+ function(handle_gnu_style_compiler)
# Supposedly the flag -mwindows would automatically make the executable use GUI subsystem
# But, when subsystem is set to GUI, linker will only search WinMain and wWinMain but not the standard main (it seems like)
# so creating GUI executable fails and the linker silently reverts to the default, CUI subsystem
target_link_options(${TARGET_NAME} PRIVATE -Wl,-subsystem:windows)
target_link_options(${TARGET_NAME} PRIVATE -Wl,-entry:mainCRTStartup)
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ endfunction()
+
+ function(handle_msvc_style_compiler)
target_link_options(${TARGET_NAME} PRIVATE /SUBSYSTEM:windows /ENTRY:mainCRTStartup)
+ endfunction()
+
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ # GCC (MinGW)
+ handle_gnu_style_compiler()
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ if("x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
+ # MSVC-style argument clang (clang-cl.exe)
+ handle_msvc_style_compiler()
+ else()
+ # GNU-style argument clang (clang.exe and clang++.exe)
+ handle_gnu_style_compiler()
+ endif()
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ handle_msvc_style_compiler()
endif()
endif()
diff --git a/core/locale/zh_CN.json b/core/locale/zh_CN.json
index 3b38f40..ebd671e 100644
--- a/core/locale/zh_CN.json
+++ b/core/locale/zh_CN.json
@@ -1,5 +1,7 @@
{
"$localized_name": "中文 - 中国",
+ "Generic.Dialog.Confirm": "确定",
+ "Generic.Dialog.Cancel": "取消",
"MainWindow.Tab.Settings": "\uf013 设置",
"MainWindow.Tab.Project": "\uf15b 项目",
"MainWindow.Tab.DatabaseView": "\uf1c0 数据",
@@ -7,8 +9,6 @@
"MainWindow.Tab.Exports": "\uf56e 导出",
"Project.New": "新建项目...",
"Project.New.DialogTitle": "新建项目向导",
- "Project.New.Confirm": "确定",
- "Project.New.Cancel": "取消",
"Project.New.Name": "项目名称",
"Project.New.Path": "项目路径",
"Project.New.Path.DialogTitle": "项目路径",
@@ -21,4 +21,16 @@
"Project.Recents.NonePresent": "(暂无最近使用的项目)",
"Project.Recents.Open.Tooltip": "打开该项目",
"Project.Recents.Delete.Tooltip": "将该项目从最近使用列表中删除,项目本身将不受影响。",
+ "ActiveProject.Close": "\uf410 关闭项目",
+ "ActiveProject.OpenInFilesystem": "\uf07b 在文件系统中打开",
+ "ActiveProject.Info.Name": "项目名称:",
+ "ActiveProject.Info.Path": "项目路径:",
+ "ItemEditor.Add": "\uf067 新建",
+ "ItemEditor.Add.DialogTitle": "新建物品项",
+ "ItemEditor.Delete": "\uf1f8 移除",
+ "Item.Product.CategoryName": "产品",
+ "Item.Product.Column.Name": "名称",
+ "Item.Product.Column.Description": "描述",
+ "Item.Factory.CategoryName": "工厂",
+ "Item.Customer.CategoryName": "客户",
} \ No newline at end of file
diff --git a/core/src/Entrypoint/main.cpp b/core/src/Entrypoint/main.cpp
index ce8aab7..5811547 100644
--- a/core/src/Entrypoint/main.cpp
+++ b/core/src/Entrypoint/main.cpp
@@ -129,6 +129,8 @@ int main(int argc, char* argv[]) {
auto backendOption = parser.get<std::string>("--rendering-backend");
auto backend = CreateBackend(backendOption);
+ ImGui::GetIO().IniFilename = nullptr;
+ ImGui::GetIO().LogFilename = nullptr;
ImGui::StyleColorsLight();
// Includes latin alphabet, although for some reason smaller than if rendered using 18 point NotoSans regular
@@ -140,7 +142,7 @@ int main(int argc, char* argv[]) {
ImGui::GetIO().Fonts->AddFontFromFileTTF("fonts/FontAwesome5-Solid.otf", 14, &config, iconRanges);
I18n::OnReload.Connect([]() { LocaleStrings::Instance = std::make_unique<LocaleStrings>(); });
- // Do i18n intialization after linking reload signals, so that when SetLanguage() is called, the locale strings will be initialized (without us writing the code another time outside the slot)
+ // Do i18n initialization after linking reload signals, so that when SetLanguage() is called, the locale strings will be initialized (without us writing the code another time outside the slot)
I18n::Init();
I18n::SetLanguage("zh_CN");
@@ -151,9 +153,11 @@ int main(int argc, char* argv[]) {
while (!glfwWindowShouldClose(window)) {
backend->BeginFrame();
UI::MainWindow();
+ ImGui::ShowDemoWindow();
backend->EndFrame();
}
+ UIState::Shutdown();
GlobalStates::Shutdown();
return 0;
diff --git a/core/src/Model/Items.cpp b/core/src/Model/Items.cpp
new file mode 100644
index 0000000..db2d39f
--- /dev/null
+++ b/core/src/Model/Items.cpp
@@ -0,0 +1,83 @@
+#include "Items.hpp"
+
+#include <limits>
+#include <utility>
+
+ItemBase::ItemBase()
+ : mId{ std::numeric_limits<size_t>::max() } {
+}
+
+ItemBase::ItemBase(size_t id)
+ : mId{ id } {
+}
+
+bool ItemBase::IsInvalid() const {
+ return mId == std::numeric_limits<size_t>::max();
+}
+
+size_t ItemBase::GetId() const {
+ return mId;
+}
+
+ProductItem::ProductItem(size_t id, std::string name)
+ : ItemBase(id)
+ , mName{ std::move(name) } {
+}
+
+const std::string& ProductItem::GetName() const {
+ return mName;
+}
+
+void ProductItem::SetName(std::string name) {
+ mName = std::move(name);
+}
+
+const std::string& ProductItem::GetDescription() const {
+ return mDescription;
+}
+
+void ProductItem::SetDescription(std::string description) {
+ mDescription = std::move(description);
+}
+
+FactoryItem::FactoryItem(size_t id, std::string name)
+ : ItemBase(id)
+ , mName{ std::move(name) } {
+}
+
+const std::string& FactoryItem::GetName() const {
+ return mName;
+}
+
+void FactoryItem::SetName(std::string name) {
+ mName = std::move(name);
+}
+
+const std::string& FactoryItem::GetDescription() const {
+ return mDescription;
+}
+
+void FactoryItem::SetDescription(std::string description) {
+ mDescription = std::move(description);
+}
+
+CustomerItem::CustomerItem(size_t id, std::string name)
+ : ItemBase(id)
+ , mName{ std::move(name) } {
+}
+
+const std::string& CustomerItem::GetName() const {
+ return mName;
+}
+
+void CustomerItem::SetName(std::string name) {
+ mName = std::move(name);
+}
+
+const std::string& CustomerItem::GetDescription() const {
+ return mDescription;
+}
+
+void CustomerItem::SetDescription(std::string description) {
+ mDescription = std::move(description);
+}
diff --git a/core/src/Model/Items.hpp b/core/src/Model/Items.hpp
new file mode 100644
index 0000000..0c7be41
--- /dev/null
+++ b/core/src/Model/Items.hpp
@@ -0,0 +1,146 @@
+#pragma once
+
+#include <tsl/array_map.h>
+#include <cstddef>
+#include <stdexcept>
+#include <string>
+#include <string_view>
+#include <vector>
+
+/// Pointers and references returned by accessors are valid as long as no non-const functions have been called.
+template <class T>
+class ItemList {
+public:
+ class Iterator {
+ private:
+ typename std::vector<T>::const_iterator mBackingIter;
+
+ public:
+ Iterator(typename std::vector<T>::const_iterator it)
+ : mBackingIter{ it } {
+ }
+
+ Iterator& operator++() {
+ ++mBackingIter;
+ return *this;
+ }
+
+ Iterator& operator++(int) {
+ auto tmp = *this;
+ ++mBackingIter;
+ return tmp;
+ }
+
+ Iterator& operator--() {
+ --mBackingIter;
+ return *this;
+ }
+
+ Iterator& operator--(int) {
+ auto tmp = *this;
+ --mBackingIter;
+ return tmp;
+ }
+
+ const T& operator*() const {
+ return *mBackingIter;
+ }
+
+ friend bool operator==(const Iterator&, const Iterator&) = default;
+ };
+
+private:
+ std::vector<T> mStorage;
+ tsl::array_map<char, size_t> mNameLookup;
+
+public:
+ template <class... Args>
+ T& Insert(std::string name, Args... args) {
+ auto iter = mNameLookup.find(name);
+ if (iter != mNameLookup.end()) {
+ throw std::runtime_error("Duplicate key.");
+ }
+
+ size_t id = mStorage.size();
+ mNameLookup.insert(name, id);
+ return mStorage.emplace_back(id, std::move(name), std::forward<Args>(args)...);
+ }
+
+ const T* Find(size_t id) const {
+ return &mStorage[id];
+ }
+
+ const T* Find(std::string_view name) const {
+ auto iter = mNameLookup.find(name);
+ if (iter != mNameLookup.end()) {
+ return &mStorage[iter.value()];
+ } else {
+ return nullptr;
+ }
+ }
+
+ Iterator begin() const {
+ return Iterator(mStorage.begin());
+ }
+
+ Iterator end() const {
+ return Iterator(mStorage.end());
+ }
+};
+
+class ItemBase {
+private:
+ size_t mId;
+
+public:
+ ItemBase();
+ ItemBase(size_t id);
+
+ bool IsInvalid() const;
+ size_t GetId() const;
+};
+
+class ProductItem : public ItemBase {
+private:
+ std::string mName;
+ std::string mDescription;
+
+public:
+ ProductItem() {}
+ ProductItem(size_t id, std::string name);
+
+ const std::string& GetName() const;
+ void SetName(std::string mName);
+ const std::string& GetDescription() const;
+ void SetDescription(std::string description);
+};
+
+class FactoryItem : public ItemBase {
+private:
+ std::string mName;
+ std::string mDescription;
+
+public:
+ FactoryItem() {}
+ FactoryItem(size_t id, std::string name);
+
+ const std::string& GetName() const;
+ void SetName(std::string name);
+ const std::string& GetDescription() const;
+ void SetDescription(std::string description);
+};
+
+class CustomerItem : public ItemBase {
+private:
+ std::string mName;
+ std::string mDescription;
+
+public:
+ CustomerItem() {}
+ CustomerItem(size_t id, std::string name);
+
+ const std::string& GetName() const;
+ void SetName(std::string name);
+ const std::string& GetDescription() const;
+ void SetDescription(std::string description);
+};
diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp
index c54a02c..f070940 100644
--- a/core/src/Model/Project.cpp
+++ b/core/src/Model/Project.cpp
@@ -25,11 +25,12 @@ Project Project::Load(const fs::path& path) {
Project proj;
proj.mRootPath = path.parent_path();
+ proj.mRootPathString = proj.mRootPath.string();
Json::Value root;
ifs >> root;
- const auto& croot = root; // Use const reference so that accessors default to returning a null if not found, instead of siliently creating new elements
+ const auto& croot = root; // Use const reference so that accessors default to returning a null if not found, instead of silently creating new elements
if (!croot.isObject()) {
throw std::runtime_error(kInvalidFormatErr);
}
@@ -46,6 +47,7 @@ Project Project::Load(const fs::path& path) {
Project Project::Create(std::string name, const fs::path& path) {
Project proj;
proj.mRootPath = path;
+ proj.mRootPathString = path.string();
proj.mName = std::move(name);
return proj;
}
@@ -54,6 +56,10 @@ const fs::path& Project::GetPath() const {
return mRootPath;
}
+const std::string& Project::GetPathString() const {
+ return mRootPathString;
+}
+
const std::string& Project::GetName() const {
return mName;
}
@@ -61,3 +67,17 @@ const std::string& Project::GetName() const {
void Project::SetName(std::string name) {
mName = std::move(name);
}
+
+Json::Value Project::Serialize() {
+ Json::Value root(Json::objectValue);
+
+ root["Name"] = mName;
+
+ return root;
+}
+
+void Project::WriteToDisk() {
+ auto root = Serialize();
+ std::ofstream ofs(mRootPath / "cplt_project.json");
+ ofs << root;
+}
diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp
index 7b5c7e3..23eafc1 100644
--- a/core/src/Model/Project.hpp
+++ b/core/src/Model/Project.hpp
@@ -1,11 +1,20 @@
#pragma once
+#include "Model/Items.hpp"
+
#include <filesystem>
#include <string>
+#include <json/forwards.h>
class Project {
public:
+ ItemList<ProductItem> Products;
+ ItemList<FactoryItem> Factories;
+ ItemList<CustomerItem> Customers;
+
+private:
std::filesystem::path mRootPath;
+ std::string mRootPathString;
std::string mName;
public:
@@ -17,10 +26,14 @@ public:
// Path to a *directory* that contains the project file.
const std::filesystem::path& GetPath() const;
+ const std::string& GetPathString() const;
const std::string& GetName() const;
void SetName(std::string name);
+ Json::Value Serialize();
+ void WriteToDisk();
+
private:
Project() = default;
};
diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp
index 6bbc0b7..bf9a8cf 100644
--- a/core/src/Model/fwd.hpp
+++ b/core/src/Model/fwd.hpp
@@ -3,5 +3,13 @@
// GlobalStates.hpp
class GlobalStates;
+// Items.hpp
+template <class T>
+class ItemList;
+class ItemBase;
+class ProductItem;
+class FactoryItem;
+class CustomerItem;
+
// Project.hpp
class Project;
diff --git a/core/src/UI/Localization.hpp b/core/src/UI/Localization.hpp
index e604165..d5424ea 100644
--- a/core/src/UI/Localization.hpp
+++ b/core/src/UI/Localization.hpp
@@ -12,6 +12,9 @@ public:
static std::unique_ptr<LocaleStrings> Instance;
public:
+ BasicTranslation DialogConfirm{ "Generic.Dialog.Confirm"sv };
+ BasicTranslation DialogCancel{ "Generic.Dialog.Cancel"sv };
+
BasicTranslation TabSettings{ "MainWindow.Tab.Settings"sv };
BasicTranslation TabProject{ "MainWindow.Tab.Project"sv };
BasicTranslation TabDatabaseView{ "MainWindow.Tab.DatabaseView"sv };
@@ -19,9 +22,7 @@ public:
BasicTranslation TabExport{ "MainWindow.Tab.Exports"sv };
BasicTranslation NewProject{ "Project.New"sv };
- BasicTranslation NewProjectTitle{ "Project.New.DialogTitle"sv };
- BasicTranslation ConfirmNewProject{ "Project.New.Confirm"sv };
- BasicTranslation CancelNewProject{ "Project.New.Cancel"sv };
+ BasicTranslation NewProjectDialogTitle{ "Project.New.DialogTitle"sv };
BasicTranslation NewProjectNameHint{ "Project.New.Name"sv };
BasicTranslation NewProjectPathHint{ "Project.New.Path"sv };
BasicTranslation NewProjectPathDialogTitle{ "Project.New.Path.DialogTitle"sv };
@@ -36,4 +37,19 @@ public:
BasicTranslation NoRecentProjectsMessage{ "Project.Recents.NonePresent"sv };
BasicTranslation OpenRecentProjectTooltip{ "Project.Recents.Open.Tooltip"sv };
BasicTranslation DeleteRecentProjectTooltip{ "Project.Recents.Delete.Tooltip"sv };
+
+ BasicTranslation CloseActiveProject{ "ActiveProject.Close"sv };
+ BasicTranslation OpenActiveProjectInFileSystem{ "ActiveProject.OpenInFilesystem"sv };
+ BasicTranslation ActiveProjectName{ "ActiveProject.Info.Name"sv };
+ BasicTranslation ActiveProjectPath{ "ActiveProject.Info.Path"sv };
+
+ BasicTranslation AddItem{ "ItemEditor.Add"sv };
+ BasicTranslation AddItemDialogTitle{ "ItemEditor.Add.DialogTitle"sv };
+ BasicTranslation DeleteItem{ "ItemEditor.Delete"sv };
+
+ BasicTranslation ProductCategoryName{ "Item.Product.CategoryName"sv };
+ BasicTranslation ProductNameColumn{ "Item.Product.Column.Name"sv };
+ BasicTranslation ProductDescriptionColumn{ "Item.Product.Column.Description"sv };
+ BasicTranslation FactoryCategoryName{ "Item.Factory.CategoryName"sv };
+ BasicTranslation CustomerCategoryName{ "Item.Customer.CategoryName"sv };
};
diff --git a/core/src/UI/States.cpp b/core/src/UI/States.cpp
index 07bbcf7..dc7c37a 100644
--- a/core/src/UI/States.cpp
+++ b/core/src/UI/States.cpp
@@ -11,6 +11,13 @@ void UIState::Init() {
uiStateInstance = std::make_unique<UIState>();
}
+void UIState::Shutdown() {
+ if (uiStateInstance) {
+ uiStateInstance->CloseCurrentProject();
+ uiStateInstance = nullptr;
+ }
+}
+
UIState& UIState::GetInstance() {
return *uiStateInstance;
}
@@ -22,7 +29,7 @@ void UIState::SetCurrentProject(std::unique_ptr<Project> project) {
void UIState::CloseCurrentProject() {
if (CurrentProject) {
- // TODO save stuff
+ CurrentProject->WriteToDisk();
CurrentProject = nullptr;
}
}
diff --git a/core/src/UI/States.hpp b/core/src/UI/States.hpp
index cbb556f..de0510c 100644
--- a/core/src/UI/States.hpp
+++ b/core/src/UI/States.hpp
@@ -9,6 +9,7 @@
class UIState {
public:
static void Init();
+ static void Shutdown();
static UIState& GetInstance();
public:
diff --git a/core/src/UI/UI.hpp b/core/src/UI/UI.hpp
index b0c3aaa..52a2ca9 100644
--- a/core/src/UI/UI.hpp
+++ b/core/src/UI/UI.hpp
@@ -1,7 +1,15 @@
#pragma once
+#include <imgui.h>
+
namespace ImGui {
+void SetNextWindowSizeRelScreen(float xPercent, float yPercent, ImGuiCond_ cond = ImGuiCond_None);
+void SetNextWindowCentered(ImGuiCond_ cond = ImGuiCond_None);
+
+void PushDisabled();
+void PopDisabled();
+
void ErrorIcon();
void WarningIcon();
diff --git a/core/src/UI/UI_Items.cpp b/core/src/UI/UI_Items.cpp
index 371e682..a990a96 100644
--- a/core/src/UI/UI_Items.cpp
+++ b/core/src/UI/UI_Items.cpp
@@ -1,9 +1,124 @@
#include "UI.hpp"
+#include "Model/GlobalStates.hpp"
+#include "Model/Project.hpp"
#include "UI/Localization.hpp"
+#include "UI/States.hpp"
+#include <IconsFontAwesome.h>
#include <imgui.h>
+#include <imgui_stdlib.h>
-void UI::ItemsTab() {
+namespace {
+/// Specialized for each item type.
+template <class T>
+void AddToItemListDialog(ItemList<T>& list);
+/// Specialized for each item type.
+template <class T>
+void ItemListEntries(ItemList<T>& list);
+
+template <>
+void AddToItemListDialog<ProductItem>(ItemList<ProductItem>& list) {
+ static std::string productName;
+ static std::string description;
+
+ auto ls = LocaleStrings::Instance.get();
+ auto& uis = UIState::GetInstance();
+ ImGui::InputText(ls->ProductNameColumn.Get(), &productName);
+ ImGui::InputText(ls->ProductDescriptionColumn.Get(), &description);
+
+ if (ImGui::Button(ls->DialogConfirm.Get())) {
+ auto& product = uis.CurrentProject->Products.Insert(std::move(productName));
+ product.SetDescription(std::move(description));
+
+ productName.clear();
+ description.clear();
+
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::SameLine();
+ if (ImGui::Button(ls->DialogCancel.Get())) {
+ ImGui::CloseCurrentPopup();
+ }
+}
+
+template <>
+void AddToItemListDialog<FactoryItem>(ItemList<FactoryItem>& list) {
// TODO
}
+
+template <>
+void AddToItemListDialog<CustomerItem>(ItemList<CustomerItem>& list) {
+ // TODO
+}
+
+template <>
+void ItemListEntries<ProductItem>(ItemList<ProductItem>& list) {
+ auto ls = LocaleStrings::Instance.get();
+ if (ImGui::BeginTable("ItemListEntries", 2)) {
+
+ ImGui::TableSetupColumn(ls->ProductNameColumn.Get());
+ ImGui::TableSetupColumn(ls->ProductDescriptionColumn.Get());
+ ImGui::TableHeadersRow();
+
+ for (auto& entry : list) {
+ ImGui::TableNextRow();
+
+ ImGui::TableNextColumn();
+ ImGui::Text("%s", entry.GetName().c_str());
+ ImGui::TableNextColumn();
+ ImGui::Text("%.8s", entry.GetDescription().c_str());
+ if (ImGui::Button(ICON_FA_EDIT)) {
+ // TODO
+ }
+ }
+ ImGui::EndTable();
+ }
+}
+
+template <>
+void ItemListEntries<FactoryItem>(ItemList<FactoryItem>& list) {
+ // TODO
+}
+
+template <>
+void ItemListEntries<CustomerItem>(ItemList<CustomerItem>& list) {
+ // TODO
+}
+
+template <class T>
+void ItemListEditor(ItemList<T>& list) {
+ auto ls = LocaleStrings::Instance.get();
+
+ if (ImGui::Button(ls->AddItem.Get())) {
+ ImGui::SetNextWindowCentered();
+ ImGui::OpenPopup(ls->AddItemDialogTitle.Get());
+ }
+ if (ImGui::BeginPopupModal(ls->AddItemDialogTitle.Get())) {
+ AddToItemListDialog<T>(list);
+ ImGui::EndPopup();
+ }
+
+ ImGui::SameLine();
+ if (ImGui::Button(ls->DeleteItem.Get())) {
+ // TODO
+ }
+
+ ItemListEntries<T>(list);
+}
+} // namespace
+
+void UI::ItemsTab() {
+ auto ls = LocaleStrings::Instance.get();
+ auto& uis = UIState::GetInstance();
+
+ if (ImGui::CollapsingHeader(ls->ProductCategoryName.Get())) {
+ ItemListEditor(uis.CurrentProject->Products);
+ }
+ if (ImGui::CollapsingHeader(ls->FactoryCategoryName.Get())) {
+ ItemListEditor(uis.CurrentProject->Factories);
+ }
+ if (ImGui::CollapsingHeader(ls->CustomerCategoryName.Get())) {
+ ItemListEditor(uis.CurrentProject->Customers);
+ }
+}
diff --git a/core/src/UI/UI_MainWindow.cpp b/core/src/UI/UI_MainWindow.cpp
index de300e2..15c28ff 100644
--- a/core/src/UI/UI_MainWindow.cpp
+++ b/core/src/UI/UI_MainWindow.cpp
@@ -5,28 +5,38 @@
#include "UI/Localization.hpp"
#include "UI/States.hpp"
-#include <portable-file-dialogs.h>
-
#include <IconsFontAwesome.h>
#include <imgui.h>
-#include <imgui_internal.h>
#include <imgui_stdlib.h>
+#include <portable-file-dialogs.h>
#include <filesystem>
+#include <memory>
namespace fs = std::filesystem;
namespace {
void LoadProjectAt(const std::filesystem::path& path) {
auto& uis = UIState::GetInstance();
- auto& gs = GlobalStates::GetInstance();
-
auto project = Project::Load(path);
- auto uptr = std::unique_ptr<Project>(new Project(std::move(project)));
- uis.SetCurrentProject(std::move(uptr));
+ uis.SetCurrentProject(std::make_unique<Project>(std::move(project)));
}
void ProjectTab_Normal() {
- // TODO
+ auto ls = LocaleStrings::Instance.get();
+ auto& gs = GlobalStates::GetInstance();
+ auto& uis = UIState::GetInstance();
+
+ if (ImGui::Button(ls->CloseActiveProject.Get())) {
+ uis.CloseCurrentProject();
+ return;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button(ls->OpenActiveProjectInFileSystem.Get())) {
+ // TODO
+ }
+
+ ImGui::Text("%s%s", ls->ActiveProjectName.Get(), uis.CurrentProject->GetName().c_str());
+ ImGui::Text("%s%s", ls->ActiveProjectPath.Get(), uis.CurrentProject->GetPathString().c_str());
}
void ProjectTab_NoProject() {
@@ -42,6 +52,7 @@ void ProjectTab_NoProject() {
auto TrySelectPath = [&](fs::path newPath) {
if (fs::exists(newPath)) {
dirNameIsValid = true;
+ dirName = newPath.string();
dirPath = std::move(newPath);
} else {
dirNameIsValid = false;
@@ -49,15 +60,14 @@ void ProjectTab_NoProject() {
};
if (ImGui::Button(ls->NewProject.Get())) {
- auto vs = ImGui::GetMainViewport()->Size; // Viewport Size
- ImGui::SetNextWindowSize({ vs.x * 0.5f, vs.y * 0.5f });
- ImGui::SetNextWindowPos({ vs.x / 2, vs.y / 2 }, ImGuiCond_Always, { 0.5f, 0.5f }); // Center window initially
- ImGui::OpenPopup(ls->NewProjectTitle.Get());
+ ImGui::SetNextWindowCentered();
+ ImGui::SetNextWindowSizeRelScreen(0.5f, 0.5f);
+ ImGui::OpenPopup(ls->NewProjectDialogTitle.Get());
}
// Make it so that the modal dialog has a close button
bool newProjectDialogDummyTrue = true;
- if (ImGui::BeginPopupModal(ls->NewProjectTitle.Get(), &newProjectDialogDummyTrue)) {
+ if (ImGui::BeginPopupModal(ls->NewProjectDialogTitle.Get(), &newProjectDialogDummyTrue)) {
ImGui::InputTextWithHint("##ProjectName", ls->NewProjectNameHint.Get(), &projectName);
if (ImGui::InputTextWithHint("##ProjectPath", ls->NewProjectPathHint.Get(), &dirName)) {
@@ -76,30 +86,26 @@ void ProjectTab_NoProject() {
ImGui::ErrorIcon();
ImGui::SameLine();
- ImGui::Text(ls->NewProjectEmptyNameError.Get());
+ ImGui::Text("%s", ls->NewProjectEmptyNameError.Get());
}
if (!dirNameIsValid) {
ImGui::ErrorIcon();
ImGui::SameLine();
- ImGui::Text(ls->NewProjectInvalidPathError.Get());
+ ImGui::Text("%s", ls->NewProjectInvalidPathError.Get());
}
ImGui::Spacing();
bool formValid = dirNameIsValid && !projectName.empty();
- if (!formValid) {
- ImGui::PushItemFlag(ImGuiItemFlags_Disabled, false);
- ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f * ImGui::GetStyle().Alpha);
- }
- if (ImGui::Button(ls->ConfirmNewProject.Get())) {
+ if (!formValid) ImGui::PushDisabled();
+ if (ImGui::Button(ls->DialogConfirm.Get())) {
ImGui::CloseCurrentPopup();
auto project = Project::Create(std::move(projectName), dirPath);
- auto uptr = std::unique_ptr<Project>(new Project(std::move(project)));
- uis.SetCurrentProject(std::move(uptr));
+ uis.SetCurrentProject(std::make_unique<Project>(std::move(project)));
// Dialog just got closed, reset states
projectName = "";
@@ -107,13 +113,10 @@ void ProjectTab_NoProject() {
dirPath = fs::path{};
dirNameIsValid = false;
}
- if (!formValid) {
- ImGui::PopItemFlag();
- ImGui::PopStyleVar();
- }
+ if (!formValid) ImGui::PopDisabled();
ImGui::SameLine();
- if (ImGui::Button(ls->CancelNewProject.Get())) {
+ if (ImGui::Button(ls->DialogCancel.Get())) {
ImGui::CloseCurrentPopup();
}
@@ -129,7 +132,7 @@ void ProjectTab_NoProject() {
}
ImGui::Separator();
- ImGui::Text(ls->RecentProjects.Get());
+ ImGui::Text("%s", ls->RecentProjects.Get());
ImGui::SameLine();
if (ImGui::Button(ls->ClearRecentProjects.Get())) {
gs.ClearRecentProjects();
@@ -137,18 +140,18 @@ void ProjectTab_NoProject() {
auto& recentProjects = gs.GetRecentProjects();
if (recentProjects.empty()) {
- ImGui::Text(ls->NoRecentProjectsMessage.Get());
+ ImGui::Text("%s", ls->NoRecentProjectsMessage.Get());
}
for (auto it = recentProjects.begin(); it != recentProjects.end(); ++it) {
auto& [path, recent] = *it;
- ImGui::Text(recent.c_str());
+ ImGui::Text("%s", recent.c_str());
ImGui::SameLine();
if (ImGui::Button(ICON_FA_EDIT)) {
LoadProjectAt(path);
}
if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip(ls->OpenRecentProjectTooltip.Get());
+ ImGui::SetTooltip("%s", ls->OpenRecentProjectTooltip.Get());
}
ImGui::SameLine();
@@ -156,7 +159,7 @@ void ProjectTab_NoProject() {
gs.RemoveRecentProject(std::distance(recentProjects.begin(), it));
}
if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip(ls->DeleteRecentProjectTooltip.Get());
+ ImGui::SetTooltip("%s", ls->DeleteRecentProjectTooltip.Get());
}
}
}
diff --git a/core/src/UI/UI_Utils.cpp b/core/src/UI/UI_Utils.cpp
index 61a62f0..615caae 100644
--- a/core/src/UI/UI_Utils.cpp
+++ b/core/src/UI/UI_Utils.cpp
@@ -2,6 +2,27 @@
#include <IconsFontAwesome.h>
#include <imgui.h>
+#include <imgui_internal.h>
+
+void ImGui::SetNextWindowSizeRelScreen(float xPercent, float yPercent, ImGuiCond_ cond) {
+ auto vs = ImGui::GetMainViewport()->Size;
+ ImGui::SetNextWindowSize({ vs.x * xPercent, vs.y * yPercent }, cond);
+}
+
+void ImGui::SetNextWindowCentered(ImGuiCond_ cond) {
+ auto vs = ImGui::GetMainViewport()->Size;
+ ImGui::SetNextWindowPos({ vs.x / 2, vs.y / 2 }, cond, { 0.5f, 0.5f });
+}
+
+void ImGui::PushDisabled() {
+ ImGui::PushItemFlag(ImGuiItemFlags_Disabled, false);
+ ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f * ImGui::GetStyle().Alpha);
+}
+
+void ImGui::PopDisabled() {
+ ImGui::PopItemFlag();
+ ImGui::PopStyleVar();
+}
void ImGui::ErrorIcon() {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{ 237 / 255.0f, 67 / 255.0f, 55 / 255.0f, 1.0f }); // #ED4337