diff options
author | rtk0c <[email protected]> | 2021-06-19 22:41:05 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2021-06-19 22:41:05 -0700 |
commit | 4378dcd83d2387534f3b10276365d1003832fe81 (patch) | |
tree | 6bc055a1bd81d5a27c9066f05e28e5115f5de944 /core/src | |
parent | eec8dfd8a21b8bb37f6acac1da84cde8fbf7ace7 (diff) |
Add cell type conversion mechanism
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/Locale/zh_CN.h | 9 | ||||
-rw-r--r-- | core/src/Model/Template/TableTemplate.cpp | 114 | ||||
-rw-r--r-- | core/src/Model/Template/TableTemplate.hpp | 39 | ||||
-rw-r--r-- | core/src/Model/Template/fwd.hpp | 2 | ||||
-rw-r--r-- | core/src/UI/UI.hpp | 2 | ||||
-rw-r--r-- | core/src/UI/UI_Templates.cpp | 166 | ||||
-rw-r--r-- | core/src/UI/UI_Utils.cpp | 9 |
7 files changed, 317 insertions, 24 deletions
diff --git a/core/src/Locale/zh_CN.h b/core/src/Locale/zh_CN.h index 8e0e3e3..d1f32ff 100644 --- a/core/src/Locale/zh_CN.h +++ b/core/src/Locale/zh_CN.h @@ -123,11 +123,20 @@ #define L10N_TEMPLATE_TABLE "表格模板" +#define L10N_TABLE "表格" +#define L10N_TABLE_CELL "单元格" +#define L10N_TABLE_CELL_CONV "转换类型..." +#define L10N_TABLE_CELL_CONV_PARAM "单元格参数" +#define L10N_TABLE_CELL_CONV_CREATE_AG "创建列表参数组" +#define L10N_TABLE_CELL_CONV_ADD_AG_LEFT "添加至左侧的列表参数组" +#define L10N_TABLE_CELL_CONV_ADD_AG_RIGHT "添加至右侧的列表参数组" +#define L10N_TABLE_CELL_SELECT_MSG "请单击选择一个单元格以编辑其属性" #define L10N_TABLE_CELL_HORIZONTAL_ALIGNMENT "水平对齐" #define L10N_TABLE_CELL_CONTENT "内容" #define L10N_TABLE_CELL_VAR_NAME "变量名" #define L10N_TABLE_CELL_VAR_TOOLTIP "参数单元格的唯一名称(表格内不得重复)。" #define L10N_TABLE_CELL_ARRAY_VAR_TOOLTIP "列表参数组内单个参数单元格的名称,在组内唯一(不同的参数组可以包含相同的参数名)。" +#define L10N_TABLE_CELL_VAR_NAME_DUP "参数名已重复" #define L10N_TABLE_CELL_VERTICAL_ALIGNMENT "垂直对齐" #define L10N_TABLE_CELL_ALIGN_LEFT "左对齐" #define L10N_TABLE_CELL_ALIGN_CENTER "居中" diff --git a/core/src/Model/Template/TableTemplate.cpp b/core/src/Model/Template/TableTemplate.cpp index be61606..88980ae 100644 --- a/core/src/Model/Template/TableTemplate.cpp +++ b/core/src/Model/Template/TableTemplate.cpp @@ -34,26 +34,30 @@ int TableCellArrayGroup::GetCount() const return RightCell - LeftCell + 1; } -TableInstanciationParameters::TableInstanciationParameters(const TableTemplate& table) +Vec2i TableCellArrayGroup::FindCell(std::string_view name) +{ +} + +TableInstantiationParameters::TableInstantiationParameters(const TableTemplate& table) : mTable{ &table } { } -TableInstanciationParameters& TableInstanciationParameters::ResetTable(const TableTemplate& newTable) +TableInstantiationParameters& TableInstantiationParameters::ResetTable(const TableTemplate& newTable) { mTable = &newTable; return *this; } -TableInstanciationParameters TableInstanciationParameters::RebindTable(const TableTemplate& newTable) const +TableInstantiationParameters TableInstantiationParameters::RebindTable(const TableTemplate& newTable) const { - TableInstanciationParameters result(newTable); + TableInstantiationParameters result(newTable); result.SingularCells = this->SingularCells; result.ArrayGroups = this->ArrayGroups; return result; } -const TableTemplate& TableInstanciationParameters::GetTable() const +const TableTemplate& TableInstantiationParameters::GetTable() const { return *mTable; } @@ -142,6 +146,102 @@ TableCell& TableTemplate::GetCell(Vec2i pos) return const_cast<TableCell&>(const_cast<const TableTemplate*>(this)->GetCell(pos)); } +void TableTemplate::SetCellType(Vec2i pos, TableCell::CellType type) +{ + auto& cell = GetCell(pos); + if (cell.Type == type) { + return; + } + + switch (type) { + case TableCell::ConstantCell: { + // TODO + } break; + + case TableCell::SingularParametricCell: { + cell.Type = type; + } break; + + // Setting the cell to be a part of a group requires calling the overload that supplies the group + case TableCell::ArrayParametricCell: break; + } +} + +int TableTemplate::GetArrayGroupCount() const +{ + return mArrayGroups.size(); +} + +const TableCellArrayGroup& TableTemplate::GetArrayGroup(int id) const +{ + return mArrayGroups[id]; +} + +TableCellArrayGroup& TableTemplate::GetArrayGroup(int id) +{ + return mArrayGroups[id]; +} + +TableCellArrayGroup& TableTemplate::AddArrayGroup(int row, int left, int right) +{ + assert(row >= 0 && row < GetTableHeight()); + assert(left >= 0 && left < GetTableWidth()); + assert(right >= 0 && right < GetTableWidth()); + + // TODO check for overlap + + if (left > right) { + std::swap(left, right); + } + + mArrayGroups.push_back(TableCellArrayGroup{ + .Row = row, + .LeftCell = left, + .RightCell = right, + }); + return mArrayGroups.back(); +} + +bool TableTemplate::ExtendArrayGroupLeft(int id, int n) +{ + assert(n > 0); + + auto& ag = mArrayGroups[id]; + ag.LeftCell -= n; + + return false; +} + +bool TableTemplate::ExtendArrayGroupRight(int id, int n) +{ + assert(n > 0); + + auto& ag = mArrayGroups[id]; + ag.RightCell += n; + + return false; +} + +TableCell* TableTemplate::FindCell(std::string_view name) +{ + auto iter = mName2Parameters.find(name); + if (iter != mName2Parameters.end()) { + return &mCells[iter.value()]; + } else { + return nullptr; + } +} + +TableCellArrayGroup* TableTemplate::FindArrayGroup(std::string_view name) +{ + auto iter = mName2ArrayGroups.find(name); + if (iter != mName2ArrayGroups.end()) { + return &mArrayGroups[iter.value()]; + } else { + return nullptr; + } +} + TableTemplate::MergeCellsResult TableTemplate::MergeCells(Vec2i topLeft, Vec2i bottomRight) { auto SortTwo = [](int& a, int& b) { @@ -200,14 +300,14 @@ TableTemplate::BreakCellsResult TableTemplate::BreakCells(Vec2i topLeft) return BCR_Success; } -lxw_workbook* TableTemplate::InstantiateToExcelWorkbook(const TableInstanciationParameters& params) const +lxw_workbook* TableTemplate::InstantiateToExcelWorkbook(const TableInstantiationParameters& params) const { auto workbook = workbook_new("Table.xlsx"); InstantiateToExcelWorksheet(workbook, params); return workbook; } -lxw_worksheet* TableTemplate::InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstanciationParameters& params) const +lxw_worksheet* TableTemplate::InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstantiationParameters& params) const { auto worksheet = workbook_add_worksheet(workbook, "CpltExport.xlsx"); diff --git a/core/src/Model/Template/TableTemplate.hpp b/core/src/Model/Template/TableTemplate.hpp index ed1a96f..754ebe1 100644 --- a/core/src/Model/Template/TableTemplate.hpp +++ b/core/src/Model/Template/TableTemplate.hpp @@ -5,8 +5,10 @@ #include "Utils/VectorHash.hpp" #include "cplt_fwd.hpp" +#include <tsl/array_map.h> #include <tsl/robin_map.h> #include <string> +#include <string_view> #include <vector> class TableCell @@ -80,11 +82,13 @@ public: /// \endcode /// /// \see TableCell -/// \see TableInstanciationParameters +/// \see TableInstantiationParameters /// \see TableTemplate class TableCellArrayGroup { public: + /// Parameter name mapped to cell location (index from LeftCell). + tsl::array_map<char, int> mName2Cell; int Row; /// Leftmost cell in this group int LeftCell; @@ -95,6 +99,9 @@ public: Vec2i GetLeftCell() const; Vec2i GetRightCell() const; int GetCount() const; + + /// Find the location of the cell within this array group that has the given name. + Vec2i FindCell(std::string_view name); }; // Forward declaration of libxlsxwriter structs @@ -103,7 +110,7 @@ struct lxw_worksheet; /// An object containing the necessary information to instanciate a table template. /// \see TableTemplate -class TableInstanciationParameters +class TableInstantiationParameters { private: const TableTemplate* mTable; @@ -116,10 +123,10 @@ public: std::vector<ArrayGroupData> ArrayGroups; public: - TableInstanciationParameters(const TableTemplate& table); + TableInstantiationParameters(const TableTemplate& table); - TableInstanciationParameters& ResetTable(const TableTemplate& newTable); - TableInstanciationParameters RebindTable(const TableTemplate& newTable) const; + TableInstantiationParameters& ResetTable(const TableTemplate& newTable); + TableInstantiationParameters RebindTable(const TableTemplate& newTable) const; const TableTemplate& GetTable() const; }; @@ -131,6 +138,10 @@ public: class TableTemplate : public Template { private: + /// Map from parameter name to index of the parameter cell (stored in mCells). + tsl::array_map<char, int> mName2Parameters; + /// Map from array group name to the index of the array group (stored in mArrayGroups). + tsl::array_map<char, int> mName2ArrayGroups; std::vector<TableCell> mCells; std::vector<TableCellArrayGroup> mArrayGroups; std::vector<int> mRowHeights; @@ -151,6 +162,20 @@ public: const TableCell& GetCell(Vec2i pos) const; TableCell& GetCell(Vec2i pos); + void SetCellType(Vec2i pos, TableCell::CellType type); + + int GetArrayGroupCount() const; + const TableCellArrayGroup& GetArrayGroup(int id) const; + TableCellArrayGroup& GetArrayGroup(int id); + TableCellArrayGroup& AddArrayGroup(int row, int left, int right); + bool ExtendArrayGroupLeft(int id, int n); + bool ExtendArrayGroupRight(int id, int n); + + /// Find a singular parameter cell by its name. This does not include cells within an array group. + TableCell* FindCell(std::string_view name); + + /// Find an array group by its name. + TableCellArrayGroup* FindArrayGroup(std::string_view name); enum MergeCellsResult { @@ -166,8 +191,8 @@ public: }; BreakCellsResult BreakCells(Vec2i topLeft); - lxw_workbook* InstantiateToExcelWorkbook(const TableInstanciationParameters& params) const; - lxw_worksheet* InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstanciationParameters& params) const; + lxw_workbook* InstantiateToExcelWorkbook(const TableInstantiationParameters& params) const; + lxw_worksheet* InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstantiationParameters& params) const; virtual ReadResult ReadFrom(std::istream& stream) override; virtual void WriteTo(std::ostream& stream) const override; diff --git a/core/src/Model/Template/fwd.hpp b/core/src/Model/Template/fwd.hpp index b0acb28..b918755 100644 --- a/core/src/Model/Template/fwd.hpp +++ b/core/src/Model/Template/fwd.hpp @@ -3,7 +3,7 @@ // TableTemplate.hpp class TableCell; class TableCellArrayGroup; -class TableInstanciationParameters; +class TableInstantiationParameters; class TableTemplate; // Template.hpp diff --git a/core/src/UI/UI.hpp b/core/src/UI/UI.hpp index 0a80b4c..b0f0a6f 100644 --- a/core/src/UI/UI.hpp +++ b/core/src/UI/UI.hpp @@ -13,6 +13,8 @@ void PopDisabled(); bool Button(const char* label, bool disabled); bool Button(const char* label, const ImVec2& sizeArg, bool disabled); +bool MenuItemConditional(const char* name, bool disabled); + void ErrorIcon(); void ErrorMessage(const char* fmt, ...); void WarningIcon(); diff --git a/core/src/UI/UI_Templates.cpp b/core/src/UI/UI_Templates.cpp index ab9c317..e77bef3 100644 --- a/core/src/UI/UI_Templates.cpp +++ b/core/src/UI/UI_Templates.cpp @@ -25,6 +25,10 @@ public: virtual void Display() = 0; }; +// Table template styles +constexpr ImU32 kSingleParamOutline = IM_COL32(255, 255, 0, 255); +constexpr ImU32 kArrayGroupOutline = IM_COL32(255, 0, 0, 255); + class TableTemplateUI : public TemplateUI { private: @@ -38,9 +42,41 @@ private: }; std::vector<UICell> mUICells; + struct UIArrayGroup + { + ImVec2 Pos; + ImVec2 Size; + }; + std::vector<UIArrayGroup> mUIArrayGroups; + + /* Selection range */ Vec2i mSelectionTL; Vec2i mSelectionBR; + /* Selection states */ + union + { + /// "CS" stands for "Constant cell selection States" + struct + { + } mCS{}; // Initialize to this element + + /// "SS" stands for "Singular parameter selection States". + struct + { + bool ErrorDuplicateVarName; + bool HasLeftAG; + bool HasRightAG; + } mSS; + + /// "AS" stands for "Array group parameter selection States". + struct + { + bool ErrorDuplicateVarName; + } mAS; + }; + + /* Table states */ bool mDirty = false; bool mFirstDraw = true; @@ -75,15 +111,38 @@ public: { mTable->Resize(width, height); mUICells.resize(width * height); + mUIArrayGroups.resize(mTable->GetArrayGroupCount()); + + for (size_t i = 0; i < mUIArrayGroups.size(); ++i) { + auto& ag = mTable->GetArrayGroup(i); + auto& uag = mUIArrayGroups[i]; + + auto itemSpacing = ImGui::GetStyle().ItemSpacing; + uag.Pos.x = 0; + for (int x = 0; x < ag.LeftCell; ++x) { + uag.Pos.x += mTable->GetColumnWidth(x) + itemSpacing.x; + } + uag.Pos.y = 0; + for (int y = 0; y < ag.Row; ++y) { + uag.Pos.y += mTable->GetRowHeight(y) + itemSpacing.y; + } + + uag.Size.x = mTable->GetRowHeight(ag.Row); + uag.Size.y = 0; + for (int x = ag.LeftCell; x <= ag.RightCell; ++x) { + uag.Size.y += mTable->GetColumnWidth(x); + } + } } private: void DisplayInspector() { + // This is an id, no need to localize if (ImGui::BeginTabBar("Inspector")) { - if (ImGui::BeginTabItem("Cell")) { + if (ImGui::BeginTabItem(I18N_TEXT("Cell", L10N_TABLE_CELL))) { if (!IsSelected()) { - ImGui::Text("Select a cell to edit"); + ImGui::Text(I18N_TEXT("Select a cell to edit", L10N_TABLE_CELL_SELECT_MSG)); } else if (mSelectionTL == mSelectionBR) { auto& selectCell = mTable->GetCell(mSelectionTL); DisplayCellProperties(mSelectionTL); @@ -93,8 +152,8 @@ private: ImGui::EndTabItem(); } - if (ImGui::BeginTabItem("Table")) { - DisplayTableInspector(); + if (ImGui::BeginTabItem(I18N_TEXT("Table", L10N_TABLE))) { + DisplayTableProperties(); ImGui::EndTabItem(); } @@ -107,6 +166,36 @@ private: auto& cell = mTable->GetCell(pos); auto& uiCell = mUICells[pos.y * mTable->GetTableWidth() + pos.x]; + if (ImGui::Button(I18N_TEXT("Convert to...", L10N_TABLE_CELL_CONV))) { + ImGui::OpenPopup("ConvertCtxMenu"); + } + if (ImGui::BeginPopup("ConvertCtxMenu")) { + if (ImGui::MenuItem(I18N_TEXT("Single parameter", L10N_TABLE_CELL_CONV_PARAM))) { + mTable->SetCellType(pos, TableCell::SingularParametricCell); + } + + bool createDisabled = cell.Type == TableCell::ArrayParametricCell; + if (ImGui::MenuItemConditional(I18N_TEXT("Create array group", L10N_TABLE_CELL_CONV_CREATE_AG), createDisabled)) { + mTable->AddArrayGroup(pos.x, pos.x, pos.y); + } + + bool leftDisabled = !mSS.HasLeftAG || cell.Type == TableCell::ArrayParametricCell; + if (ImGui::MenuItemConditional(I18N_TEXT("Add to array group to the left", L10N_TABLE_CELL_CONV_ADD_AG_LEFT), leftDisabled)) { + auto& leftCell = mTable->GetCell((Vec2i){ pos.x - 1, pos.y }); + mTable->ExtendArrayGroupRight(leftCell.DataId, 1); + } + + bool rightDisabled = !mSS.HasRightAG || cell.Type == TableCell::ArrayParametricCell; + if (ImGui::MenuItemConditional(I18N_TEXT("Add to array group to the right", L10N_TABLE_CELL_CONV_ADD_AG_RIGHT), rightDisabled)) { + auto& rightCell = mTable->GetCell((Vec2i){ pos.x + 1, pos.y }); + mTable->ExtendArrayGroupLeft(rightCell.DataId, 1); + } + + ImGui::EndPopup(); + } + + ImGui::Spacing(); + constexpr auto kLeft = I18N_TEXT("Left", L10N_TABLE_CELL_ALIGN_LEFT); constexpr auto kCenter = I18N_TEXT("Center", L10N_TABLE_CELL_ALIGN_CENTER); constexpr auto kRight = I18N_TEXT("Right", L10N_TABLE_CELL_ALIGN_RIGHT); @@ -162,20 +251,31 @@ private: case TableCell::SingularParametricCell: if (ImGui::InputText(I18N_TEXT("Variable name", L10N_TABLE_CELL_VAR_NAME), &cell.Content)) { - // TODO validate if name is repeat + auto c = mTable->FindCell(cell.Content); + mSS.ErrorDuplicateVarName = c != nullptr; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip(I18N_TEXT("Name of the parameter link to this cell.", L10N_TABLE_CELL_VAR_TOOLTIP)); } + + if (mSS.ErrorDuplicateVarName) { + ImGui::ErrorMessage(I18N_TEXT("Variable name duplicated.", L10N_TABLE_CELL_VAR_NAME_DUP)); + } break; case TableCell::ArrayParametricCell: if (ImGui::InputText(I18N_TEXT("Variable name", L10N_TABLE_CELL_VAR_NAME), &cell.Content)) { - // TODO validate if name is repeat + auto ag = mTable->GetArrayGroup(cell.DataId); + auto cl = ag.FindCell(cell.Content); + mAS.ErrorDuplicateVarName = cl.x != -1 && cl.y != -1; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip(I18N_TEXT("Name of the parameter lnk to this cell; local within the array group.", L10N_TABLE_CELL_ARRAY_VAR_TOOLTIP)); } + + if (mAS.ErrorDuplicateVarName) { + ImGui::ErrorMessage(I18N_TEXT("Variable name duplicated.", L10N_TABLE_CELL_VAR_NAME_DUP)); + } break; } } @@ -185,7 +285,7 @@ private: // TODO } - void DisplayTableInspector() + void DisplayTableProperties() { // TODO } @@ -193,6 +293,7 @@ private: void DisplayTable() { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 8)); ImGui::BeginChild("TableTemplate"); auto regularColor = ImGui::GetColorU32(ImGuiCol_Button); @@ -222,7 +323,6 @@ private: window->DC.CursorPos + ImGui::CalcItemSize(size, 0.0f, 0.0f), }; - // TODO draw indicator for parameter cells /* Draw cell body */ if (uiCell.Selected) { @@ -242,6 +342,22 @@ private: window->DrawList->AddRectFilled(rect.Min, rect.Max, color); } + /* Draw cell type indicator */ + switch (cell.Type) { + // No indicator + case TableCell::ConstantCell: break; + + case TableCell::SingularParametricCell: + window->DrawList->AddRect( + rect.Min - ImVec2(1, 1), + rect.Max + ImVec2(1, 1), + kSingleParamOutline); + break; + + // Drawn separately in loop below + case TableCell::ArrayParametricCell: break; + } + /* Draw cell content */ auto content = cell.Content.c_str(); @@ -283,8 +399,15 @@ private: } } + for (auto& uag : mUIArrayGroups) { + ImGui::GetCurrentWindow()->DrawList->AddRect( + uag.Pos - ImVec2(1, 1), + uag.Pos + uag.Size + ImVec2(1, 1), + kArrayGroupOutline); + } + ImGui::EndChild(); - ImGui::PopStyleVar(); + ImGui::PopStyleVar(2); } template <class TFunction> @@ -314,6 +437,13 @@ private: mSelectionTL = { -1, -1 }; mSelectionBR = { -1, -1 }; + + ResetCS(); + } + + void ResetCS() + { + mCS = {}; } void SelectRange(Vec2i p1, Vec2i p2) @@ -334,6 +464,16 @@ private: auto& uiCell = mUICells[i]; uiCell.Selected = true; }); + + ResetAS(); + } + + void ResetSS(Vec2i pos) + { + mSS = {}; + mSS.ErrorDuplicateVarName = false; + mSS.HasLeftAG = pos.x >= 1; + mSS.HasRightAG = pos.x < mTable->GetTableWidth(); } void SelectCell(Vec2i cell) @@ -345,6 +485,14 @@ private: int i = cell.y * mTable->GetTableWidth() + cell.x; mUICells[i].Selected = true; + + ResetSS(cell); + } + + void ResetAS() + { + mAS = {}; + mAS.ErrorDuplicateVarName = false; } }; diff --git a/core/src/UI/UI_Utils.cpp b/core/src/UI/UI_Utils.cpp index e3ac097..0dcde8f 100644 --- a/core/src/UI/UI_Utils.cpp +++ b/core/src/UI/UI_Utils.cpp @@ -44,6 +44,15 @@ bool ImGui::Button(const char* label, const ImVec2& sizeArg, bool disabled) return res; } +bool ImGui::MenuItemConditional(const char* name, bool disabled) +{ + if (disabled) PushDisabled(); + bool res = MenuItem(name, nullptr, false, disabled); + if (disabled) PopDisabled(); + + return res; +} + void ImGui::ErrorIcon() { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{ 237 / 255.0f, 67 / 255.0f, 55 / 255.0f, 1.0f }); // #ED4337 |