diff options
| author | Loic Guegan <manzerbredes@mailbox.org> | 2022-02-12 19:13:34 +0100 |
|---|---|---|
| committer | Loic Guegan <manzerbredes@mailbox.org> | 2022-02-12 19:13:34 +0100 |
| commit | a359219e33fdf3afb5ddfbb084563054a947b106 (patch) | |
| tree | 91dab9c21321f73152993183cd6e8cf4a04017f8 /src | |
Create project
Diffstat (limited to 'src')
| -rw-r--r-- | src/CGEHalfMove.cpp | 59 | ||||
| -rw-r--r-- | src/CGEHalfMove.hpp | 47 | ||||
| -rw-r--r-- | src/CGEditor.cpp | 99 | ||||
| -rw-r--r-- | src/CGEditor.hpp | 37 | ||||
| -rw-r--r-- | src/Types.cpp | 29 | ||||
| -rw-r--r-- | src/Types.hpp | 88 | ||||
| -rw-r--r-- | src/components/Component.hpp | 19 | ||||
| -rw-r--r-- | src/components/Margin.cpp | 30 | ||||
| -rw-r--r-- | src/components/Margin.hpp | 12 | ||||
| -rw-r--r-- | src/components/Menu.cpp | 64 | ||||
| -rw-r--r-- | src/components/Menu.hpp | 12 | ||||
| -rw-r--r-- | src/components/MoveTable.cpp | 280 | ||||
| -rw-r--r-- | src/components/MoveTable.hpp | 65 | ||||
| -rw-r--r-- | src/components/Scrollbar.cpp | 112 | ||||
| -rw-r--r-- | src/components/Scrollbar.hpp | 16 |
15 files changed, 969 insertions, 0 deletions
diff --git a/src/CGEHalfMove.cpp b/src/CGEHalfMove.cpp new file mode 100644 index 0000000..7a5d095 --- /dev/null +++ b/src/CGEHalfMove.cpp @@ -0,0 +1,59 @@ +#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(std::string move) + : MainLine(NULL), IsBlack(false), Number(0), Parent(NULL) { + this->move = move; +} + +void CGEHalfMove::SetComment(const std::string &c) { + if (c.size() > 0) { + NbLineComment = 1; + for (const char &c : c) { + if (c == '\n') { + NbLineComment++; + } + } + this->comment = c; + } +} + +std::string CGEHalfMove::GetComment() { return (comment); } + +std::uint16_t CGEHalfMove::GetNbLineComment() { return (this->NbLineComment); } + +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 new file mode 100644 index 0000000..2ed7516 --- /dev/null +++ b/src/CGEHalfMove.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include <string> +#include <vector> + +namespace cgeditor { + +/** + * @brief Move (mainlines and variations) displayed in the editor + * + */ +class CGEHalfMove { + /// @brief Comment linked to the move + std::string comment; + /// @brief Number of line in @a comment + std::uint16_t NbLineComment = 0; + +public: + CGEHalfMove(); + CGEHalfMove(CGEHalfMove *parent); + CGEHalfMove(std::string move); + + /// @brief CUrrent move number + std::uint16_t Number; + /// @brief Current move value + std::string move; + + 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<CGEHalfMove *> variations; + + /// @brief Set comment and update number of lines + void SetComment(const std::string &c); + /// @brief Get current value of comment + std::string GetComment(); + /// @brief Get number of lines in comment + std::uint16_t GetNbLineComment(); + /// @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 new file mode 100644 index 0000000..fc7c567 --- /dev/null +++ b/src/CGEditor.cpp @@ -0,0 +1,99 @@ +#include "CGEditor.hpp" + +namespace cgeditor { + +CGEditor::CGEditor() { + SBV = new Scrollbar(&status, false); + SBH = new Scrollbar(&status, true); + MT = new MoveTable(&status); + MA = new Margin(&status); + ME = new Menu(&status); +} + +CGEditor::~CGEditor() { + delete SBV; + delete SBH; + delete MT; + delete MA; + delete ME; +} + +void CGEditor::Draw() { + bool ShoudUpdateMouse = false; + if (status.LeftClick || status.RightClick) { + ShoudUpdateMouse = true; + } + + // Should be refreshed before Scrollbar! + // To update status.MoveTableMaxX and status.MoveTableMaxY + MA->Refresh(); + MT->Refresh(); + MA->DrawMargin(MT->GetVariationsMarging()); + SBV->Refresh(); + SBH->Refresh(); + ME->Refresh(); + + // Order matter + DrawComponent(MA); + DrawComponent(MT); + DrawComponent(SBV); + DrawComponent(SBH); + DrawComponent(ME); + + // Handle events + for (Event &e : status.Events) { + HandleEvent(e); + } + status.Events.clear(); + + // Update mouse events + status.LeftClick = false; + status.RightClick = false; + status.IsDrag = false; + if (ShoudUpdateMouse) { + status.LastMouseClicX = status.MouseX; + status.LastMouseClicY = status.MouseY; + } +} + +void CGEditor::CallDrawElement(Element e) { + // For element that want to expands up to the edge + if (e.width < 0) { + if (e.ShouldApplyScroll) { + e.width = + status.CanvasWidth - status.ScrollbarWidth - (e.x + status.ScrollX); + } else { + e.width = status.CanvasWidth - status.ScrollbarWidth - e.x; + } + if (e.width < 0) { + e.width *= -1; + } + } + + // Apply scroll + if (e.ShouldApplyScroll) { + e.x += status.ScrollX; + if (!e.IgnoreScrollY) { + e.y += status.ScrollY; + } + } + + // Check if element is visible + if (((e.x) >= 0 && ((e.x) <= status.CanvasWidth) && (e.y) >= 0 && + ((e.y) <= status.CanvasHeight)) || + ((e.x + e.width) >= 0 && ((e.x + e.width) <= status.CanvasWidth) && + (e.y + e.height) >= 0 && ((e.y + e.height) <= status.CanvasHeight))) { + if (e.IsOver(status.MouseX, status.MouseY)) { + e.prop |= Property::Mouseover; + } + DrawElement(e); + } +} + +void CGEditor::DrawComponent(Component *c) { + for (Element &e : c->GetElements()) { + CallDrawElement(e); + } +} + +} // namespace cgeditor
\ No newline at end of file diff --git a/src/CGEditor.hpp b/src/CGEditor.hpp new file mode 100644 index 0000000..d30d05e --- /dev/null +++ b/src/CGEditor.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "Types.hpp" +#include "components/Margin.hpp" +#include "components/Menu.hpp" +#include "components/MoveTable.hpp" +#include "components/Scrollbar.hpp" + +#include <string> + +namespace cgeditor { + +class CGEditor { + /// @brief Prepare element for drawing and draw + void CallDrawElement(Element); + /// @brief Draw all elements of a component + void DrawComponent(Component *); + + Scrollbar *SBV, *SBH; + MoveTable *MT; + Margin *MA; + Menu *ME; + +protected: + Status status; + ///@brief Draw the Chess Game Editor on the canvas using current status + void Draw(); + /// @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; + +public: + CGEditor(); + ~CGEditor(); +}; +} // namespace cgeditor
\ No newline at end of file diff --git a/src/Types.cpp b/src/Types.cpp new file mode 100644 index 0000000..76bf398 --- /dev/null +++ b/src/Types.cpp @@ -0,0 +1,29 @@ +#include "Types.hpp" + +namespace cgeditor { + +bool Element::IsOver(const double &X, const double &Y) const { + if (width < 0) { + return (x >= x && Y >= y && Y <= (y + height)); + } + return ((X >= x && X <= (x + width) && Y >= y && Y <= (y + height))); +} + +Property operator|(Property lhs, Property rhs) { + return static_cast<Property>( + static_cast<std::underlying_type_t<Property>>(lhs) | + static_cast<std::underlying_type_t<Property>>(rhs)); +} + +bool operator&(Property lhs, Property rhs) { + return (static_cast<std::underlying_type_t<Property>>(lhs) & + static_cast<std::underlying_type_t<Property>>(rhs)); +} + +Property &operator|=(Property &lhs, Property rhs) { + return lhs = static_cast<Property>( + static_cast<std::underlying_type_t<Property>>(lhs) | + static_cast<std::underlying_type_t<Property>>(rhs)); +} + +} // namespace cgeditor
\ No newline at end of file diff --git a/src/Types.hpp b/src/Types.hpp new file mode 100644 index 0000000..db8b6f3 --- /dev/null +++ b/src/Types.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include "CGEHalfMove.hpp" +#include <string> + +namespace cgeditor { + +enum class Property : std::uint32_t { + None = 0, + Image = 1, + Rectangle = 1 << 1, + Text = 1 << 2, + On = 1 << 3, + Move = 1 << 4, + Margin = 1 << 5, + Menuitem = 1 << 6, // Is it a menu item + Comment = 1 << 7, + Black = 1 << 8, // Is it a move for black + Scrollbar = 1 << 9, + Horizontal = 1 << 10, // Is it an horizontal scrollbar + Scrollbarbg = 1 << 11, // Is it the background of the scrollbar + Button = 1 << 12, // Is it a button + Dots = 1 << 13, // Move dots + Movenumber = 1 << 14, + Current = 1 << 15, + Mouseover = 1 << 16 // Set on every element where mouse is over +}; +Property operator|(Property lhs, Property rhs); +Property &operator|=(Property &lhs, Property rhs); +bool operator&(Property lhs, Property rhs); + +class Element { +public: + Property prop = Property::None; + std::string text; + double x, y; + double width, height; + /// @brief Should element be scrolled + bool ShouldApplyScroll = false; + /// @brief For margin bar to avoid scrolling it vertically + bool IgnoreScrollY = false; + /// @brief Check if a given point is over the element + bool IsOver(const double &X, const double &Y) const; +}; + +typedef struct Event { + enum Type { CommentSelected, Promote, Delete, SetAsMainline, Goto, None }; + Type type = None; + /// @brief Move related to the event + CGEHalfMove *move = NULL; +} Event; + +/** + * @brief Chess Game Editor status + * Various parameters that can be tuned by the user. + * The user should manually set mouse event boolean + * for the editor to work properly + */ +typedef struct Status { + double MouseX = 0, MouseY = 0; + double LastMouseClicX = 0, LastMouseClicY = 0; + double CanvasWidth, CanvasHeight; + double MenuItemWidth = 150, MenuItemHeight = 50; + double MoveWidth = 100, MoveHeight = 50; + double MarginBarWidth = 50; + double ScrollbarWidth = 30; + double MenuX, MenuY; + double MoveX, MoveY; + std::uint16_t CommentLinePerRow = 2; + /// @brief Ask the editor to scroll for a specific amout of pixels + double EventVScroll = 0, EventHScroll = 0; + /// @brief Amount of pixel to scroll elements + double ScrollX = 0, ScrollY = 0; + /// @brief Set according to mouse events + bool LeftClick, RightClick; + /// @brief Can be use to close the menu + bool IsMenuOpen = false; + bool UseMoveImages = false; + 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; + std::vector<Event> Events; +} Status; + +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/Component.hpp b/src/components/Component.hpp new file mode 100644 index 0000000..204ee47 --- /dev/null +++ b/src/components/Component.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "Types.hpp" +#include <vector> + +namespace cgeditor { + +class Component { +protected: + Status *status; + std::vector<Element> elements; + +public: + Component(Status *s) : status(s){}; + std::vector<Element> GetElements() { return (this->elements); } + virtual void Refresh() = 0; +}; + +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/Margin.cpp b/src/components/Margin.cpp new file mode 100644 index 0000000..077cce5 --- /dev/null +++ b/src/components/Margin.cpp @@ -0,0 +1,30 @@ +#include "Margin.hpp" + +namespace cgeditor { + +Margin::Margin(Status *s) : Component(s) {} + +void Margin::Refresh() { + elements.clear(); + Element e; + e.x = 0; + e.y = 0; + e.height = status->CanvasHeight - status->ScrollbarWidth; + e.ShouldApplyScroll = true; + e.IgnoreScrollY = true; + DrawMargin(e); +} + +void Margin::DrawMargin(Element e) { + e.prop=Property::Margin | Property::Rectangle; + e.width = status->MarginBarWidth; + elements.push_back(e); +} + +void Margin::DrawMargin(std::vector<Element> elts) { + for(Element &e:elts){ + DrawMargin(e); + } +} + +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/Margin.hpp b/src/components/Margin.hpp new file mode 100644 index 0000000..8c2f243 --- /dev/null +++ b/src/components/Margin.hpp @@ -0,0 +1,12 @@ +#include "Component.hpp" + +namespace cgeditor { +class Margin : public Component { + +public: + Margin(Status *s); + void Refresh(); + void DrawMargin(Element e); + void DrawMargin(std::vector<Element> elts); +}; +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/Menu.cpp b/src/components/Menu.cpp new file mode 100644 index 0000000..36d1d1a --- /dev/null +++ b/src/components/Menu.cpp @@ -0,0 +1,64 @@ +#include "Menu.hpp" + +namespace cgeditor { + +Menu::Menu(Status *s) : Component(s), WasOpen(false) { + entries.push_back("Delete from here"); + entries.push_back("Promote"); + entries.push_back("Set as main line"); +} +void Menu::Refresh() { + if (WasOpen && (status->LeftClick || status->RightClick)) { + char i = 0; + for (Element &e : elements) { + if (e.IsOver(status->MouseX, status->MouseY)) { + if (i == 0) { + status->Events.push_back({Event::Type::Delete,status->SelectedMove}); + } else if (i == 1) { + status->Events.push_back({Event::Type::Promote,status->SelectedMove}); + } else if (i == 2) { + status->Events.push_back({Event::Type::SetAsMainline,status->SelectedMove}); + } + } + i++; + } + status->IsMenuOpen = false; + WasOpen = false; + elements.clear(); + return; + } + + elements.clear(); + // Draw menu backward to avoid getting out of the canvas + bool backwardY = + (status->MouseY + status->MenuItemHeight * entries.size()) >= + status->CanvasHeight; + bool backwardX = + (status->MouseX + status->MenuItemWidth) >= status->CanvasWidth; + + if (status->IsMenuOpen) { + char i = 0; + for (std::string &en : entries) { + Element e; + e.prop=Property::Text|Property::Menuitem; + e.text = en; + if (backwardX) { + e.x = (status->MouseX - status->MenuItemWidth); + } else { + e.x = status->MouseX; + } + if (backwardY) { + e.y = (status->MouseY - status->MenuItemHeight) - + i * status->MenuItemHeight; + } else { + e.y = status->MouseY + i * status->MenuItemHeight; + } + e.width = status->MenuItemWidth; + e.height = status->MenuItemHeight; + elements.push_back(e); + i++; + } + WasOpen = true; + } +} +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/Menu.hpp b/src/components/Menu.hpp new file mode 100644 index 0000000..ca33ac3 --- /dev/null +++ b/src/components/Menu.hpp @@ -0,0 +1,12 @@ +#include "Component.hpp" + +namespace cgeditor { +class Menu : public Component { + std::vector<std::string> entries; + /// @brief Set to true if the menu was open during the last editor draw + bool WasOpen; +public: + Menu(Status *s); + void Refresh(); +}; +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/MoveTable.cpp b/src/components/MoveTable.cpp new file mode 100644 index 0000000..e7f7af3 --- /dev/null +++ b/src/components/MoveTable.cpp @@ -0,0 +1,280 @@ +#include "MoveTable.hpp" + +namespace cgeditor { + +MoveTable::MoveTable(Status *s) : Component(s) { + ImageWidth = status->MoveWidth * 0.25; // Image is 25% of the cell +} +void MoveTable::Refresh() { + status->MoveTableMaxX = 0; + status->MoveTableMaxY = 0; + elements.clear(); + VariationMargins.clear(); + CurrentMove = -1; // No current move by default + if (status->Moves != NULL) { + 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) { + if (status->UseMoveImages) { + elements[CurrentMove].prop |= Property::Current; + elements[CurrentMove + 1].prop |= Property::Current; + } else { + elements[CurrentMove].prop |= Property::Current; + } + } + } else { + Element e; + e.prop = Property::Text; + e.text = "No move to show"; + e.x = status->MarginBarWidth; + e.y = 0; + e.height = status->MoveHeight; + e.width = -1; + elements.push_back(e); + } +} + +bool MoveTable::IsMouseOver(const Element &e) const { + // Check if we clicked on scroll bars + if (status->IsMenuOpen || + status->MouseX >= (status->CanvasWidth - status->ScrollbarWidth) || + status->MouseY >= (status->CanvasHeight - status->ScrollbarWidth)) { + return (false); + } + return (e.IsOver(status->MouseX - status->ScrollX, + status->MouseY - status->ScrollY)); +} + +std::uint32_t MoveTable::UpdateMoves(CGEHalfMove *m, std::uint32_t line, + std::uint32_t indent, bool only_black) { + + //---------- Check black or white ---------- + char indent_black = 0; + if (m->IsBlack) { + indent_black++; + } + + //---------- Create temporary move surrounding area ---------- + Element move_bound; + move_bound.prop = Property::Move; + if (m->IsBlack) { + move_bound.prop |= Property::Black; + } + move_bound.x = status->MarginBarWidth + status->MoveX + + status->MoveWidth * (indent + indent_black) + + ((indent + 1) / 2 * status->MarginBarWidth); + move_bound.y = status->MoveHeight * line; + move_bound.width = status->MoveWidth; + move_bound.height = status->MoveHeight; + move_bound.text = m->move; + move_bound.ShouldApplyScroll = true; + bool isMouseOver = IsMouseOver(move_bound); + + //---------- Update current focus move ---------- + if (isMouseOver) { + if (status->LeftClick) { + if (!status->IsMenuOpen) { + status->Events.push_back({Event::Type::Goto, m}); + status->CurrentMove = m; + } + } else if (status->RightClick) { + status->IsMenuOpen = true; + status->SelectedMove = m; + } + } + + //---------- Check if current move is focused ---------- + if (status->CurrentMove == m) { + CurrentMove = elements.size(); + } + + //---------- Draw move ---------- + if (status->UseMoveImages) { + // Image + Element img; + img.prop = Property::Image | Property::Move; + img.x = move_bound.x; + img.y = status->MoveHeight * line; + img.width = ImageWidth; + img.height = status->MoveHeight; + img.ShouldApplyScroll = true; + elements.push_back(img); + // Move + Element e; + e.prop = move_bound.prop | Property::Text; + e.text = m->move; + e.x = ImageWidth + move_bound.x; + e.y = status->MoveHeight * line; + e.width = status->MoveWidth - ImageWidth; + e.height = status->MoveHeight; + e.ShouldApplyScroll = true; + elements.push_back(e); + } else { + move_bound.prop |= Property::Text; + elements.push_back(move_bound); + } + + //---------- 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 (only_black) { + DRAW_NB_VAR((move_bound.x - status->MoveWidth) - status->MarginBarWidth, + status->MoveHeight * line, m->Number); + } else { + DRAW_NB_VAR(move_bound.x - ((indent + 1) / 2 * status->MarginBarWidth), + status->MoveHeight * line, m->Number); + } + } + + //---------- Draw dots ---------- + if (only_black) { + DRAW_DOTS(move_bound.x - status->MoveWidth, move_bound.y); + } + + //---------- Scrolling infos ---------- + if ((move_bound.x + move_bound.width) > status->MoveTableMaxX) { + status->MoveTableMaxX = move_bound.x + move_bound.width; + } + if ((move_bound.y + move_bound.height) > status->MoveTableMaxY) { + status->MoveTableMaxY = move_bound.y + move_bound.height; + } + + //---------- Comments ---------- + if (m->GetNbLineComment() > 0) { + line = DrawComment(m, line, indent, move_bound, indent_black); + } + + //---------- Variations ---------- + if (m->variations.size() > 0) { + line = DrawVariations(m, line, indent, move_bound, indent_black); + } + + //---------- Mainline ---------- + if (m->MainLine != NULL) { + only_black = (m->MainLine->IsBlack && + (m->GetNbLineComment() > 0 || m->variations.size())); + if (m->IsBlack) { + line = UpdateMoves(m->MainLine, line + 1, indent, only_black); + } else { + line = UpdateMoves(m->MainLine, line, indent, only_black); + } + } + + return (line); +} + +std::uint32_t MoveTable::DrawComment(CGEHalfMove *m, std::uint32_t line, + std::uint32_t indent, + const Element &move_bound, + const char &indent_black) { + // Show three dots + if (!m->IsBlack) { + DRAW_DOTS(status->MarginBarWidth + status->MoveX + + status->MoveWidth * (indent + 1) + + ((indent + 1) / 2 * status->MarginBarWidth), + status->MoveHeight * line); + } + // Print comment + line++; + // Computer number of rows for the comment + std::uint16_t CommentRows = m->GetNbLineComment() / status->CommentLinePerRow; + if (m->GetNbLineComment() % status->CommentLinePerRow > 0) { + CommentRows++; + } + // Draw comment + Element e; + e.prop = Property::Text | Property::Comment; + e.x = move_bound.x - + (indent_black * + status->MoveWidth); // status->MarginBarWidth + status->MoveX; + e.y = status->MoveHeight * line; + e.width = -1; // Negative width means expands to the edge of the canvas + e.height = CommentRows * status->MoveHeight; + e.text = m->GetComment(); + e.ShouldApplyScroll = true; + elements.push_back(e); + // Do not forget to add marging before comment + if (indent > 0) { + e.x -= status->MarginBarWidth; + VariationMargins.push_back(e); + } + if (status->LeftClick && IsMouseOver(e)) { + status->Events.push_back({Event::Type::CommentSelected, m}); + } + line += CommentRows; // Skip right amount of lines + // Since we already increment line for black later on: + if (m->IsBlack || m->variations.size() > 0) { + line--; + } + + return (line); +} + +std::uint32_t MoveTable::DrawVariations(CGEHalfMove *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) { + DRAW_DOTS(status->MarginBarWidth + status->MoveX + + status->MoveWidth * (indent + 1), + status->MoveHeight * line); + } + // Show button on the right side of the move + { + Element e; + e.prop = Property::Rectangle | Property::Button; + e.x = move_bound.x + status->MoveWidth; + 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; + } + if (!m->Folded) { + e.prop |= Property::On; + } + elements.push_back(e); + } + if (!m->Folded) { + for (CGEHalfMove *v : m->variations) { + // For each variation show show/hide button + { + Element e; + e.prop = Property::Rectangle | Property::Button; + e.x = (status->MarginBarWidth + status->MoveX + + status->MoveWidth * indent) + + status->MoveWidth - std::ceil(status->MoveHeight / 2) - + std::ceil(status->MoveHeight / 4); + e.y = (status->MoveHeight * (line + 1)) + + std::ceil(status->MoveHeight / 4); + e.width = std::ceil(status->MoveHeight / 2); + e.height = e.width; + e.ShouldApplyScroll = true; + if (status->LeftClick && IsMouseOver(e)) { + v->Hide = !v->Hide; + } + if (!v->Hide) { + e.prop |= Property::On; + } + elements.push_back(e); + } + if (!v->Hide) { + line = UpdateMoves(v, line + 1, indent + 1, v->IsBlack); + } else { + line++; + } + } + } + // New line after variation + if (m->MainLine != NULL && m->MainLine->IsBlack) { + line++; + } + return (line); +} +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/MoveTable.hpp b/src/components/MoveTable.hpp new file mode 100644 index 0000000..d220f5e --- /dev/null +++ b/src/components/MoveTable.hpp @@ -0,0 +1,65 @@ +#include "Component.hpp" +#include <cmath> + +#define IS_VISIBLE(e) \ + (((e.x + status->ScrollX) >= 0 && \ + ((e.x + status->ScrollX) <= status->CanvasWidth) && \ + (e.y + status->ScrollY) >= 0 && \ + ((e.y + status->ScrollY) <= status->CanvasHeight)) || \ + ((e.x + e.width + status->ScrollX) >= 0 && \ + ((e.x + e.width + status->ScrollX) <= status->CanvasWidth) && \ + (e.y + e.height + status->ScrollY) >= 0 && \ + ((e.y + e.height + status->ScrollY) <= status->CanvasHeight))) + +#define DRAW_DOTS(XX, YY) \ + { \ + Element e; \ + e.prop = Property::Text | Property::Dots; \ + e.x = (XX); \ + e.y = (YY); \ + e.width = status->MoveWidth; \ + e.height = status->MoveHeight; \ + e.text = "..."; \ + e.ShouldApplyScroll = true; \ + elements.push_back(e); \ + } + +#define DRAW_NB_(XX, YY, NB) \ + Element ln; \ + ln.prop = Property::Text | Property::Movenumber; \ + ln.text = std::to_string(NB); \ + ln.x = (XX); \ + ln.y = (YY); \ + ln.width = status->MarginBarWidth; \ + ln.height = status->MoveHeight; \ + ln.ShouldApplyScroll = true; \ + elements.push_back(ln); + +#define DRAW_NB(XX, YY, NB) \ + { DRAW_NB_(XX, YY, NB); } + +#define DRAW_NB_VAR(XX, YY, NB) \ + { \ + DRAW_NB_(XX, YY, NB); \ + VariationMargins.push_back(ln); \ + } + +namespace cgeditor { +class MoveTable : public Component { + std::uint32_t UpdateMoves(CGEHalfMove *, std::uint32_t, std::uint32_t,bool only_black); + std::int32_t CurrentMove; + double ImageWidth; + std::vector<Element> VariationMargins; + bool IsMouseOver(const Element &e) const; + std::uint32_t DrawComment(CGEHalfMove *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, + const Element &move_bound, + const char &indent_black); + +public: + MoveTable(Status *s); + void Refresh(); + std::vector<Element> GetVariationsMarging() { return (VariationMargins); } +}; +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/Scrollbar.cpp b/src/components/Scrollbar.cpp new file mode 100644 index 0000000..76c5c1a --- /dev/null +++ b/src/components/Scrollbar.cpp @@ -0,0 +1,112 @@ +#include "Scrollbar.hpp" + +namespace cgeditor { +Scrollbar::Scrollbar(Status *s, bool IsHorizontal) : Component(s) { + this->IsHorizontal = IsHorizontal; + + bg.prop = Property::Rectangle | Property::Scrollbarbg; + bar.prop = Property::Rectangle | Property::Scrollbar; + + if (IsHorizontal) { + bg.prop |= Property::Horizontal; + bar.x = 0; + } else { + bar.y = 0; + } + + DragX = 0; + DragY = 0; + Trigger = false; +} + +void Scrollbar::Refresh() { + if (IsHorizontal) { + bg.y = status->CanvasHeight - status->ScrollbarWidth; + bg.width = status->CanvasWidth - status->ScrollbarWidth; + bg.height = status->ScrollbarWidth; + bar.y = bg.y; + } else { + bg.x = status->CanvasWidth - status->ScrollbarWidth; + bg.width = status->CanvasWidth; + bg.height = status->CanvasHeight - status->ScrollbarWidth; + bar.x = bg.x; + } + + bar.width = bg.width; + bar.height = bg.height; + + // Compute move table canvas + double MTCanvasHeight = status->CanvasHeight - status->ScrollbarWidth; + double MTCanvasWidth = status->CanvasWidth - status->ScrollbarWidth; + + bool shouldScroll = false; + if (!IsHorizontal && status->MoveTableMaxY > MTCanvasHeight) { + bar.height = + std::ceil(bg.height * (MTCanvasHeight / status->MoveTableMaxY)); + shouldScroll = true; + } + + if (IsHorizontal && status->MoveTableMaxX > MTCanvasWidth) { + bar.width = std::ceil(bg.width * (MTCanvasWidth / status->MoveTableMaxX)); + shouldScroll = true; + } + + if (shouldScroll) { + if (IsHorizontal && status->EventHScroll != 0) { + double percent = status->EventHScroll / status->MoveTableMaxX; + double maxScroll = bg.width - bar.width; + bar.x += maxScroll * percent; + bar.x = std::max(bg.x, bar.x); + bar.x = std::min(bg.x + maxScroll, bar.x); + double curScroll = bar.x - bg.x; + double scrollPercent = curScroll / maxScroll; + status->ScrollX = + -(status->MoveTableMaxX - MTCanvasWidth) * scrollPercent; + status->EventHScroll = 0; + } else if (status->EventVScroll != 0) { + double percent = status->EventVScroll / status->MoveTableMaxY; + double maxScroll = bg.height - bar.height; + bar.y += maxScroll * percent; + bar.y = std::max(bg.y, bar.y); + bar.y = std::min(bg.y + maxScroll, bar.y); + double curScroll = bar.y - bg.y; + double scrollPercent = curScroll / maxScroll; + status->ScrollY = + -(status->MoveTableMaxY - MTCanvasHeight) * scrollPercent; + status->EventVScroll = 0; + } else if (status->LeftClick && + bar.IsOver(status->MouseX, status->MouseY)) { + DragX = bar.x; + DragY = bar.y; + Trigger = true; + } else if (Trigger && status->IsDrag) { + if (IsHorizontal) { + bar.x = DragX + (status->MouseX - status->LastMouseClicX); + bar.x = std::max(bg.x, bar.x); + double maxScroll = bg.width - bar.width; + bar.x = std::min(bg.x + maxScroll, bar.x); + double curScroll = bar.x - bg.x; + double scrollPercent = curScroll / maxScroll; + status->ScrollX = + -(status->MoveTableMaxX - MTCanvasWidth) * scrollPercent; + } else { + bar.y = DragY + (status->MouseY - status->LastMouseClicY); + bar.y = std::max(bg.y, bar.y); + double maxScroll = bg.height - bar.height; + bar.y = std::min(bg.y + maxScroll, bar.y); + double curScroll = bar.y - bg.y; + double scrollPercent = curScroll / maxScroll; + status->ScrollY = + -(status->MoveTableMaxY - MTCanvasHeight) * scrollPercent; + } + } else { + Trigger = false; + } + } + + elements.clear(); + elements.push_back(bg); + elements.push_back(bar); +} + +} // namespace cgeditor
\ No newline at end of file diff --git a/src/components/Scrollbar.hpp b/src/components/Scrollbar.hpp new file mode 100644 index 0000000..3ed97ae --- /dev/null +++ b/src/components/Scrollbar.hpp @@ -0,0 +1,16 @@ +#include "Component.hpp" +#include <algorithm> // std::max +#include <cmath> + +namespace cgeditor { +class Scrollbar : public Component { + /// @brief Set to true if it is the horizontal scrollbar + bool IsHorizontal; + Element bg,bar; + double DragY,DragX; + bool Trigger; +public: + Scrollbar(Status* s,bool IsHorizontal); + void Refresh(); +}; +} // namespace cgeditor
\ No newline at end of file |
