From 4303d0be47526b35e5bb3e3be001da227dae5d96 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Fri, 9 Apr 2021 23:17:45 -0700 Subject: Fix crash on load entries - TODO format time properly - TODO add Purchases table visualization --- core/CMakeLists.txt | 2 +- core/src/Model/Project.cpp | 4 +- core/src/Model/Project.hpp | 13 +- core/src/Model/TransactionDatabase.cpp | 160 ------------------------ core/src/Model/TransactionDatabase.hpp | 50 -------- core/src/Model/TransactionsModel.cpp | 139 +++++++++++++++++++++ core/src/Model/TransactionsModel.hpp | 55 ++++++++ core/src/Model/fwd.hpp | 2 +- core/src/UI/UI_DatabaseView.cpp | 222 +++++++++++++++++++++------------ 9 files changed, 347 insertions(+), 300 deletions(-) delete mode 100644 core/src/Model/TransactionDatabase.cpp delete mode 100644 core/src/Model/TransactionDatabase.hpp create mode 100644 core/src/Model/TransactionsModel.cpp create mode 100644 core/src/Model/TransactionsModel.hpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 02fa74a..320a5de 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -38,7 +38,7 @@ add_source_group(MODEL_MODULE_SOURCES src/Model/GlobalStates.cpp src/Model/Items.cpp src/Model/Project.cpp - src/Model/TransactionDatabase.cpp + src/Model/TransactionsModel.cpp ) add_source_group(UI_MODULE_SOURCES diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp index 2a79e3f..e762efb 100644 --- a/core/src/Model/Project.cpp +++ b/core/src/Model/Project.cpp @@ -82,11 +82,11 @@ void Project::SetName(std::string name) { mName = std::move(name); } -const TransactionDatabase& Project::GetDatabase() const { +const TransactionModel& Project::GetTransactionsModel() const { return mDb; } -TransactionDatabase& Project::GetDatabase() { +TransactionModel& Project::GetTransactionsModel() { return mDb; } diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp index dca10d0..748ca82 100644 --- a/core/src/Model/Project.hpp +++ b/core/src/Model/Project.hpp @@ -1,7 +1,7 @@ #pragma once #include "Model/Items.hpp" -#include "Model/TransactionDatabase.hpp" +#include "Model/TransactionsModel.hpp" #include #include @@ -17,7 +17,7 @@ private: std::filesystem::path mRootPath; std::string mRootPathString; std::string mName; - TransactionDatabase mDb; + TransactionModel mDb; public: /// Load the project from a directory containing the cplt_project.json file. @@ -27,11 +27,6 @@ public: /// This function assumes the given directory will exist and is empty. Project(std::filesystem::path rootPath, std::string name); - Project(const Project&) = delete; - Project& operator=(const Project&) = delete; - Project(Project&&) = default; - Project& operator=(Project&&) = default; - /// Path to a *directory* that contains the project file. const std::filesystem::path& GetPath() const; const std::string& GetPathString() const; @@ -39,8 +34,8 @@ public: const std::string& GetName() const; void SetName(std::string name); - const TransactionDatabase& GetDatabase() const; - TransactionDatabase& GetDatabase(); + const TransactionModel& GetTransactionsModel() const; + TransactionModel& GetTransactionsModel(); Json::Value Serialize(); void WriteToDisk(); diff --git a/core/src/Model/TransactionDatabase.cpp b/core/src/Model/TransactionDatabase.cpp deleted file mode 100644 index 766727d..0000000 --- a/core/src/Model/TransactionDatabase.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "TransactionDatabase.hpp" - -#include "Model/Project.hpp" - -#include -#include - -namespace fs = std::filesystem; - -SalesTable::SalesTable(TransactionDatabase& db) - // language=SQLite - : GetRowsStatement(db.GetSQLite(), R"""( -SELECT * FROM Sales WHERE rowid >= ? AND rowid < ? -)""") - // language=SQLite - // TODO - , FilterRowsStatement(db.GetSQLite(), R"""( -)""") { -} - -int SalesTable::GetEntryCont() const { - // TODO - return 0; -} - -DeliveryTable::DeliveryTable(TransactionDatabase& db) { -} - -PurchasesTable::PurchasesTable(TransactionDatabase& db) { -} - -static std::string GetDatabaseFilePath(const Project& project) { - auto dbsDir = project.GetPath() / "databases"; - fs::create_directories(dbsDir); - - auto dbFile = dbsDir / "transactions.sqlite3"; - return dbFile.string(); -} - -TransactionDatabase::TransactionDatabase(Project& project) - : mProject{ &project } - , mDb(GetDatabaseFilePath(project), SQLite::OPEN_READWRITE) - , mSales(*this) - , mPurchases(*this) - , mDeliveries(*this) { - // Schema - // - Customer: the customer item ID - // - Deadline: unix epoch time of order deadline - // - DeliveryTime: the time this order was completed (through a set of deliveries) - if (!mDb.tableExists("Sales")) { - // language=SQLite - mDb.exec(R"""( -CREATE TABLE Sales( - INT PRIMARY KEY, - Customer INT, - Deadline DATETIME, - DeliveryTime DATETIME -); -)"""); - } - - if (!mDb.tableExists("SalesItems")) { - // language=SQLite - mDb.exec(R"""( -CREATE TABLE SalesItems( - SaleId INT, - ItemId INT, - Count INT -); -)"""); - } - - // Schema - // - Factory: the factory id, - // - OrderTime: the time this order was made - // - DeliveryTime: the time this order was completed (through a set of deliveries) - if (!mDb.tableExists("Purchases")) { - // language=SQLite - mDb.exec(R"""( -CREATE TABLE Purchases( - INT PRIMARY KEY, - Factory INT, - OrderTime DATETIME, - DeliveryTime DATETIME -); -)"""); - } - - if (!mDb.tableExists("PurchasesItems")) { - // language=SQLite - mDb.exec(R"""( -CREATE TABLE PurchasesItems( - PurchaseId INT, - ItemId INT, - Count INT -); -)"""); - } - - // Schema - // - SendTime: unix epoch time of sending to delivery - // - ArriveTime: unix epoch time of delivery arrived at warehouse; 0 if not arrived yet - // - AssociatedOrder: rowid of the order that this delivery is completing (which table: Outgoing=true -> Sales, Outgoing=false -> Purchases) - // - Outgoing: true if the delivery is from warehouse to customer; false if the delivery is from factory to warehouse - if (!mDb.tableExists("Deliveries")) { - // language=SQLite - mDb.exec(R"""( -CREATE TABLE Deliveries( - INT PRIMARY KEY, - SendTime DATETIME, - ArriveTime DATETIME, - AssociatedOrder INT, - Outgoing BOOLEAN -); -)"""); - } - - if (!mDb.tableExists("DeliveriesItems")) { - // language=SQLite - mDb.exec(R"""( -CREATE TABLE DeliveriesItems( - DeliveryId INT, - ItemId INT, - Count INT -); -)"""); - } -} - -const SQLite::Database& TransactionDatabase::GetSQLite() const { - return mDb; -} - -SQLite::Database& TransactionDatabase::GetSQLite() { - return mDb; -} - -const SalesTable& TransactionDatabase::GetSales() const { - return mSales; -} - -SalesTable& TransactionDatabase::GetSales() { - return mSales; -} - -const PurchasesTable& TransactionDatabase::GetPurchases() const { - return mPurchases; -} - -PurchasesTable& TransactionDatabase::GetPurchases() { - return mPurchases; -} - -const DeliveryTable& TransactionDatabase::GetDeliveries() const { - return mDeliveries; -} - -DeliveryTable& TransactionDatabase::GetDeliveries() { - return mDeliveries; -} diff --git a/core/src/Model/TransactionDatabase.hpp b/core/src/Model/TransactionDatabase.hpp deleted file mode 100644 index 9c869c4..0000000 --- a/core/src/Model/TransactionDatabase.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "cplt_fwd.hpp" - -#include -#include -#include - -class SalesTable { -public: - SQLite::Statement GetRowsStatement; - SQLite::Statement FilterRowsStatement; - -public: - SalesTable(TransactionDatabase& db); - - int GetEntryCont() const; -}; - -class PurchasesTable { -public: - PurchasesTable(TransactionDatabase& db); -}; - -class DeliveryTable { -public: - DeliveryTable(TransactionDatabase& db); -}; - -class TransactionDatabase { -private: - Project* mProject; - SQLite::Database mDb; - SalesTable mSales; - PurchasesTable mPurchases; - DeliveryTable mDeliveries; - -public: - TransactionDatabase(Project& project); - - const SQLite::Database& GetSQLite() const; - SQLite::Database& GetSQLite(); - - const SalesTable& GetSales() const; - SalesTable& GetSales(); - const PurchasesTable& GetPurchases() const; - PurchasesTable& GetPurchases(); - const DeliveryTable& GetDeliveries() const; - DeliveryTable& GetDeliveries() -}; diff --git a/core/src/Model/TransactionsModel.cpp b/core/src/Model/TransactionsModel.cpp new file mode 100644 index 0000000..6035528 --- /dev/null +++ b/core/src/Model/TransactionsModel.cpp @@ -0,0 +1,139 @@ +#include "TransactionsModel.hpp" + +#include "Model/Project.hpp" + +#include +#include + +namespace fs = std::filesystem; + +SalesTable::SalesTable(TransactionModel& db) + // language=SQLite + : GetRowsStatement(db.GetSQLite(), R"""( +SELECT * FROM Sales WHERE rowid >= ? AND rowid < ? +)""") + // language=SQLite + // TODO + , FilterRowsStatement(db.GetSQLite(), R"""( +)""") { +} + +DeliveryTable::DeliveryTable(TransactionModel& db) { +} + +PurchasesTable::PurchasesTable(TransactionModel& db) { +} + +static std::string GetDatabaseFilePath(const Project& project) { + auto dbsDir = project.GetPath() / "databases"; + fs::create_directories(dbsDir); + + auto dbFile = dbsDir / "transactions.sqlite3"; + return dbFile.string(); +} + +/// Wrapper for SQLite::Database that creates the default tables +TransactionModel::DatabaseWrapper::DatabaseWrapper(TransactionModel& self) + : mSqlite(GetDatabaseFilePath(*self.mProject), SQLite::OPEN_READWRITE) { + + // If this table doesn't exist, the database probably just got initialized + if (mSqlite.tableExists("Sales")) { + return; + } + + // 'Sales' schema + // - Customer: the customer item ID + // - Deadline: unix epoch time of order deadline + // - DeliveryTime: the time this order was completed (through a set of deliveries) + + // 'Purchases' schema + // - Factory: the factory id, + // - OrderTime: the time this order was made + // - DeliveryTime: the time this order was completed (through a set of deliveries) + + // 'Deliveries' schema + // - SendTime: unix epoch time of sending to delivery + // - ArriveTime: unix epoch time of delivery arrived at warehouse; 0 if not arrived yet + // - AssociatedOrder: rowid of the order that this delivery is completing (which table: Outgoing=true -> Sales, Outgoing=false -> Purchases) + // - Outgoing: true if the delivery is from warehouse to customer; false if the delivery is from factory to warehouse + + // language=SQLite + mSqlite.exec(R"""( +CREATE TABLE IF NOT EXISTS Sales( + INT PRIMARY KEY, + Customer INT, + Deadline DATETIME, + DeliveryTime DATETIME +); +CREATE TABLE IF NOT EXISTS SalesItems( + SaleId INT, + ItemId INT, + Count INT +); + +CREATE TABLE IF NOT EXISTS Purchases( + INT PRIMARY KEY, + Factory INT, + OrderTime DATETIME, + DeliveryTime DATETIME +); +CREATE TABLE IF NOT EXISTS PurchasesItems( + PurchaseId INT, + ItemId INT, + Count INT +); + +CREATE TABLE IF NOT EXISTS Deliveries( + INT PRIMARY KEY, + SendTime DATETIME, + ArriveTime DATETIME, + AssociatedOrder INT, + Outgoing BOOLEAN +); +CREATE TABLE IF NOT EXISTS DeliveriesItems( + DeliveryId INT, + ItemId INT, + Count INT +); +)"""); +} + +TransactionModel::TransactionModel(Project& project) + : mProject{ &project } + , mDbWrapper(*this) + , mSales(*this) + , mPurchases(*this) + , mDeliveries(*this) { +} + +const SQLite::Database& TransactionModel::GetSQLite() const { + return mDbWrapper.mSqlite; +} + +SQLite::Database& TransactionModel::GetSQLite() { + return mDbWrapper.mSqlite; +} + +const SalesTable& TransactionModel::GetSales() const { + return mSales; +} + +SalesTable& TransactionModel::GetSales() { + return mSales; +} + +const PurchasesTable& TransactionModel::GetPurchases() const { + return mPurchases; +} + +PurchasesTable& TransactionModel::GetPurchases() { + return mPurchases; +} + +const DeliveryTable& TransactionModel::GetDeliveries() const { + return mDeliveries; +} + +DeliveryTable& TransactionModel::GetDeliveries() { + return mDeliveries; +} diff --git a/core/src/Model/TransactionsModel.hpp b/core/src/Model/TransactionsModel.hpp new file mode 100644 index 0000000..beed663 --- /dev/null +++ b/core/src/Model/TransactionsModel.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include "cplt_fwd.hpp" + +#include +#include +#include + +class SalesTable { +public: + SQLite::Statement GetRowsStatement; + SQLite::Statement FilterRowsStatement; + +public: + SalesTable(TransactionModel& db); +}; + +class PurchasesTable { +public: + PurchasesTable(TransactionModel& db); +}; + +class DeliveryTable { +public: + DeliveryTable(TransactionModel& db); +}; + +// TODO fuck SQLite::Statement has move ctor but not move assignment operator +class TransactionModel { +private: + class DatabaseWrapper { + public: + SQLite::Database mSqlite; + DatabaseWrapper(TransactionModel& self); + }; + + Project* mProject; + DatabaseWrapper mDbWrapper; + SalesTable mSales; + PurchasesTable mPurchases; + DeliveryTable mDeliveries; + +public: + TransactionModel(Project& project); + + const SQLite::Database& GetSQLite() const; + SQLite::Database& GetSQLite(); + + const SalesTable& GetSales() const; + SalesTable& GetSales(); + const PurchasesTable& GetPurchases() const; + PurchasesTable& GetPurchases(); + const DeliveryTable& GetDeliveries() const; + DeliveryTable& GetDeliveries(); +}; diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp index e153923..4bd508c 100644 --- a/core/src/Model/fwd.hpp +++ b/core/src/Model/fwd.hpp @@ -22,4 +22,4 @@ class Project; class SalesTable; class PurchasesTable; class DeliveryTable; -class TransactionDatabase; +class TransactionModel; diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp index 2b74918..8791c5b 100644 --- a/core/src/UI/UI_DatabaseView.cpp +++ b/core/src/UI/UI_DatabaseView.cpp @@ -1,5 +1,6 @@ #include "UI.hpp" +#include "Model/Filter.hpp" #include "Model/Project.hpp" #include "UI/Localization.hpp" #include "UI/States.hpp" @@ -10,6 +11,7 @@ #include #include #include +#include #include #include @@ -25,6 +27,13 @@ public: std::string DeliveryTime; }; +class PurchaseEntry { +public: + std::string Factory; + std::string OrderTime; + std::string DeliveryTime; +}; + class SalesViewTab { private: Project* mProject; @@ -42,38 +51,63 @@ private: /// A cached, contiguous (row id of each entry is monotonically increasing, but not necessarily starts at 0) list ready-to-be-presented entries. May be incomplete. std::vector mEntries; - // TODO this is very impractical (running filter in client code instead of letting SQLite doing it) - // Maybe simply cache a list of indices produced by a sql query? - - /// A bitset of all active (should-be-displayed) entries based on current filter, updated whenever \c mActiveFilter is updated. - /// Should have the exact same size as \c entries. - std::vector mActiveEntries; - - /// A cached list of index to entries that should be displayed on the current page. - std::vector mCurrentPageEntries; + /// A vector of row ids of entries (in \c mEntries) that are visible under the current filter. To use these indices, the elements should be mapped to \c mEntries + /// index by offsetting by \c mFirstCachedRowId. + std::vector mActiveEntries; - /// The current page the user is on. - int mCurrentPage; /// Last possible page for the current set table and filter (inclusive). int mLastPage; + /// The current page the user is on. + int mCurrentPage; - /* UI states */ - - int mSelectedEntry; + int mSelectedEntryRowId; public: + Project* GetProject() const { + return mProject; + } + void OnProjectChanged(Project* newProject) { mProject = newProject; + mFirstCachedRowId = 0; + mLastCachedRowId = 0; + mEntries.clear(); - mCurrentPage = 0; - mLastPage = -1; + mActiveEntries.clear(); - mSelectedEntry = -1; + UpdateLastPage(); + SetPage(0); + + mSelectedEntryRowId = -1; + } + + TableRowsFilter* GetFilter() const { + return mActiveFilter.get(); + } + + void OnFilterChanged() { + auto& stmt = mProject->GetTransactionsModel().GetSales().FilterRowsStatement; + DEFER { + stmt.reset(); + }; + + // TODO lazy loading when too many results + mActiveEntries.clear(); + int columnIdx = stmt.getColumnIndex("rowid"); + while (stmt.executeStep()) { + mActiveEntries.push_back(stmt.getColumn(columnIdx).getInt()); + } + + UpdateLastPage(); + SetPage(0); + + mSelectedEntryRowId = -1; } void OnFilterChanged(std::unique_ptr filter) { - // TODO + mActiveFilter = std::move(filter); + OnFilterChanged(); } void Draw() { @@ -94,7 +128,7 @@ public: } ImGui::SameLine(); - if (ImGui::Button(ls->Edit.Get(), mSelectedEntry == -1)) { + if (ImGui::Button(ls->Edit.Get(), mSelectedEntryRowId == -1)) { ImGui::OpenPopup(ls->EditSaleEntryDialogTitle.Get()); } if (ImGui::BeginPopupModal(ls->EditSaleEntryDialogTitle.Get(), &dummy, ImGuiWindowFlags_AlwaysAutoResize)) { @@ -109,22 +143,17 @@ public: ImGui::TableSetupColumn(ls->DatabaseDeliveryTimeColumn.Get()); ImGui::TableHeadersRow(); - for (int i : mCurrentPageEntries) { - auto& entry = mEntries[i]; - - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.Customer.c_str(), mSelectedEntry == i)) { - mSelectedEntry = i; + auto [begin, end] = CalcRangeForPage(mCurrentPage); + if (mActiveFilter) { + end = std::min(end, (int64_t)mActiveEntries.size() - 1); + for (int i = begin; i < end; ++i) { + int rowId = mActiveEntries[i]; + DrawEntry(GetEntry(rowId), rowId); } - - ImGui::NextColumn(); - ImGui::Text("%s", entry.Deadline.c_str()); - - ImGui::NextColumn(); - if (entry.DeliveryTime.empty()) { - ImGui::Text("%s", ls->NotDeliveredMessage.Get()); - } else { - ImGui::Text("%s", entry.DeliveryTime.c_str()); + } else { + end = std::min(end, mLastCachedRowId); + for (int rowId = begin; rowId < end; ++rowId) { + DrawEntry(GetEntry(rowId), rowId); } } @@ -132,23 +161,43 @@ public: } } + void DrawEntry(const SaleEntry& entry, int rowId) { + auto ls = LocaleStrings::Instance.get(); + + ImGui::TableNextColumn(); + if (ImGui::Selectable(entry.Customer.c_str(), mSelectedEntryRowId == rowId, ImGuiSelectableFlags_SpanAllColumns)) { + mSelectedEntryRowId = rowId; + } + + ImGui::TableNextColumn(); + ImGui::Text("%s", entry.Deadline.c_str()); + + ImGui::TableNextColumn(); + if (entry.DeliveryTime.empty()) { + ImGui::Text("%s", ls->NotDeliveredMessage.Get()); + } else { + ImGui::Text("%s", entry.DeliveryTime.c_str()); + } + } + + const SaleEntry& GetEntry(int64_t rowId) const { + return mEntries[RowIdToIndex(rowId)]; + } + + SaleEntry& GetEntry(int64_t rowId) { + return mEntries[RowIdToIndex(rowId)]; + } + private: void SetPage(int page) { - if (mCurrentPage != page) return; - mCurrentPage = page; EnsureCacheCoversPage(page); + } - auto [begin, end] = CalcRangeForPage(page); - begin -= mFirstCachedRowId; - end -= mFirstCachedRowId; - - mCurrentPageEntries.clear(); - for (auto i = begin; i < end; ++i) { - if (mActiveEntries[i]) { - mCurrentPageEntries.push_back(i); - } - } + void UpdateLastPage() { + mLastPage = mActiveEntries.empty() + ? 0 + : CalcPageForRowId(mActiveEntries.back()); } void EnsureCacheCoversPage(int page) { @@ -166,11 +215,11 @@ private: bool doRebuild = false; if (firstRow < mFirstCachedRowId) { - newFirst = CalcPageForRowId(firstRow) * kMaxEntriesPerPage; + newFirst = (CalcPageForRowId(firstRow) + 1) * kMaxEntriesPerPage; doRebuild = true; } if (lastRow > mLastCachedRowId) { - newLast = CalcPageForRowId(lastRow) * kMaxEntriesPerPage; + newLast = (CalcPageForRowId(lastRow) + 1) * kMaxEntriesPerPage; doRebuild = true; } if (!doRebuild) return; @@ -178,12 +227,12 @@ private: auto front = LoadRange(newFirst, mFirstCachedRowId); auto back = LoadRange(mLastCachedRowId + 1, newLast + 1); - mEntries.insert(mEntries.begin(), front.begin(), front.end()); - mEntries.insert(mEntries.end(), back.begin(), back.end()); - // TODO update mActiveEntries + mFirstCachedRowId -= front.size(); + mLastCachedRowId += back.size(); - mFirstCachedRowId = newFirst; - mLastCachedRowId = newLast; + // TODO don't reallocate buffer/move elements around all the time. Maybe use a linked list of buckets? + mEntries.insert(mEntries.begin(), std::make_move_iterator(front.begin()), std::make_move_iterator(front.end())); + mEntries.insert(mEntries.end(), std::make_move_iterator(back.begin()), std::make_move_iterator(back.end())); } std::vector LoadRange(int64_t begin, int64_t end) { @@ -196,7 +245,7 @@ private: result.reserve(size); - auto& stmt = mProject->GetDatabase().GetSales().GetRowsStatement; + auto& stmt = mProject->GetTransactionsModel().GetSales().GetRowsStatement; DEFER { stmt.reset(); }; @@ -204,34 +253,54 @@ private: stmt.bind(1, begin); stmt.bind(2, end); - while (true) { - bool hasResult = stmt.executeStep(); - if (hasResult) { - result.push_back(stmt.getColumns()); - } else { - return result; + auto StringifyEpochTime = [](int64_t epoch) -> std::string { + if (epoch == 0) { + return ""; } + + auto t = static_cast(epoch); + std::string str(29, '\0'); + std::strftime(str.data(), 21, "%Y-%m-%dT%H:%M:%S.", std::localtime(&t)); + return str; + }; + + int customerCol = stmt.getColumnIndex("Customer"); + int deadlineCol = stmt.getColumnIndex("Deadline"); + int deliveryTimeCol = stmt.getColumnIndex("DeliveryTime"); + + while (stmt.executeStep()) { + auto customer = stmt.getColumn(customerCol).getInt(); + auto deadline = stmt.getColumn(deadlineCol).getInt64(); + auto deliveryTime = stmt.getColumn(deliveryTimeCol).getInt64(); + result.push_back(SaleEntry{ + .Customer = mProject->Customers.Find(customer)->GetName(), + .Deadline = StringifyEpochTime(deadline), + .DeliveryTime = StringifyEpochTime(deliveryTime), + }); } + return result; + } + + int RowIdToIndex(int64_t rowId) const { + return rowId - mFirstCachedRowId; + } + + int64_t IndexToRowId(int index) const { + return index + mFirstCachedRowId; } static int CalcPageForRowId(int64_t rowId) { return rowId / kMaxEntriesPerPage; } - /// Calculate range [begin, end) of row ids that the path-th page would show. + /// 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 }; } }; -class PurchaseEntry { -public: - std::string Factory; - std::string OrderTime; - std::string DeliveryTime; -}; - class PurchasesViewTab { private: std::vector mEntries; @@ -256,26 +325,25 @@ void UI::DatabaseViewTab() { auto& uis = UIState::GetInstance(); static Project* currentProject = nullptr; - static auto salesView = std::make_unique(); - static auto purchasesView = std::make_unique(); + static SalesViewTab salesView; + static PurchasesViewTab purchasesView; if (currentProject != uis.CurrentProject.get()) { currentProject = uis.CurrentProject.get(); - salesView->OnProjectChanged(currentProject); - purchasesView->OnProjectChanged(currentProject); + salesView.OnProjectChanged(currentProject); + purchasesView.OnProjectChanged(currentProject); } if (ImGui::BeginTabBar("##DatabaseViewTabs")) { if (ImGui::BeginTabItem(ls->SalesViewTab.Get())) { - salesView->Draw(); + salesView.Draw(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(ls->PurchasesViewTab.Get())) { - purchasesView->Draw(); + purchasesView.Draw(); ImGui::EndTabItem(); } - // if (ImGui::BeginTabItem(ls->DeliveriesTableTab.Get())) { - // ImGui::EndTabItem(); - // } + + ImGui::EndTabBar(); } } -- cgit v1.2.3-70-g09d2