diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..704f939 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libs/chess-move-interface"] + path = libs/chess-move-interface + url = git@gitlab.com:manzerbredes/chess-move-interface.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8576f39..5388b1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,10 @@ include_directories(src) file(GLOB_RECURSE SRC_CPP_FILES src/*.cpp) add_library(cgeditor SHARED ${SRC_CPP_FILES}) +# ChessMoveInterface +add_subdirectory(libs/chess-move-interface) +include_directories(${CMI_INCLUDE_DIR}) + # Examples set(COMPILE_EXAMPLES FALSE CACHE BOOL "Compiling included examples") if(COMPILE_EXAMPLES) diff --git a/examples/wxWidgets/CMakeLists.txt b/examples/wxWidgets/CMakeLists.txt index af76025..61c1396 100644 --- a/examples/wxWidgets/CMakeLists.txt +++ b/examples/wxWidgets/CMakeLists.txt @@ -4,3 +4,5 @@ find_package(wxWidgets COMPONENTS core base REQUIRED) include(${wxWidgets_USE_FILE}) add_executable(wxwidgets_example main.cpp MyHalfMove.cpp) target_link_libraries(wxwidgets_example cgeditor ${wxWidgets_LIBRARIES}) +target_link_libraries(wxwidgets_example ChessMoveInterface) + diff --git a/examples/wxWidgets/MyHalfMove.cpp b/examples/wxWidgets/MyHalfMove.cpp index 0e08113..510ff70 100644 --- a/examples/wxWidgets/MyHalfMove.cpp +++ b/examples/wxWidgets/MyHalfMove.cpp @@ -1,114 +1,6 @@ #include "MyHalfMove.hpp" -MyHalfMove::MyHalfMove(std::string move) { this->move = move; } -MyHalfMove::~MyHalfMove() {} - -void MyHalfMove::AddVariation(MyHalfMove *m) { - m->IsBlack = this->IsBlack; - m->Number = this->Number; - MyHalfMove::variations.push_back(m); - cgeditor::CGEHalfMove::variations.push_back(m); - m->SetParent(this); -} - -void MyHalfMove::SetMainline(MyHalfMove *m) { - if (!this->IsBlack) { - m->IsBlack = true; - m->Number = this->Number; - } else { - m->IsBlack = false; - m->Number = this->Number + 1; - } - MyHalfMove::mainline = m; - cgeditor::CGEHalfMove::MainLine = m; - if (m != NULL) { - m->SetParent(this); - } -} -void MyHalfMove::SetParent(MyHalfMove *m) { - MyHalfMove::parent = m; - CGEHalfMove::Parent = m; -} -void MyHalfMove::RemoveChild(MyHalfMove *m) { - std::uint32_t i = 0; - bool found = false; - for (i; i < MyHalfMove::variations.size(); i++) { - if (MyHalfMove::variations[i] == m) { - found = true; - break; - } - } - if (found) { - MyHalfMove::variations.erase(MyHalfMove::variations.begin() + i); - } - if (MyHalfMove::MainLine == m) { - MyHalfMove::MainLine = NULL; - } - cgeditor::CGEHalfMove::RemoveChild((CGEHalfMove *)m); -} - -MyHalfMove *MyHalfMove::GetParent() { return (parent); } - -MyHalfMove *MyHalfMove::GetRoot() { - MyHalfMove *m = this; - MyHalfMove *p = MyHalfMove::parent; - while (p != NULL) { - if (p->mainline != m) { - return (m); - } - m = p; - p = m->MyHalfMove::parent; - } - return (m); -} - -void MyHalfMove::SetAsMainline() { - MyHalfMove *root = GetRoot(); - MyHalfMove *lastRoot; - do { - lastRoot = root; - root->MyHalfMove::Promote(); - root = GetRoot(); - } while (root != lastRoot); - - // std::cout << IsVariation() << std::endl << std::flush; -} - -void MyHalfMove::Promote() { - if (MyHalfMove::parent != NULL) { - MyHalfMove *p = MyHalfMove::parent; - if (p->MyHalfMove::mainline != this) { - if (MyHalfMove::parent->MyHalfMove::parent != NULL) { - MyHalfMove *pp = MyHalfMove::parent->MyHalfMove::parent; - if (pp->MyHalfMove::mainline == p) { - pp->MyHalfMove::SetMainline(this); - } else { - pp->AddVariation(this); - pp->MyHalfMove::RemoveChild(p); - } - } - if (p->MyHalfMove::mainline == this) { - p->MyHalfMove::SetMainline(NULL); - } else { - p->MyHalfMove::RemoveChild(this); - } - this->AddVariation(p); - } - } -} - -bool MyHalfMove::IsVariation() { - MyHalfMove *m = this; - MyHalfMove *p = MyHalfMove::parent; - while (p != NULL) { - if (p->mainline != m) { - return (true); - } - m = p; - p = m->MyHalfMove::parent; - } - return (false); -} +MyHalfMove::MyHalfMove(std::string move){SetSAN(move); } MyHalfMove *BuildExampleGame() { MyHalfMove *m = new MyHalfMove("e4"); @@ -128,7 +20,7 @@ MyHalfMove *BuildExampleGame() { m2 = new MyHalfMove("Bc4"); m->SetMainline(m2); - m->comment="Italian Opening"; + m->SetComment("Italian Opening"); m = m2; m2 = new MyHalfMove("Bc5"); @@ -136,7 +28,7 @@ MyHalfMove *BuildExampleGame() { m = m2; m2 = new MyHalfMove("c3"); - m2->comment="Giuoco Pianissimo"; + m2->SetComment("Giuoco Pianissimo"); m->SetMainline(m2); m = m2; @@ -158,7 +50,7 @@ MyHalfMove *BuildExampleGame() { { MyHalfMove *var = new MyHalfMove("Re1"); - var->comment="Also possible"; + var->SetComment("Also possible"); m->AddVariation(var); MyHalfMove *var2 = new MyHalfMove("a6"); @@ -185,8 +77,8 @@ MyHalfMove *BuildExampleGame() { m2 = new MyHalfMove("a6"); m->SetMainline(m2); - m->comment="Test for a very long comment, to see how line breaks are handle by the framework."; - m->comment+="Test for a very long comment, to see how line breaks are handle by the framework."; + m->SetComment("Test for a very long comment, to see how line breaks are handle by the framework."); + m->SetComment(m->GetComment()+"Test for a very long comment, to see how line breaks are handle by the framework."); m = m2; m2 = new MyHalfMove("Bb3"); @@ -198,7 +90,7 @@ MyHalfMove *BuildExampleGame() { m = m2; m2 = new MyHalfMove("Re1"); - m2->nag="!!"; + m2->SetNAG(3); m->SetMainline(m2); m = m2; diff --git a/examples/wxWidgets/MyHalfMove.hpp b/examples/wxWidgets/MyHalfMove.hpp index dfb8860..84cc5e6 100644 --- a/examples/wxWidgets/MyHalfMove.hpp +++ b/examples/wxWidgets/MyHalfMove.hpp @@ -1,5 +1,6 @@ +#pragma once + #include "CGEditor.hpp" -#include /** * @brief Create your custom half move class @@ -8,32 +9,10 @@ * an overview of how to keep your move sync with the one of CGEditor * */ -class MyHalfMove : public cgeditor::CGEHalfMove { - MyHalfMove *parent = NULL; - MyHalfMove *mainline = NULL; - std::vector variations; +class MyHalfMove : public CMI::HalfMove { public: MyHalfMove(std::string move); - ~MyHalfMove(); - /// @brief Add variation to current move - void AddVariation(MyHalfMove *m); - /// @brief Remove the specified child from mainline and/or variations - void RemoveChild(MyHalfMove *m); - /// @brief Set value of the mailine - void SetMainline(MyHalfMove *m); - /// @brief Set this move as mainline - void SetAsMainline(); - /// @brief Promote the current move and submove - void Promote(); - /// @brief Check if current half move is within a variation - bool IsVariation(); - /// @brief Get the root of a variation - MyHalfMove* GetRoot(); - /// @brief Get parent of the current move - MyHalfMove* GetParent(); - /// @brief Set parent of the current move - void SetParent(MyHalfMove *m); }; /// @brief Build the example game to use in the editor diff --git a/examples/wxWidgets/main.cpp b/examples/wxWidgets/main.cpp index 7d2f59e..303d014 100644 --- a/examples/wxWidgets/main.cpp +++ b/examples/wxWidgets/main.cpp @@ -38,7 +38,7 @@ private: CGEditor::status.CanvasWidth = sz.GetWidth(); CGEditor::status.CanvasHeight = sz.GetHeight(); CGEditor::status.UseMoveIcons = - true; // Piece image should be drawn before the move ? + false; // Piece image should be drawn before the move ? const wxPoint pt = wxGetMousePosition(); CGEditor::status.MouseX = pt.x - this->GetScreenPosition().x; @@ -90,7 +90,7 @@ private: e.y + (e.height - sz.GetHeight()) / 2)); } - /** + /** * @brief CGEditor is going to call this method with the elements to draw on * the canvas * @@ -161,22 +161,27 @@ private: str = "Comment Selected"; else if (e.type == cgeditor::Event::Type::Promote) { str = "Promote"; - static_cast(e.move)->MyHalfMove::Promote(); + e.move->Promote(); + SyncCache(); } else if (e.type == cgeditor::Event::Type::Delete) { str = "Delete"; - if (e.move->Parent != NULL) { - static_cast(e.move)->GetParent()->MyHalfMove::RemoveChild( - (MyHalfMove *)e.move); + if (e.move->GetParent() != nullptr) { + static_cast((e.move)->GetParent())->RemoveChild(static_cast(e.move)); + delete static_cast(e.move); + SyncCache(); // Do not forget to sync the cache } else { - CGEditor::status.Moves = NULL; + CGEditor::status.Moves = nullptr; } } else if (e.type == cgeditor::Event::Type::SetAsMainline) { str = "Set as main line"; - static_cast(e.move)->MyHalfMove::SetAsMainline(); + e.move->SetAsMainline(); + SyncCache(); } else if (e.type == cgeditor::Event::Type::Goto) { str = "Goto move"; } - std::cout << "Event received: " << str << std::endl << std::flush; + wxLogDebug("Event received: %s", str); + if(CGEditor::status.Moves != nullptr && !CGEditor::status.Moves->IsConsistent()) + wxLogError("ERROR!! The tree of moves is not consistent anymore! Something wrong happends"); } // wxWidgets specific diff --git a/libs/chess-move-interface b/libs/chess-move-interface new file mode 160000 index 0000000..720c394 --- /dev/null +++ b/libs/chess-move-interface @@ -0,0 +1 @@ +Subproject commit 720c394c50e7cb79e6403c408b40dfe8d994230a diff --git a/src/CGEHalfMove.cpp b/src/CGEHalfMove.cpp deleted file mode 100644 index f26f19e..0000000 --- a/src/CGEHalfMove.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "CGEHalfMove.hpp" - -namespace cgeditor { - -CGEHalfMove::CGEHalfMove() - : MainLine(NULL), IsBlack(false), Number(1), Parent(NULL) {} - -CGEHalfMove::CGEHalfMove(CGEHalfMove *parent) { - CGEHalfMove(); - Parent = parent; - Parent->MainLine = this; - if (parent->IsBlack) { - Number = parent->Number + 1; - IsBlack = false; - } else { - Number = parent->Number; - IsBlack = true; - } -} - -CGEHalfMove::CGEHalfMove(const std::string &move) - : MainLine(NULL), IsBlack(false), Number(0), Parent(NULL) { - this->move = move; -} - -void CGEHalfMove::RemoveChild(CGEHalfMove *m) { - std::uint32_t i = 0; - bool found = false; - for (i; i < variations.size(); i++) { - if (variations[i] == m) { - found = true; - break; - } - } - if (found) { - variations.erase(variations.begin() + i); - } - if (MainLine == m) { - MainLine = NULL; - } -} - -} // namespace cgeditor \ No newline at end of file diff --git a/src/CGEHalfMove.hpp b/src/CGEHalfMove.hpp deleted file mode 100644 index 681aee1..0000000 --- a/src/CGEHalfMove.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include - -namespace cgeditor { - -/** - * @brief Move (mainlines and variations) displayed in the editor - * - */ -class CGEHalfMove { - -public: - CGEHalfMove(); - CGEHalfMove(CGEHalfMove *parent); - CGEHalfMove(const std::string &move); - - /// @brief CUrrent move number - std::uint16_t Number; - /// @brief Current move value - std::string move; - /// @brief Current NAG - std::string nag; - /// @brief Comment linked to the move - std::string comment; - - CGEHalfMove *MainLine; - CGEHalfMove *Parent; - bool IsBlack; - /// @brief Says if variations of that move must be drawn - bool Folded = false; - /// @brief Says if this move must be drawn - bool Hide = false; - /// @brief Variations of the move - std::vector variations; - - /// @brief Remove a move from the MainLine and/or variations - void RemoveChild(CGEHalfMove *m); -}; -} // namespace cgeditor \ No newline at end of file diff --git a/src/CGEditor.cpp b/src/CGEditor.cpp index e55a578..fadbdc8 100644 --- a/src/CGEditor.cpp +++ b/src/CGEditor.cpp @@ -8,6 +8,58 @@ CGEditor::CGEditor() { MT = new MoveTable(&status); MA = new Margin(&status); ME = new Menu(&status); + // Default Standard NagTable: https://en.wikipedia.org/wiki/Numeric_Annotation_Glyphs + status.NagTable[0]=""; + status.NagTable[1]="!"; + status.NagTable[2]="?"; + status.NagTable[3]="!!"; + status.NagTable[4]="??"; + status.NagTable[5]="!?"; + status.NagTable[6]="?!"; + status.NagTable[7]="□"; + status.NagTable[10]="="; + status.NagTable[13]="∞"; + status.NagTable[14]="⩲"; + status.NagTable[15]="⩱"; + status.NagTable[16]="±"; + status.NagTable[17]="∓"; + status.NagTable[18]="+-"; + status.NagTable[19]="-+"; + status.NagTable[22]="⨀"; + status.NagTable[23]="⨀"; + status.NagTable[26]="○"; + status.NagTable[27]="○"; + status.NagTable[32]="⟳"; + status.NagTable[33]="⟳"; + status.NagTable[36]="↑"; + status.NagTable[37]="↑"; + status.NagTable[40]="→"; + status.NagTable[41]="→"; + status.NagTable[44]="⯹"; + status.NagTable[45]="⯹"; + status.NagTable[132]="⇆"; + status.NagTable[133]="⇆"; + status.NagTable[138]="⨁"; + status.NagTable[139]="⨁"; + // Default Non-Standard NagTable: https://en.wikipedia.org/wiki/Numeric_Annotation_Glyphs + status.NagTable[140]="∆"; + status.NagTable[141]="∇"; + status.NagTable[142]="⌓"; + status.NagTable[143]="<="; + status.NagTable[144]="=="; + status.NagTable[145]="RR"; + status.NagTable[146]="N"; + status.NagTable[220]="⬒"; + status.NagTable[221]="⬓"; + status.NagTable[238]="○"; + status.NagTable[239]="⇔"; + status.NagTable[240]="⇗"; + status.NagTable[241]="⊞"; + status.NagTable[242]="⟫"; + status.NagTable[243]="⟪"; + status.NagTable[244]="✕"; + status.NagTable[245]="⊥"; + status.NagTable[254]="∟"; } CGEditor::~CGEditor() { @@ -86,6 +138,15 @@ void CGEditor::CallDrawElement(Element e) { DrawElement(e); } +void CGEditor::SyncCache(){ + MA->SyncCache(); + MT->SyncCache(); + MA->SyncCache(); + SBV->SyncCache(); + SBH->SyncCache(); + ME->SyncCache(); +} + void CGEditor::DrawComponent(Component *c) { for (Element &e : c->GetElements()) { CallDrawElement(e); @@ -103,4 +164,20 @@ bool CGEditor::ProcessEvents(){ return processed; } -} // namespace cgeditor \ No newline at end of file +std::string CGEditor::GetNAGSymbol(const std::uint8_t id) const{ + for(auto const& pair: status.NagTable){ + if(pair.first == id) + return pair.second; + } + return ""; +} + +std::uint8_t CGEditor::GetNAGId(const std::string& symbol) const{ + for(auto const& pair: status.NagTable){ + if(pair.second == symbol) + return pair.first; + } + return 0; +} + +} // namespace cgeditor diff --git a/src/CGEditor.hpp b/src/CGEditor.hpp index e113325..2ac81a3 100644 --- a/src/CGEditor.hpp +++ b/src/CGEditor.hpp @@ -27,11 +27,16 @@ protected: void Draw(); /// @brief Process the events generated during the drawing bool ProcessEvents(); + /// @brief Synchronize the editor cache (must be called when game was modified from outside the editor) + void SyncCache(); /// @brief Draw an element on the canvas virtual void DrawElement(const Element &) = 0; /// @brief Handle event that occured during editor drawing virtual void HandleEvent(const Event &) = 0; - + /// @brief Convert NAG id to symbol using the NagTable + std::string GetNAGSymbol(const std::uint8_t) const; + /// @brief Convert NAG symbol to id using the NagTable + std::uint8_t GetNAGId(const std::string&) const; public: CGEditor(); ~CGEditor(); diff --git a/src/Types.hpp b/src/Types.hpp index 402bf12..83c79f0 100644 --- a/src/Types.hpp +++ b/src/Types.hpp @@ -1,7 +1,8 @@ #pragma once -#include "CGEHalfMove.hpp" +#include "CMI.hpp" #include +#include namespace cgeditor { @@ -54,7 +55,7 @@ typedef struct Event { enum Type { CommentSelected, Promote, Delete, SetAsMainline, Goto, None }; Type type = None; /// @brief Move related to the event - CGEHalfMove *move = NULL; + CMI::HalfMove *move = nullptr; } Event; /** @@ -92,10 +93,11 @@ typedef struct Status { double MoveTableMaxX = 0, MoveTableMaxY = 0; /// @brief User should set it to true when mouse is dragging bool IsDrag = false; - CGEHalfMove *Moves = NULL; - CGEHalfMove *CurrentMove = NULL; - CGEHalfMove *SelectedMove = NULL; + CMI::HalfMove *Moves = nullptr; + CMI::HalfMove *CurrentMove = nullptr; + CMI::HalfMove *SelectedMove = nullptr; std::vector Events; + std::unordered_map NagTable; } Status; } // namespace cgeditor \ No newline at end of file diff --git a/src/components/Component.hpp b/src/components/Component.hpp index 204ee47..564660a 100644 --- a/src/components/Component.hpp +++ b/src/components/Component.hpp @@ -14,6 +14,7 @@ public: Component(Status *s) : status(s){}; std::vector GetElements() { return (this->elements); } virtual void Refresh() = 0; + virtual void SyncCache() {}; }; } // namespace cgeditor \ No newline at end of file diff --git a/src/components/MoveTable.cpp b/src/components/MoveTable.cpp index b08bf9e..653356a 100644 --- a/src/components/MoveTable.cpp +++ b/src/components/MoveTable.cpp @@ -1,5 +1,4 @@ #include "MoveTable.hpp" -#include namespace cgeditor { @@ -11,8 +10,8 @@ void MoveTable::Refresh() { elements.clear(); VariationMargins.clear(); CurrentMove = -1; // No current move by default - if (status->Moves != NULL) { - UpdateMoves(status->Moves, 0, 0, status->Moves->IsBlack); + if (status->Moves != nullptr) { + UpdateMoves(status->Moves, 0, 0, status->Moves->IsBlack()); // We only set the type after the call to UpdateMoves() // This way only a single move will be the current move if (CurrentMove >= 0) { @@ -46,19 +45,19 @@ bool MoveTable::IsMouseOver(const Element &e) const { status->MouseY - status->ScrollY)); } -std::uint32_t MoveTable::UpdateMoves(CGEHalfMove *m, std::uint32_t line, +std::uint32_t MoveTable::UpdateMoves(CMI::HalfMove *m, std::uint32_t line, std::uint32_t indent, bool only_black) { //---------- Check black or white ---------- char indent_black = 0; - if (m->IsBlack) { + if (m->IsBlack()) { indent_black++; } //---------- Create temporary move surrounding area ---------- Element move_bound; move_bound.prop = Property::Move; - if (m->IsBlack) { + if (m->IsBlack()) { move_bound.prop |= Property::Black; } move_bound.x = status->MarginBarWidth + @@ -67,7 +66,7 @@ std::uint32_t MoveTable::UpdateMoves(CGEHalfMove *m, std::uint32_t line, move_bound.y = status->MoveHeight * line; move_bound.width = status->MoveWidth; move_bound.height = status->MoveHeight; - move_bound.text = m->move; + move_bound.text = m->GetSAN(); move_bound.ShouldApplyScroll = true; bool isMouseOver = IsMouseOver(move_bound); @@ -102,12 +101,12 @@ std::uint32_t MoveTable::UpdateMoves(CGEHalfMove *m, std::uint32_t line, // Move Element e; e.prop = move_bound.prop | Property::Text; - e.text = m->move; - if (m->move.size() > 0) { - char c = m->move[0]; + e.text = m->GetSAN(); + if (m->GetSAN().size() > 0) { + char c = m->GetSAN()[0]; if (!(c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f' || c == 'g' || c == 'h' || c == 'O' || c == '0')) { - e.text = m->move.substr(1, m->move.size()); + e.text = m->GetSAN().substr(1, m->GetSAN().size()); if (c == 'N') { img.prop |= Property::Knight; } else if (c == 'B') { @@ -137,9 +136,9 @@ std::uint32_t MoveTable::UpdateMoves(CGEHalfMove *m, std::uint32_t line, } //---------- NAG ---------- - if(m->nag.size()>0){ + if(m->GetNAG()>0){ Element nag; - nag.text = m->nag; + nag.text = status->NagTable[m->GetNAG()]; nag.x = move_bound.x + status->MoveWidth - status->NagWidth - status->NagRightMargin; nag.y = status->MoveHeight * line; nag.width = status->NagWidth; @@ -150,15 +149,15 @@ std::uint32_t MoveTable::UpdateMoves(CGEHalfMove *m, std::uint32_t line, } //---------- Move number in marge or for variation ---------- - if (indent == 0 && (!m->IsBlack || only_black)) { - DRAW_NB(0, status->MoveHeight * line, m->Number); - } else if (indent > 0 && (!m->IsBlack || only_black)) { + if (indent == 0 && (!m->IsBlack() || only_black)) { + DRAW_NB(0, status->MoveHeight * line, m->GetNumber()); + } else if (indent > 0 && (!m->IsBlack() || only_black)) { if (only_black) { DRAW_NB_VAR((move_bound.x - status->MoveWidth) - status->MarginBarWidth, - status->MoveHeight * line, m->Number); + status->MoveHeight * line, m->GetNumber()); } else { DRAW_NB_VAR(move_bound.x - ((indent + 1) / 2 * status->MarginBarWidth), - status->MoveHeight * line, m->Number); + status->MoveHeight * line, m->GetNumber()); } } @@ -176,35 +175,50 @@ std::uint32_t MoveTable::UpdateMoves(CGEHalfMove *m, std::uint32_t line, } //---------- Comments ---------- - if (m->comment.size() > 0) { + if (m->GetComment().size() > 0) { line = DrawComment(m, line, indent, move_bound, indent_black); } //---------- Variations ---------- - if (m->variations.size() > 0) { + if (m->GetVariations().size() > 0) { line = DrawVariations(m, line, indent, move_bound, indent_black); } //---------- Mainline ---------- - if (m->MainLine != NULL) { - only_black = (m->MainLine->IsBlack && - (m->comment.size() > 0 || m->variations.size())); - if (m->IsBlack) { - line = UpdateMoves(m->MainLine, line + 1, indent, only_black); + if (m->GetMainline() != nullptr) { + only_black = (m->GetMainline()->IsBlack() && + (m->GetComment().size() > 0 || m->GetVariations().size())); + if (m->IsBlack()) { + line = UpdateMoves(m->GetMainline(), line + 1, indent, only_black); } else { - line = UpdateMoves(m->MainLine, line, indent, only_black); + line = UpdateMoves(m->GetMainline(), line, indent, only_black); } } return (line); } -std::uint32_t MoveTable::DrawComment(CGEHalfMove *m, std::uint32_t line, +void MoveTable::SyncCache(){ + if(status->Moves != nullptr){ + std::vector toDelete; + for (auto const& entry : MovesStates){ + if(!status->Moves->Contains(entry.first)) + toDelete.push_back(entry.first); + } + for(auto key: toDelete){ + MovesStates.erase(key); + } + } + else + MovesStates.clear(); +} + +std::uint32_t MoveTable::DrawComment(CMI::HalfMove *m, std::uint32_t line, std::uint32_t indent, const Element &move_bound, const char &indent_black) { // Show three dots - if (!m->IsBlack) { + if (!m->IsBlack()) { DRAW_DOTS(status->MarginBarWidth + status->MoveWidth * (indent + 1) + ((indent + 1) / 2 * status->MarginBarWidth), status->MoveHeight * line); @@ -212,7 +226,7 @@ std::uint32_t MoveTable::DrawComment(CGEHalfMove *m, std::uint32_t line, line++; // Goto the right line /// ----- Compute comment bounding box values: - int nchar=m->comment.size(); + int nchar=m->GetComment().size(); int nline=ceil((double)nchar/(double)status->CommentCharPerLine); std::uint16_t nrow=ceil(((nline*status->CommentCharHeight)+2*status->CommentPadding)/status->MoveHeight); int width=status->CommentCharPerLine*status->CommentCharWidth+2*status->CommentPadding; @@ -249,7 +263,7 @@ std::uint32_t MoveTable::DrawComment(CGEHalfMove *m, std::uint32_t line, l.height=status->CommentCharHeight; l.ShouldApplyScroll = true; for(int i=0;icomment.substr(i*status->CommentCharPerLine,status->CommentCharPerLine); + l.text=m->GetComment().substr(i*status->CommentCharPerLine,status->CommentCharPerLine); // Remove leading space: if(l.text.size()>2 && l.text[0]==' '){ l.text=l.text.substr(1,l.text.size()); @@ -264,18 +278,18 @@ std::uint32_t MoveTable::DrawComment(CGEHalfMove *m, std::uint32_t line, } line += nrow; // Skip right amount of lines // ----- Since we already increment line for black later on: - if (m->IsBlack || m->variations.size() > 0) { + if (m->IsBlack() || m->GetVariations().size() > 0) { line--; } return (line); } -std::uint32_t MoveTable::DrawVariations(CGEHalfMove *m, std::uint32_t line, +std::uint32_t MoveTable::DrawVariations(CMI::HalfMove *m, std::uint32_t line, std::uint32_t indent, const Element &move_bound, const char &indent_black) { // Show three dots next to move if white turn - if ((m->variations.size() == 0) && !m->IsBlack) { + if ((m->GetVariations().size() == 0) && !m->IsBlack()) { DRAW_DOTS(status->MarginBarWidth + status->MoveWidth * (indent + 1), status->MoveHeight * line); } @@ -284,22 +298,22 @@ std::uint32_t MoveTable::DrawVariations(CGEHalfMove *m, std::uint32_t line, Element e; e.prop = Property::Rectangle | Property::Button; e.x = move_bound.x + status->MoveWidth; - if (!m->IsBlack) + if (!m->IsBlack()) e.x += status->MoveWidth; e.y = move_bound.y + std::ceil(status->MoveHeight / 4); e.width = std::ceil(status->MoveHeight / 2); e.height = e.width; e.ShouldApplyScroll = true; if (status->LeftClick && IsMouseOver(e)) { - m->Folded = !m->Folded; + MovesStates[m].IsFolded=!MovesStates[m].IsFolded; } - if (!m->Folded) { + if (!MovesStates[m].IsFolded) { e.prop |= Property::On; } elements.push_back(e); } - if (!m->Folded) { - for (CGEHalfMove *v : m->variations) { + if (!MovesStates[m].IsFolded) { + for (CMI::HalfMove *v : m->GetVariations()) { // For each variation show show/hide button { Element e; @@ -313,22 +327,22 @@ std::uint32_t MoveTable::DrawVariations(CGEHalfMove *m, std::uint32_t line, e.height = e.width; e.ShouldApplyScroll = true; if (status->LeftClick && IsMouseOver(e)) { - v->Hide = !v->Hide; + MovesStates[v].IsHidden = !MovesStates[v].IsHidden; } - if (!v->Hide) { + if (!MovesStates[v].IsHidden) { e.prop |= Property::On; } elements.push_back(e); } - if (!v->Hide) { - line = UpdateMoves(v, line + 1, indent + 1, v->IsBlack); + if (!MovesStates[v].IsHidden) { + line = UpdateMoves(v, line + 1, indent + 1, v->IsBlack()); } else { line++; } } } // New line after variation - if (m->MainLine != NULL && m->MainLine->IsBlack) { + if (m->GetMainline() != nullptr && m->GetMainline()->IsBlack()) { line++; } return (line); diff --git a/src/components/MoveTable.hpp b/src/components/MoveTable.hpp index 28ebd12..e357287 100644 --- a/src/components/MoveTable.hpp +++ b/src/components/MoveTable.hpp @@ -1,5 +1,6 @@ #include "Component.hpp" #include +#include #define IS_VISIBLE(e) \ (((e.x + status->ScrollX) >= 0 && \ @@ -46,19 +47,25 @@ namespace cgeditor { class MoveTable : public Component { - std::uint32_t UpdateMoves(CGEHalfMove *, std::uint32_t, std::uint32_t,bool only_black); + typedef struct MoveState { + bool IsFolded=false; + bool IsHidden=false; + } MoveState; + std::uint32_t UpdateMoves(CMI::HalfMove *, std::uint32_t, std::uint32_t,bool only_black); std::int32_t CurrentMove; std::vector VariationMargins; + /// @brief Must be kept consistent: + std::unordered_map MovesStates; bool IsMouseOver(const Element &e) const; - std::uint32_t DrawComment(CGEHalfMove *m, std::uint32_t line, std::uint32_t indent, + std::uint32_t DrawComment(CMI::HalfMove *m, std::uint32_t line, std::uint32_t indent, const Element &move_bound, const char &indent_black); - std::uint32_t DrawVariations(CGEHalfMove *m, std::uint32_t line, std::uint32_t indent, + std::uint32_t DrawVariations(CMI::HalfMove *m, std::uint32_t line, std::uint32_t indent, const Element &move_bound, const char &indent_black); - public: MoveTable(Status *s); void Refresh(); std::vector GetVariationsMarging() { return (VariationMargins); } + void SyncCache(); }; } // namespace cgeditor \ No newline at end of file