mirror of
https://gitlab.com/manzerbredes/cgeditor.git
synced 2025-05-23 07:47:40 +00:00
Create project
This commit is contained in:
commit
a359219e33
24 changed files with 2152 additions and 0 deletions
59
src/CGEHalfMove.cpp
Normal file
59
src/CGEHalfMove.cpp
Normal file
|
@ -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
|
47
src/CGEHalfMove.hpp
Normal file
47
src/CGEHalfMove.hpp
Normal file
|
@ -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
|
99
src/CGEditor.cpp
Normal file
99
src/CGEditor.cpp
Normal file
|
@ -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
|
37
src/CGEditor.hpp
Normal file
37
src/CGEditor.hpp
Normal file
|
@ -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
|
29
src/Types.cpp
Normal file
29
src/Types.cpp
Normal file
|
@ -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
|
88
src/Types.hpp
Normal file
88
src/Types.hpp
Normal file
|
@ -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
|
19
src/components/Component.hpp
Normal file
19
src/components/Component.hpp
Normal file
|
@ -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
|
30
src/components/Margin.cpp
Normal file
30
src/components/Margin.cpp
Normal file
|
@ -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
|
12
src/components/Margin.hpp
Normal file
12
src/components/Margin.hpp
Normal file
|
@ -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
|
64
src/components/Menu.cpp
Normal file
64
src/components/Menu.cpp
Normal file
|
@ -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
|
12
src/components/Menu.hpp
Normal file
12
src/components/Menu.hpp
Normal file
|
@ -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
|
280
src/components/MoveTable.cpp
Normal file
280
src/components/MoveTable.cpp
Normal file
|
@ -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
|
65
src/components/MoveTable.hpp
Normal file
65
src/components/MoveTable.hpp
Normal file
|
@ -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
|
112
src/components/Scrollbar.cpp
Normal file
112
src/components/Scrollbar.cpp
Normal file
|
@ -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
|
16
src/components/Scrollbar.hpp
Normal file
16
src/components/Scrollbar.hpp
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue