From 8f0dda5eab181b0f14f2652b4e984aaaae3f258c Mon Sep 17 00:00:00 2001 From: rtk0c Date: Mon, 27 Jun 2022 18:27:13 -0700 Subject: Start from a clean slate --- core/src/UI/UI.hpp | 48 -- core/src/UI/UI_DatabaseView.cpp | 668 --------------------------- core/src/UI/UI_Items.cpp | 252 ----------- core/src/UI/UI_MainWindow.cpp | 237 ---------- core/src/UI/UI_Settings.cpp | 8 - core/src/UI/UI_Templates.cpp | 977 ---------------------------------------- core/src/UI/UI_Utils.cpp | 315 ------------- core/src/UI/UI_Workflows.cpp | 293 ------------ core/src/UI/fwd.hpp | 6 - 9 files changed, 2804 deletions(-) delete mode 100644 core/src/UI/UI.hpp delete mode 100644 core/src/UI/UI_DatabaseView.cpp delete mode 100644 core/src/UI/UI_Items.cpp delete mode 100644 core/src/UI/UI_MainWindow.cpp delete mode 100644 core/src/UI/UI_Settings.cpp delete mode 100644 core/src/UI/UI_Templates.cpp delete mode 100644 core/src/UI/UI_Utils.cpp delete mode 100644 core/src/UI/UI_Workflows.cpp delete mode 100644 core/src/UI/fwd.hpp (limited to 'core/src/UI') diff --git a/core/src/UI/UI.hpp b/core/src/UI/UI.hpp deleted file mode 100644 index 0a80b4c..0000000 --- a/core/src/UI/UI.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include - -namespace ImGui { - -void SetNextWindowSizeRelScreen(float xPercent, float yPercent, ImGuiCond cond = ImGuiCond_None); -void SetNextWindowCentered(ImGuiCond cond = ImGuiCond_None); - -void PushDisabled(); -void PopDisabled(); - -bool Button(const char* label, bool disabled); -bool Button(const char* label, const ImVec2& sizeArg, bool disabled); - -void ErrorIcon(); -void ErrorMessage(const char* fmt, ...); -void WarningIcon(); -void WarningMessage(const char* fmt, ...); - -enum class IconType -{ - Flow, - Circle, - Square, - Grid, - RoundSquare, - Diamond, -}; - -void DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor); -void Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color = ImVec4(1, 1, 1, 1), const ImVec4& innerColor = ImVec4(0, 0, 0, 0)); - -bool Splitter(bool splitVertically, float thickness, float* size1, float* size2, float minSize1, float minSize2, float splitterLongAxisSize = -1.0f); - -} // namespace ImGui - -namespace UI { - -void MainWindow(); - -void SettingsTab(); -void DatabaseViewTab(); -void ItemsTab(); -void WorkflowsTab(); -void TemplatesTab(); - -} // namespace UI diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp deleted file mode 100644 index e128a59..0000000 --- a/core/src/UI/UI_DatabaseView.cpp +++ /dev/null @@ -1,668 +0,0 @@ -#include "UI.hpp" - -#include "Model/Filter.hpp" -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Utils/I18n.hpp" -#include "Utils/ScopeGuard.hpp" -#include "Utils/Time.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace CPLT_UNITY_ID { - -// TODO move to Settings -constexpr int kMaxEntriesPerPage = 32; -constexpr int kSummaryItemCount = 3; -constexpr int kSummaryMaxLength = 25; - -std::pair SplitEntryIndex(int entryIdx) -{ - int page = entryIdx / kMaxEntriesPerPage; - int row = entryIdx % kMaxEntriesPerPage; - return { page, row }; -} - -enum class DeliveryDirection -{ - FactoryToWarehouse, - WarehouseToCustomer, -}; - -struct Item -{ - int ItemId; - int Count; -}; - -struct DeliveryEntry -{ - std::vector Items; - std::string ItemsSummary; - std::string ShipmentTime; - std::string ArriveTime; - DeliveryDirection Direction; - - const char* StringifyDirection() const - { - switch (Direction) { - case DeliveryDirection::FactoryToWarehouse: return "Factory to warehouse"; - case DeliveryDirection::WarehouseToCustomer: return "Warehouse to customer"; - } - } -}; - -struct SaleEntry -{ - static constexpr auto kType = DeliveryDirection::WarehouseToCustomer; - - std::vector AssociatedDeliveries; - std::vector Items; - std::string ItemsSummary; - std::string Customer; - std::string Deadline; - std::string DeliveryTime; - int Id; - bool DeliveriesCached = false; -}; - -struct PurchaseEntry -{ - static constexpr auto kType = DeliveryDirection::FactoryToWarehouse; - - std::vector AssociatedDeliveries; - std::vector Items; - std::string ItemsSummary; - std::string Factory; - std::string OrderTime; - std::string DeliveryTime; - int Id; - bool DeliveriesCached; -}; - -template -class GenericTableView -{ -public: - // clang-format off - static constexpr bool kHasItems = requires(T t) - { - t.Items; - t.ItemsSummary; - }; - static constexpr bool kHasCustomer = requires(T t) { t.Customer; }; - static constexpr bool kHasDeadline = requires(T t) { t.Deadline; }; - static constexpr bool kHasFactory = requires(T t) { t.Factory; }; - static constexpr bool kHasOrderTime = requires(T t) { t.OrderTime; }; - static constexpr bool kHasCompletionTime = requires(T t) { t.DeliveryTime; }; - static constexpr int kColumnCount = kHasItems + kHasCustomer + kHasDeadline + kHasFactory + kHasOrderTime + kHasCompletionTime; - // clang-format on - - using Page = std::vector; - - struct QueryStatements - { - SQLite::Statement* GetRowCount; - SQLite::Statement* GetRows; - SQLite::Statement* GetItems; - SQLite::Statement* FilterRows; - } Statements; - -protected: - // Translation entries for implementer to fill out - const char* mEditDialogTitle; - - Project* mProject; - Page* mCurrentPage = nullptr; - - /// Current active filter object, or \c nullptr. - std::unique_ptr mActiveFilter; - - tsl::robin_map mPages; - - /// A vector of entry indices (in \c mEntries) that are visible under the current filter. - std::vector mActiveEntries; - - /// Number of rows in the table. - int mRowCount; - /// Last possible page for the current set table and filter (inclusive). - int mLastPage; - - /// The current page the user is on. - int mCurrentPageNumber; - - /// Row index of the select entry - int mSelectRow; - -public: - /// Calculate the first visible row's entry index. - int GetFirstVisibleRowIdx() const - { - return mCurrentPageNumber * kMaxEntriesPerPage; - } - - Project* GetProject() const - { - return mProject; - } - - void OnProjectChanged(Project* newProject) - { - mProject = newProject; - - auto& stmt = *Statements.GetRowCount; - if (stmt.executeStep()) { - mRowCount = stmt.getColumn(0).getInt(); - } else { - std::cerr << "Failed to fetch row count from SQLite.\n"; - mRowCount = 0; - } - - mActiveFilter = nullptr; - mActiveEntries.clear(); - - mPages.clear(); - mCurrentPage = nullptr; - UpdateLastPage(); - SetPage(0); - - mSelectRow = -1; - } - - TableRowsFilter* GetFilter() const - { - return mActiveFilter.get(); - } - - void OnFilterChanged() - { - auto& stmt = *Statements.FilterRows; - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - // TODO lazy loading when too many results - mActiveEntries.clear(); - int columnIdx = stmt.getColumnIndex("Id"); - while (stmt.executeStep()) { - mActiveEntries.push_back(stmt.getColumn(columnIdx).getInt()); - } - - UpdateLastPage(); - SetPage(0); - - mSelectRow = -1; - } - - void OnFilterChanged(std::unique_ptr filter) - { - mActiveFilter = std::move(filter); - OnFilterChanged(); - } - - void Display() - { - bool dummy = true; - - if (ImGui::Button(ICON_FA_ARROW_LEFT, mCurrentPageNumber == 0)) { - SetPage(mCurrentPageNumber - 1); - } - - ImGui::SameLine(); - // +1 to convert from 0-based indices to 1-based, for human legibility - ImGui::Text("%d/%d", mCurrentPageNumber + 1, mLastPage + 1); - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_RIGHT, mCurrentPageNumber == mLastPage)) { - SetPage(mCurrentPageNumber + 1); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { - // TODO - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_EDIT " " I18N_TEXT("Edit", L10N_EDIT), mSelectRow == -1)) { - ImGui::OpenPopup(mEditDialogTitle); - } - if (ImGui::BeginPopupModal(mEditDialogTitle, &dummy, ImGuiWindowFlags_AlwaysAutoResize)) { - auto& entry = (*mCurrentPage)[mSelectRow]; - int entryIdx = GetFirstVisibleRowIdx() + mSelectRow; - EditEntry(entry, entryIdx, mSelectRow); - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), mSelectRow == -1)) { - // TODO - } - - ImGui::Columns(2); - { - DisplayMainTable(); - ImGui::NextColumn(); - - if (mSelectRow == -1) { - ImGui::TextWrapped("%s", I18N_TEXT("Select an entry to show associated deliveries", L10N_DATABASE_MESSAGE_NO_ORDER_SELECTED)); - } else { - DisplayDeliveriesTable(); - } - ImGui::NextColumn(); - } - ImGui::Columns(1); - } - - void SetPage(int page) - { - mCurrentPageNumber = page; - mCurrentPage = &LoadAndGetPage(page); - mSelectRow = -1; - } - -private: - static int CalcPageForRowId(int64_t entryIdx) - { - return entryIdx / kMaxEntriesPerPage; - } - - /// Calculate range [begin, end) of index for the list of entries that are currently visible that the path-th page would show. - /// i.e. when there is a filter, look into \c mActiveEntryIndices; when there is no filter, use directly. - static std::pair CalcRangeForPage(int page) - { - int begin = page * kMaxEntriesPerPage; - return { begin, begin + kMaxEntriesPerPage }; - } - - void DisplayMainTable() - { - if (ImGui::BeginTable("DataTable", kColumnCount, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX)) { - - if constexpr (kHasCustomer) ImGui::TableSetupColumn(I18N_TEXT("Customer", L10N_DATABASE_COLUMN_CUSTOMER)); - if constexpr (kHasDeadline) ImGui::TableSetupColumn(I18N_TEXT("Deadline", L10N_DATABASE_COLUMN_DEADLINE)); - if constexpr (kHasFactory) ImGui::TableSetupColumn(I18N_TEXT("Factory", L10N_DATABASE_COLUMN_FACTORY)); - if constexpr (kHasOrderTime) ImGui::TableSetupColumn(I18N_TEXT("Order time", L10N_DATABASE_COLUMN_ORDER_TIME)); - if constexpr (kHasCompletionTime) ImGui::TableSetupColumn(I18N_TEXT("Completion time", L10N_DATABASE_COLUMN_DELIVERY_TIME)); - if constexpr (kHasItems) ImGui::TableSetupColumn(I18N_TEXT("Items", L10N_DATABASE_COLUMN_ITEMS)); - ImGui::TableHeadersRow(); - - if (mActiveFilter) { - // TODO - auto [begin, end] = CalcRangeForPage(mCurrentPageNumber); - end = std::min(end, (int64_t)mActiveEntries.size() - 1); - for (int i = begin; i < end; ++i) { - int rowIdx = mActiveEntries[i]; - DisplayEntry(rowIdx); - } - } else { - int firstRowIdx = mCurrentPageNumber * kMaxEntriesPerPage; - auto& page = *mCurrentPage; - - int end = std::min((int)page.size(), kMaxEntriesPerPage); - for (int i = 0; i < end; ++i) { - DisplayEntry(page[i], i, firstRowIdx + i); - } - } - - ImGui::EndTable(); - } - } - - void DisplayEntry(int rowIdx) - { - // TODO - // auto [pageNumber, pageEntry] = SplitRowIndex(rowIdx); - // auto& entry = LoadAndGetPage(pageNumber)[pageEntry]; - // DisplayEntry(entry, rowIdx); - } - - void DisplayEntry(T& entry, int rowIdx, int entryIdx) - { - ImGui::PushID(rowIdx); - ImGui::TableNextRow(); - - if constexpr (kHasCustomer) { - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.Customer.c_str(), mSelectRow == rowIdx, ImGuiSelectableFlags_SpanAllColumns)) { - mSelectRow = rowIdx; - } - } - - if constexpr (kHasDeadline) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.Deadline.c_str()); - } - - if constexpr (kHasFactory) { - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.Factory.c_str(), mSelectRow == rowIdx, ImGuiSelectableFlags_SpanAllColumns)) { - mSelectRow = rowIdx; - } - } - - if constexpr (kHasOrderTime) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.OrderTime.c_str()); - } - - if constexpr (kHasCompletionTime) { - ImGui::TableNextColumn(); - if (entry.DeliveryTime.empty()) { - ImGui::TextUnformatted(I18N_TEXT("Not delivered", L10N_DATABASE_MESSAGE_NOT_DELIVERED)); - } else { - ImGui::TextUnformatted(entry.DeliveryTime.c_str()); - } - } - - if constexpr (kHasItems) { - ImGui::TableNextColumn(); - if (ImGui::TreeNode(entry.ItemsSummary.c_str())) { - DrawItems(entry.Items); - ImGui::TreePop(); - } - } - - ImGui::PopID(); - } - - void EditEntry(T& entry, int rowIdx, int entryIdx) - { - // TODO - } - - void DisplayDeliveriesTable() - { - if (ImGui::BeginTable("DeliveriesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX)) { - - ImGui::TableSetupColumn(I18N_TEXT("Shipment time", L10N_DATABASE_COLUMN_SHIPMENT_TIME)); - ImGui::TableSetupColumn(I18N_TEXT("Arrival time", L10N_DATABASE_COLUMN_ARRIVAL_TIME)); - ImGui::TableSetupColumn(I18N_TEXT("Items", L10N_DATABASE_COLUMN_ITEMS)); - ImGui::TableHeadersRow(); - - auto& entry = (*mCurrentPage)[mSelectRow]; - auto& deliveries = entry.AssociatedDeliveries; - for (auto& delivery : deliveries) { - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - ImGui::TextUnformatted(delivery.ShipmentTime.c_str()); - - ImGui::TableNextColumn(); - ImGui::TextUnformatted(delivery.ArriveTime.c_str()); - - ImGui::TableNextColumn(); - if (ImGui::TreeNode(delivery.ItemsSummary.c_str())) { - DrawItems(delivery.Items); - ImGui::TreePop(); - } - } - - ImGui::EndTable(); - } - } - - std::vector LoadItems(SQLite::Statement& stmt, int64_t id) - { - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - stmt.bind(1, id); - - std::vector entries; - int itemIdCol = stmt.getColumnIndex("ItemId"); - int countCol = stmt.getColumnIndex("Count"); - while (stmt.executeStep()) { - entries.push_back(Item{ - .ItemId = stmt.getColumn(itemIdCol).getInt(), - .Count = stmt.getColumn(countCol).getInt(), - }); - } - - return entries; - } - - std::string CreateItemsSummary(const std::vector& items) - { - if (items.empty()) { - return ""; - } - - std::string result; - for (int i = 0, max = std::min((int)items.size(), kSummaryItemCount); i < max; ++i) { - auto& name = mProject->Products.Find(items[i].ItemId)->GetName(); - if (result.length() + name.length() > kSummaryMaxLength) { - break; - } - - result += name; - result += ", "; - } - - // Remove ", " - result.pop_back(); - result.pop_back(); - - result += "..."; - - return result; - } - - std::vector LoadDeliveriesEntries(int64_t orderId, DeliveryDirection type) - { - bool outgoingFlag; - switch (type) { - case DeliveryDirection::FactoryToWarehouse: outgoingFlag = false; break; - case DeliveryDirection::WarehouseToCustomer: outgoingFlag = true; break; - } - - auto& stmt = mProject->Database.GetDeliveries().FilterByTypeAndId; - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - stmt.bind(1, orderId); - stmt.bind(2, outgoingFlag); - - std::vector entries; - int rowIdCol = stmt.getColumnIndex("Id"); - int sendTimeCol = stmt.getColumnIndex("ShipmentTime"); - int arrivalTimeCol = stmt.getColumnIndex("ArrivalTime"); - while (stmt.executeStep()) { - auto items = LoadItems( - mProject->Database.GetDeliveries().GetItems, - stmt.getColumn(rowIdCol).getInt64()); - auto summary = CreateItemsSummary(items); - - entries.push_back(DeliveryEntry{ - .Items = std::move(items), - .ItemsSummary = std::move(summary), - .ShipmentTime = TimeUtils::StringifyTimeStamp(stmt.getColumn(arrivalTimeCol).getInt64()), - .ArriveTime = TimeUtils::StringifyTimeStamp(stmt.getColumn(sendTimeCol).getInt64()), - .Direction = type, - }); - } - - return entries; - } - - Page& LoadAndGetPage(int page) - { - auto iter = mPages.find(page); - if (iter != mPages.end()) { - return iter.value(); - } - - auto& stmt = *Statements.GetRows; - // clang-format off - DEFER { stmt.reset(); }; - // clang-format on - - stmt.bind(1, kMaxEntriesPerPage); - stmt.bind(2, page * kMaxEntriesPerPage); - - // If a field flag is false, the column index won't be used (controlled by other if constexpr's downstream) - // so there is no UB here - - // This column is always necessary (and present) because the deliveries table require it - int idCol = stmt.getColumnIndex("Id"); - - int customerCol; - if constexpr (kHasCustomer) customerCol = stmt.getColumnIndex("Customer"); - - int deadlineCol; - if constexpr (kHasDeadline) deadlineCol = stmt.getColumnIndex("Deadline"); - - int factoryCol; - if constexpr (kHasFactory) factoryCol = stmt.getColumnIndex("Factory"); - - int orderTimeCol; - if constexpr (kHasOrderTime) orderTimeCol = stmt.getColumnIndex("OrderTime"); - - int deliveryTimeCol; - if constexpr (kHasCompletionTime) deliveryTimeCol = stmt.getColumnIndex("DeliveryTime"); - - Page entries; - while (stmt.executeStep()) { - auto& entry = entries.emplace_back(); - - auto id = stmt.getColumn(idCol).getInt64(); - entry.AssociatedDeliveries = LoadDeliveriesEntries(id, T::kType); - - if constexpr (kHasItems) { - auto items = LoadItems( - *Statements.GetItems, - id); - auto itemsSummary = CreateItemsSummary(items); - entry.Items = std::move(items); - entry.ItemsSummary = std::move(itemsSummary); - } - - if constexpr (kHasCustomer) { - auto customerId = stmt.getColumn(customerCol).getInt(); - entry.Customer = mProject->Customers.Find(customerId)->GetName(); - } - - if constexpr (kHasDeadline) { - auto timeStamp = stmt.getColumn(deadlineCol).getInt64(); - entry.Deadline = TimeUtils::StringifyTimeStamp(timeStamp); - } - - if constexpr (kHasFactory) { - auto factoryId = stmt.getColumn(factoryCol).getInt(); - entry.Factory = mProject->Factories.Find(factoryId)->GetName(); - } - - if constexpr (kHasOrderTime) { - auto timeStamp = stmt.getColumn(orderTimeCol).getInt64(); - entry.OrderTime = TimeUtils::StringifyTimeStamp(timeStamp); - } - - if constexpr (kHasCompletionTime) { - auto timeStamp = stmt.getColumn(deliveryTimeCol).getInt64(); - entry.DeliveryTime = TimeUtils::StringifyTimeStamp(timeStamp); - } - } - - auto [res, _] = mPages.try_emplace(page, std::move(entries)); - return res.value(); - } - - void DrawItems(const std::vector& items) - { - for (auto& item : items) { - auto& name = mProject->Products.Find(item.ItemId)->GetName(); - ImGui::Text("%s × %d", name.c_str(), item.Count); - } - } - - void UpdateLastPage() - { - mLastPage = mActiveEntries.empty() - ? CalcPageForRowId(mRowCount) - : CalcPageForRowId(mActiveEntries.back()); - } -}; - -class SalesTableView : public GenericTableView -{ -public: - SalesTableView() - { - mEditDialogTitle = I18N_TEXT("Edit sales entry", L10N_DATABASE_SALES_VIEW_EDIT_DIALOG_TITLE); - } - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "HidingNonVirtualFunction" - void OnProjectChanged(Project* newProject) - { - auto& table = newProject->Database.GetSales(); - Statements.GetRowCount = &table.GetRowCount; - Statements.GetRows = &table.GetRows; - Statements.GetItems = &table.GetItems; - // TODO - // stmts.FilterRowsStatement = ; - - GenericTableView::OnProjectChanged(newProject); - } -#pragma clang diagnostic pop -}; - -class PurchasesTableView : public GenericTableView -{ -public: - PurchasesTableView() - { - mEditDialogTitle = I18N_TEXT("Edit purchase entry", L10N_DATABASE_PURCHASES_VIEW_EDIT_DIALOG_TITLE); - } - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "HidingNonVirtualFunction" - void OnProjectChanged(Project* newProject) - { - auto& table = newProject->Database.GetPurchases(); - Statements.GetRowCount = &table.GetRowCount; - Statements.GetRows = &table.GetRows; - Statements.GetItems = &table.GetItems; - // TODO - // stmts.FilterRowsStatement = ; - - GenericTableView::OnProjectChanged(newProject); - } -#pragma clang diagnostic pop -}; -} // namespace CPLT_UNITY_ID - -void UI::DatabaseViewTab() -{ - auto& gs = GlobalStates::GetInstance(); - - static Project* currentProject = nullptr; - static CPLT_UNITY_ID::SalesTableView sales; - static CPLT_UNITY_ID::PurchasesTableView purchases; - - if (currentProject != gs.GetCurrentProject()) { - currentProject = gs.GetCurrentProject(); - sales.OnProjectChanged(currentProject); - purchases.OnProjectChanged(currentProject); - } - - if (ImGui::BeginTabBar("DatabaseViewTabs")) { - if (ImGui::BeginTabItem(I18N_TEXT("Sales", L10N_DATABASE_SALES_VIEW_TAB_NAME))) { - sales.Display(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem(I18N_TEXT("Purchases", L10N_DATABASE_PURCHASES_VIEW_TAB_NAME))) { - purchases.Display(); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } -} diff --git a/core/src/UI/UI_Items.cpp b/core/src/UI/UI_Items.cpp deleted file mode 100644 index a33c61b..0000000 --- a/core/src/UI/UI_Items.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include "UI.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Utils/I18n.hpp" - -#include -#include -#include - -namespace CPLT_UNITY_ID { - -enum class ActionResult -{ - Confirmed, - Canceled, - Pending, -}; - -/// \param list Item list that the item is in. -/// \param item A non-null pointer to the currently being edited item. It should not change until this function returns a non-\c ActionResult::Pending value. -template -ActionResult ItemEditor(ItemList& list, T* item) -{ - constexpr bool kHasDescription = requires(T t) - { - t.GetDescription(); - }; - constexpr bool kHasEmail = requires(T t) - { - t.GetEmail(); - }; - - static bool duplicateName = false; - - static std::string name; - static std::string description; - static std::string email; - if (name.empty()) { - name = item->GetName(); - if constexpr (kHasDescription) description = item->GetDescription(); - if constexpr (kHasEmail) email = item->GetEmail(); - } - - auto ClearStates = [&]() { - duplicateName = false; - name = {}; - description = {}; - }; - - if (ImGui::InputText(I18N_TEXT("Name", L10N_ITEM_COLUMN_NAME), &name)) { - duplicateName = name != item->GetName() && list.Find(name) != nullptr; - } - if constexpr (kHasDescription) ImGui::InputText(I18N_TEXT("Description", L10N_ITEM_COLUMN_DESCRIPTION), &description); - if constexpr (kHasEmail) ImGui::InputText(I18N_TEXT("Email", L10N_ITEM_COLUMN_EMAIL), &email); - - if (name.empty()) { - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - } - if (duplicateName) { - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - } - - // Return Value - auto rv = ActionResult::Pending; - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM), name.empty() || duplicateName)) { - if (item->GetName() != name) { - item->SetName(std::move(name)); - } - if constexpr (kHasDescription) - if (item->GetDescription() != description) { - item->SetDescription(std::move(description)); - } - if constexpr (kHasEmail) - if (item->GetEmail() != email) { - item->SetEmail(std::move(email)); - } - - ImGui::CloseCurrentPopup(); - ClearStates(); - rv = ActionResult::Confirmed; - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - ClearStates(); - rv = ActionResult::Canceled; - } - - return rv; -} - -template -void ItemListEntries(ItemList& list, int& selectedIdx) -{ - constexpr bool kHasDescription = requires(T t) - { - t.GetDescription(); - }; - constexpr bool kHasEmail = requires(T t) - { - t.GetEmail(); - }; - constexpr bool kHasStock = requires(T t) - { - t.GetPrice(); - }; - constexpr bool kHasPrice = requires(T t) - { - t.GetPrice(); - }; - constexpr int kColumns = 1 /* Name column */ + kHasDescription + kHasEmail + kHasStock + kHasPrice; - - if (ImGui::BeginTable("", kColumns, ImGuiTableFlags_Borders)) { - - ImGui::TableSetupColumn(I18N_TEXT("Name", L10N_ITEM_COLUMN_NAME)); - if constexpr (kHasDescription) ImGui::TableSetupColumn(I18N_TEXT("Description", L10N_ITEM_COLUMN_DESCRIPTION)); - if constexpr (kHasEmail) ImGui::TableSetupColumn(I18N_TEXT("Email", L10N_ITEM_COLUMN_EMAIL)); - if constexpr (kHasStock) ImGui::TableSetupColumn(I18N_TEXT("Stock", L10N_ITEM_COLUMN_STOCK)); - if constexpr (kHasPrice) ImGui::TableSetupColumn(I18N_TEXT("Price", L10N_ITEM_COLUMN_PRICE)); - ImGui::TableHeadersRow(); - - size_t idx = 0; - for (auto& entry : list) { - if (entry.IsInvalid()) { - continue; - } - - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.GetName().c_str(), selectedIdx == idx, ImGuiSelectableFlags_SpanAllColumns)) { - selectedIdx = idx; - } - - if constexpr (kHasDescription) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.GetDescription().c_str()); - } - - if constexpr (kHasEmail) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.GetEmail().c_str()); - } - - if constexpr (kHasStock) { - ImGui::TableNextColumn(); - ImGui::Text("%d", entry.GetStock()); - } - - if constexpr (kHasPrice) { - ImGui::TableNextColumn(); - // TODO format in dollars - ImGui::Text("%d", entry.GetPrice()); - } - - idx++; - } - ImGui::EndTable(); - } -} - -template -void ItemListEditor(ItemList& list) -{ - bool opened = true; - static int selectedIdx = -1; - static T* editingItem = nullptr; - - if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Add item", L10N_ITEM_ADD_DIALOG_TITLE)); - - editingItem = &list.Insert(""); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Add item", L10N_ITEM_ADD_DIALOG_TITLE), &opened, ImGuiWindowFlags_AlwaysAutoResize)) { - switch (ItemEditor(list, editingItem)) { - case ActionResult::Confirmed: - editingItem = nullptr; - break; - case ActionResult::Canceled: - list.Remove(editingItem->GetId()); - editingItem = nullptr; - break; - default: - break; - } - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_EDIT " " I18N_TEXT("Edit", L10N_EDIT), selectedIdx == -1)) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Edit item", L10N_ITEM_EDIT_DIALOG_TITLE)); - - editingItem = list.Find(selectedIdx); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Edit item", L10N_ITEM_EDIT_DIALOG_TITLE), &opened, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ItemEditor(list, editingItem) != ActionResult::Pending) { - editingItem = nullptr; - } - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), selectedIdx == -1)) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Delete item", L10N_ITEM_DELETE_DIALOG_TITLE)); - - list.Remove(selectedIdx); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Delete item", L10N_ITEM_DELETE_DIALOG_TITLE), &opened, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::TextUnformatted(I18N_TEXT("Are you sure you want to delete this item?", L10N_ITEM_DELETE_DIALOG_MESSAGE)); - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM))) { - ImGui::CloseCurrentPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } - - ItemListEntries(list, selectedIdx); -} -} // namespace CPLT_UNITY_ID - -void UI::ItemsTab() -{ - auto& gs = GlobalStates::GetInstance(); - - if (ImGui::BeginTabBar("ItemViewTabs")) { - if (ImGui::BeginTabItem(I18N_TEXT("Products", L10N_ITEM_CATEGORY_PRODUCT))) { - CPLT_UNITY_ID::ItemListEditor(gs.GetCurrentProject()->Products); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem(I18N_TEXT("Factories", L10N_ITEM_CATEGORY_FACTORY))) { - CPLT_UNITY_ID::ItemListEditor(gs.GetCurrentProject()->Factories); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem(I18N_TEXT("Customers", L10N_ITEM_CATEGORY_CUSTOMER))) { - CPLT_UNITY_ID::ItemListEditor(gs.GetCurrentProject()->Customers); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } -} diff --git a/core/src/UI/UI_MainWindow.cpp b/core/src/UI/UI_MainWindow.cpp deleted file mode 100644 index d059359..0000000 --- a/core/src/UI/UI_MainWindow.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "UI.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Utils/I18n.hpp" - -#include -#include -#include -#include -#include -#include - -namespace fs = std::filesystem; - -namespace CPLT_UNITY_ID { -void ProjectTab_Normal() -{ - auto& gs = GlobalStates::GetInstance(); - - if (ImGui::Button(ICON_FA_TIMES " " I18N_TEXT("Close", L10N_CLOSE))) { - gs.SetCurrentProject(nullptr); - return; - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER " " I18N_TEXT("Open in filesystem", L10N_PROJECT_OPEN_IN_FILESYSTEM))) { - // TODO - } - - ImGui::Text("%s %s", I18N_TEXT("Project name", L10N_PROJECT_NAME), gs.GetCurrentProject()->GetName().c_str()); - ImGui::Text("%s %s", I18N_TEXT("Project path", L10N_PROJECT_PATH), gs.GetCurrentProject()->GetPathString().c_str()); -} - -void ProjectTab_NoProject() -{ - auto& gs = GlobalStates::GetInstance(); - - bool openedDummy = true; - bool openErrorDialog = false; - static std::string projectName; - static std::string dirName; - static fs::path dirPath; - static bool dirNameIsValid = false; - - auto TrySelectPath = [&](fs::path newPath) { - if (fs::exists(newPath)) { - dirNameIsValid = true; - dirName = newPath.string(); - dirPath = std::move(newPath); - } else { - dirNameIsValid = false; - } - }; - - if (ImGui::Button(I18N_TEXT("Create project....", L10N_PROJECT_NEW))) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Create project wizard", L10N_PROJECT_NEW_DIALOG_TITLE)); - } - - // Make it so that the modal dialog has a close button - if (ImGui::BeginPopupModal(I18N_TEXT("Create project wizard", L10N_PROJECT_NEW_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::InputTextWithHint("##ProjectName", I18N_TEXT("Project name", L10N_PROJECT_NAME), &projectName); - - if (ImGui::InputTextWithHint("##ProjectPath", I18N_TEXT("Project path", L10N_PROJECT_PATH), &dirName)) { - // Changed, validate value - TrySelectPath(fs::path(dirName)); - } - ImGui::SameLine(); - if (ImGui::Button("...")) { - auto selection = pfd::select_folder(I18N_TEXT("Project path", L10N_PROJECT_NEW_PATH_DIALOG_TITLE)).result(); - if (!selection.empty()) { - TrySelectPath(fs::path(selection)); - } - } - - if (projectName.empty()) { - ImGui::ErrorMessage("%s", I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - } - if (!dirNameIsValid) { - ImGui::ErrorMessage("%s", I18N_TEXT("Invalid path", L10N_INVALID_PATH_ERROR)); - } - - ImGui::Spacing(); - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM), !dirNameIsValid || projectName.empty())) { - ImGui::CloseCurrentPopup(); - - gs.SetCurrentProject(std::make_unique(std::move(dirPath), std::move(projectName))); - - // Dialog just got closed, reset states - projectName.clear(); - dirName.clear(); - dirPath.clear(); - dirNameIsValid = false; - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Open project...", L10N_PROJECT_OPEN))) { - auto selection = pfd::open_file(I18N_TEXT("Open project", L10N_PROJECT_OPEN_DIALOG_TITLE)).result(); - if (!selection.empty()) { - fs::path path(selection[0]); - - try { - // Project's constructor wants a path to directory containing cplt_project.json - gs.SetCurrentProject(std::make_unique(path.parent_path())); - openErrorDialog = false; - } catch (const std::exception& e) { - openErrorDialog = true; - } - } - } - - // TODO cleanup UI - // Recent projects - - ImGui::Separator(); - ImGui::TextUnformatted(I18N_TEXT("Recent projects", L10N_PROJECT_RECENTS)); - - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Clear", L10N_PROJECT_RECENTS_CLEAR))) { - gs.ClearRecentProjects(); - } - - auto& rp = gs.GetRecentProjects(); - // End of vector is the most recently used, so that appending has less overhead - size_t toRemoveIdx = rp.size(); - - if (rp.empty()) { - ImGui::TextUnformatted(I18N_TEXT("No recent projects", L10N_PROJECT_RECENTS_NONE_PRESENT)); - } else { - for (auto it = rp.rbegin(); it != rp.rend(); ++it) { - auto& [path, recent] = *it; - ImGui::TextUnformatted(recent.c_str()); - - size_t idx = std::distance(it, rp.rend()) - 1; - ImGui::PushID(idx); - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER_OPEN)) { - try { - gs.SetCurrentProject(std::make_unique(path)); - openErrorDialog = false; - } catch (const std::exception& e) { - openErrorDialog = true; - } - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(I18N_TEXT("Open this project", L10N_PROJECT_RECENTS_OPEN_TOOLTIP)); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH)) { - toRemoveIdx = idx; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(I18N_TEXT("Delete this project from the Recent Projects list, the project itself will not be affected", L10N_PROJECT_RECENTS_DELETE_TOOLTIP)); - } - - ImGui::PopID(); - } - } - - if (toRemoveIdx != rp.size()) { - gs.RemoveRecentProject(toRemoveIdx); - } - - if (openErrorDialog) { - ImGui::SetNextWindowCentered(); - ImGui::OpenPopup(I18N_TEXT("Error", L10N_ERROR)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Error", L10N_ERROR), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::ErrorMessage("%s", I18N_TEXT("Invalid project file", L10N_PROJECT_INVALID_PROJECT_FORMAT)); - ImGui::EndPopup(); - } -} -} // namespace CPLT_UNITY_ID - -void UI::MainWindow() -{ - auto& gs = GlobalStates::GetInstance(); - - auto windowSize = ImGui::GetMainViewport()->Size; - ImGui::SetNextWindowSize({ windowSize.x, windowSize.y }); - ImGui::SetNextWindowPos({ 0, 0 }); - ImGui::Begin("##MainWindow", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); - if (ImGui::BeginTabBar("MainWindowTabs")) { - if (ImGui::BeginTabItem(ICON_FA_COGS " " I18N_TEXT("Settings", L10N_MAIN_TAB_SETTINGS))) { - UI::SettingsTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_FILE " " I18N_TEXT("Project", L10N_MAIN_WINDOW_TAB_PROJECT), nullptr)) { - if (gs.HasCurrentProject()) { - CPLT_UNITY_ID::ProjectTab_Normal(); - } else { - CPLT_UNITY_ID::ProjectTab_NoProject(); - } - ImGui::EndTabItem(); - } - if (!gs.HasCurrentProject()) { - // No project open, simply skip all project specific tabs - goto endTab; - } - - if (ImGui::BeginTabItem(ICON_FA_DATABASE " " I18N_TEXT("Data", L10N_MAIN_WINDOW_TAB_DATABASE_VIEW))) { - UI::DatabaseViewTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_BOX " " I18N_TEXT("Items", L10N_MAIN_WINDOW_TAB_ITEMS))) { - UI::ItemsTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_SCROLL " " I18N_TEXT("Workflows", L10N_MAIN_WINDOW_TAB_WORKFLOWS))) { - UI::WorkflowsTab(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_FA_TABLE " " I18N_TEXT("Templates", L10N_MAIN_WINDOW_TAB_TEMPLATES))) { - UI::TemplatesTab(); - ImGui::EndTabItem(); - } - - endTab: - ImGui::EndTabBar(); - } - ImGui::End(); -} diff --git a/core/src/UI/UI_Settings.cpp b/core/src/UI/UI_Settings.cpp deleted file mode 100644 index 107b94c..0000000 --- a/core/src/UI/UI_Settings.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "UI/UI.hpp" - -#include - -void UI::SettingsTab() -{ - // TODO -} diff --git a/core/src/UI/UI_Templates.cpp b/core/src/UI/UI_Templates.cpp deleted file mode 100644 index cd15cb7..0000000 --- a/core/src/UI/UI_Templates.cpp +++ /dev/null @@ -1,977 +0,0 @@ -#include "UI.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "Model/Template/TableTemplate.hpp" -#include "Model/Template/TableTemplateIterator.hpp" -#include "Model/Template/Template.hpp" -#include "Utils/I18n.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace CPLT_UNITY_ID { -class TemplateUI -{ -public: - static std::unique_ptr CreateByKind(std::unique_ptr