Put drawing program tools code in folder

This commit is contained in:
Yousef Khadadeh 2025-12-26 10:40:58 +03:00
parent 702df53794
commit 783bb7dacb
45 changed files with 133 additions and 139 deletions

View file

@ -0,0 +1,199 @@
#include "BrushTool.hpp"
#include <Helpers/ConvertVec.hpp>
#include "../../GUIStuff/GUIManager.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "DrawingProgramToolBase.hpp"
#include "../../CanvasComponents/BrushStrokeCanvasComponent.hpp"
#include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
#include "Helpers/NetworkingObjects/NetObjWeakPtr.hpp"
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
#include <chrono>
#define VEL_SMOOTH_MIN 0.6
#define VEL_SMOOTH_MAX 1.0
#define MINIMUM_DISTANCE_TO_NEXT_POINT 0.002f
BrushTool::BrushTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType BrushTool::get_type() {
return DrawingProgramToolType::BRUSH;
}
void BrushTool::switch_tool(DrawingProgramToolType newTool) {
commit_stroke();
}
void BrushTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
if(objInfoBeingEdited == erasedComp) {
isDrawing = false;
objInfoBeingEdited = nullptr;
}
}
bool BrushTool::extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
auto& points = brushStroke.d->points;
if(points.size() >= 1 && (newPoint - points[points.size() - 1].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
if(points.size() >= 3 && (newPoint - points[points.size() - 3].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
return true;
}
bool BrushTool::extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
auto& points = brushStroke.d->points;
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
if(points.size() >= 3 && (newPoint - points[points.size() - 3].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
if(points.size() >= 4 && (newPoint - points[points.size() - 4].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
return true;
}
void BrushTool::tool_update() {
if(drawP.controls.cursorHoveringOverCanvas)
drawP.world.main.input.hideCursor = true;
if(!isDrawing)
penSmoothingData.clear();
if(drawP.world.main.input.pen.isDown && drawP.controls.leftClickHeld && drawP.world.main.toolbar.tabletOptions.pressureAffectsBrushWidth) {
while(!penSmoothingData.empty() && (std::chrono::steady_clock::now() - penSmoothingData.front().t) > std::chrono::milliseconds(static_cast<int>(drawP.world.main.toolbar.tabletOptions.smoothingSamplingTime * 1000.0f)))
penSmoothingData.pop_front();
penSmoothingData.emplace_back(
drawP.world.main.input.pen.pressure,
std::chrono::steady_clock::now()
);
float averagePressure = 0.0f;
for(auto& [width, t] : penSmoothingData)
averagePressure += width;
averagePressure /= penSmoothingData.size();
penWidth = averagePressure;
if(penWidth != 0.0f) {
float brushMinSize = drawP.world.main.toolbar.tabletOptions.brushMinimumSize;
penWidth = brushMinSize + penWidth * (1.0f - brushMinSize);
}
}
else
penWidth = 1.0f;
bool isPenDown = false;
if(drawP.world.main.input.pen.isDown && drawP.controls.leftClickHeld)
isPenDown = penWidth != 0.0;
else if(drawP.controls.leftClickHeld)
isPenDown = true;
if(isPenDown) {
if(!isDrawing) {
if(drawP.layerMan.is_a_layer_being_edited()) {
isDrawing = true;
CanvasComponentContainer* newBrushStrokeContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::BRUSHSTROKE);
BrushStrokeCanvasComponent& newBrushStroke = static_cast<BrushStrokeCanvasComponent&>(newBrushStrokeContainer->get_comp());
BrushStrokeCanvasComponentPoint p;
p.pos = drawP.world.main.input.mouse.pos;
p.width = drawP.controls.relativeWidth * penWidth;
prevPointUnaltered = p.pos;
newBrushStroke.d->points.emplace_back(p);
newBrushStroke.d->color = drawP.controls.foregroundColor;
newBrushStroke.d->hasRoundCaps = hasRoundCaps;
newBrushStrokeContainer->coords = drawP.world.drawData.cam.c;
objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newBrushStrokeContainer);
addedTemporaryPoint = false;
}
}
else {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
BrushStrokeCanvasComponent& brushStroke = static_cast<BrushStrokeCanvasComponent&>(containerPtr->get_comp());
BrushStrokeCanvasComponentPoint p;
p.pos = containerPtr->coords.get_mouse_pos(drawP.world);
p.width = drawP.controls.relativeWidth * penWidth;
if(!addedTemporaryPoint) {
if(extensive_point_checking(brushStroke, p.pos)) {
brushStroke.d->points.emplace_back(p);
addedTemporaryPoint = true;
}
else
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
}
if(addedTemporaryPoint) {
const BrushStrokeCanvasComponentPoint& prevP = brushStroke.d->points[brushStroke.d->points.size() - 2];
float distToPrev = (p.pos - prevP.pos).norm();
if(extensive_point_checking_back(brushStroke, p.pos)) {
brushStroke.d->points.back().pos = p.pos;
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
brushStroke.d->points[brushStroke.d->points.size() - 2].width = std::max(brushStroke.d->points[brushStroke.d->points.size() - 2].width, p.width);
}
if((!drawingMinimumRelativeToSize && distToPrev >= 10.0) || (drawingMinimumRelativeToSize && distToPrev >= drawP.controls.relativeWidth * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) {
brushStroke.d->points.back() = p;
addedTemporaryPoint = false;
if(midwayInterpolation) {
if(brushStroke.d->points.size() != 2) // Don't interpolate the first point
brushStroke.d->points[brushStroke.d->points.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5;
prevPointUnaltered = p.pos;
}
}
}
containerPtr->send_comp_update(drawP, false);
containerPtr->commit_update(drawP);
}
}
else if(isDrawing) {
isDrawing = false;
commit_stroke();
}
}
void BrushTool::commit_stroke() {
if(objInfoBeingEdited) {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
containerPtr->commit_update(drawP);
containerPtr->send_comp_update(drawP, true);
objInfoBeingEdited = nullptr;
}
}
void BrushTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("brush tool");
t.gui.text_label_centered("Brush");
t.gui.checkbox_field("hasroundcaps", "Round Caps", &hasRoundCaps);
t.gui.slider_scalar_field("relwidth", "Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.pop_id();
}
bool BrushTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
bool BrushTool::prevent_undo_or_redo() {
return isDrawing;
}
void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(!drawData.main->toolbar.io->hoverObstructed) {
SkPaint linePaint;
linePaint.setColor4f(drawP.world.canvasTheme.get_tool_front_color());
linePaint.setStroke(true);
linePaint.setStrokeWidth(0.0f);
float width;
if(isDrawing)
width = drawP.controls.relativeWidth * penWidth * 0.5f;
else
width = drawP.controls.relativeWidth * 0.5f;
Vector2f pos = drawData.main->input.mouse.pos;
canvas->drawCircle(pos.x(), pos.y(), width, linePaint);
}
}

View file

@ -0,0 +1,46 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include <Helpers/Hashes.hpp>
#include <include/core/SkPath.h>
#include <include/core/SkVertices.h>
#include <Helpers/SCollision.hpp>
#include <deque>
#include <include/core/SkCanvas.h>
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
class DrawingProgram;
struct DrawData;
class BrushStrokeCanvasComponent;
class BrushTool : public DrawingProgramToolBase {
public:
BrushTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual void tool_update() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
private:
bool extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint);
bool extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint);
void commit_stroke();
float penWidth = 1.0f;
bool addedTemporaryPoint = false;
struct SmoothingPoint {
float val;
std::chrono::steady_clock::time_point t;
};
std::deque<SmoothingPoint> penSmoothingData;
CanvasComponentContainer::ObjInfoSharedPtr objInfoBeingEdited;
bool isDrawing = false;
bool hasRoundCaps = true;
bool drawingMinimumRelativeToSize = true;
bool midwayInterpolation = true;
Vector2f prevPointUnaltered = {0, 0};
};

View file

@ -0,0 +1,53 @@
#include "DrawingProgramToolBase.hpp"
#include "BrushTool.hpp"
#include "EraserTool.hpp"
#include "PanCanvasTool.hpp"
#include "RectSelectTool.hpp"
#include "LassoSelectTool.hpp"
#include "RectDrawTool.hpp"
#include "EllipseDrawTool.hpp"
#include "TextBoxTool.hpp"
#include "EyeDropperTool.hpp"
#include "ScreenshotTool.hpp"
#include "EditTool.hpp"
#include "GridModifyTool.hpp"
#include "ZoomCanvasTool.hpp"
DrawingProgramToolBase::DrawingProgramToolBase(DrawingProgram& initDrawP):
drawP(initDrawP)
{}
DrawingProgramToolBase::~DrawingProgramToolBase() {}
std::unique_ptr<DrawingProgramToolBase> DrawingProgramToolBase::allocate_tool_type(DrawingProgram& drawP, DrawingProgramToolType t) {
switch(t) {
case DrawingProgramToolType::BRUSH:
return std::make_unique<BrushTool>(drawP);
case DrawingProgramToolType::ERASER:
return std::make_unique<EraserTool>(drawP);
case DrawingProgramToolType::LASSOSELECT:
return std::make_unique<LassoSelectTool>(drawP);
case DrawingProgramToolType::RECTSELECT:
return std::make_unique<RectSelectTool>(drawP);
case DrawingProgramToolType::RECTANGLE:
return std::make_unique<RectDrawTool>(drawP);
case DrawingProgramToolType::ELLIPSE:
return std::make_unique<EllipseDrawTool>(drawP);
case DrawingProgramToolType::TEXTBOX:
return std::make_unique<TextBoxTool>(drawP);
case DrawingProgramToolType::EYEDROPPER:
return std::make_unique<EyeDropperTool>(drawP);
case DrawingProgramToolType::SCREENSHOT:
return std::make_unique<ScreenshotTool>(drawP);
case DrawingProgramToolType::GRIDMODIFY:
return std::make_unique<GridModifyTool>(drawP);
case DrawingProgramToolType::EDIT:
return std::make_unique<EditTool>(drawP);
case DrawingProgramToolType::ZOOM:
return std::make_unique<ZoomCanvasTool>(drawP);
case DrawingProgramToolType::PAN:
return std::make_unique<PanCanvasTool>(drawP);
}
return nullptr;
}

View file

@ -0,0 +1,41 @@
#pragma once
#include <include/core/SkCanvas.h>
#include "../../DrawData.hpp"
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
#define MINIMUM_DISTANCE_BETWEEN_BOUNDS 10.0f
enum class DrawingProgramToolType : int {
BRUSH = 0,
ERASER,
LASSOSELECT,
RECTSELECT,
RECTANGLE,
ELLIPSE,
TEXTBOX,
EYEDROPPER,
SCREENSHOT,
GRIDMODIFY,
EDIT,
ZOOM,
PAN
};
class DrawingProgramToolBase {
public:
DrawingProgramToolBase(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() = 0;
virtual void gui_toolbox() = 0;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) = 0;
virtual bool right_click_popup_gui(Vector2f popupPos) = 0;
virtual void tool_update() = 0;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) = 0;
virtual void switch_tool(DrawingProgramToolType newTool) = 0;
virtual bool prevent_undo_or_redo() = 0;
virtual ~DrawingProgramToolBase();
static std::unique_ptr<DrawingProgramToolBase> allocate_tool_type(DrawingProgram& drawP, DrawingProgramToolType t);
protected:
DrawingProgram& drawP;
};

View file

@ -0,0 +1,194 @@
#include "EditTool.hpp"
#include <chrono>
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "Helpers/MathExtras.hpp"
#include "Helpers/SCollision.hpp"
#include "../../SharedTypes.hpp"
#include <cereal/types/vector.hpp>
#include <memory>
#include "EditTools/TextBoxEditTool.hpp"
#include "EditTools/RectDrawEditTool.hpp"
#include "EditTools/ImageEditTool.hpp"
#include "EditTools/EllipseDrawEditTool.hpp"
EditTool::EditTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType EditTool::get_type() {
return DrawingProgramToolType::EDIT;
}
void EditTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
if(isEditing) {
bool editHappened = compEditTool->edit_gui(objInfoBeingEdited);
if(editHappened)
objInfoBeingEdited->obj->commit_update(drawP);
}
else {
t.gui.push_id("edit tool");
t.gui.text_label_centered("Edit");
t.gui.pop_id();
}
}
void EditTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
if(erasedComp == objInfoBeingEdited)
switch_tool(get_type());
}
bool EditTool::right_click_popup_gui(Vector2f popupPos) {
if(isEditing)
return compEditTool->right_click_popup_gui(objInfoBeingEdited, popupPos);
else
return drawP.selection_action_menu(popupPos);
}
void EditTool::add_point_handle(const HandleData& handle) {
pointHandles.emplace_back(handle);
}
void EditTool::switch_tool(DrawingProgramToolType newTool) {
if(objInfoBeingEdited) {
compEditTool->commit_edit_updates(objInfoBeingEdited, prevData);
objInfoBeingEdited->obj->commit_update(drawP);
objInfoBeingEdited->obj->send_comp_update(drawP, true);
objInfoBeingEdited = nullptr;
}
pointHandles.clear();
pointDragging = nullptr;
isEditing = false;
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
}
void EditTool::edit_start(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
isEditing = true;
switch(comp->obj->get_comp().get_type()) {
case CanvasComponentType::TEXTBOX: {
compEditTool = std::make_unique<TextBoxEditTool>(drawP);
break;
}
case CanvasComponentType::ELLIPSE: {
compEditTool = std::make_unique<EllipseDrawEditTool>(drawP);
break;
}
case CanvasComponentType::RECTANGLE: {
compEditTool = std::make_unique<RectDrawEditTool>(drawP);
break;
}
case CanvasComponentType::IMAGE: {
compEditTool = std::make_unique<ImageEditTool>(drawP);
break;
}
default: {
isEditing = false;
break;
}
}
if(isEditing) {
objInfoBeingEdited = comp;
compEditTool->edit_start(*this, objInfoBeingEdited, prevData);
}
}
bool EditTool::is_editable(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
return comp->obj->get_comp().get_type() != CanvasComponentType::BRUSHSTROKE;
}
void EditTool::tool_update() {
if(!isEditing) {
SCollision::AABB<WorldScalar> mouseAABB{drawP.world.get_mouse_world_pos() - WorldVec{0.5f, 0.5f}, drawP.world.get_mouse_world_pos() + WorldVec{0.5f, 0.5f}};
SCollision::ColliderCollection<WorldScalar> cMouseAABB;
cMouseAABB.aabb.emplace_back(mouseAABB);
cMouseAABB.recalculate_bounds();
SCollision::AABB<float> camMouseAABB{drawP.world.main.input.mouse.pos - Vector2f{0.5f, 0.5f}, drawP.world.main.input.mouse.pos + Vector2f{0.5f, 0.5f}};
SCollision::ColliderCollection<float> camCMouseAABB;
camCMouseAABB.aabb.emplace_back(camMouseAABB);
camCMouseAABB.recalculate_bounds();
if(drawP.controls.leftClick) {
bool modifySelection = !drawP.selection.is_being_transformed();
if(drawP.world.main.input.mouse.leftClicks >= 2 && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
CanvasComponentContainer::ObjInfoSharedPtr selectedObjectToEdit = drawP.selection.get_front_object_colliding_with(camCMouseAABB);
if(selectedObjectToEdit && is_editable(selectedObjectToEdit)) {
drawP.selection.deselect_all();
edit_start(selectedObjectToEdit);
modifySelection = false;
}
}
if(modifySelection) {
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(camCMouseAABB, true);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true);
}
}
}
}
else {
SCollision::Circle<float> mouseCircle{drawP.world.main.input.mouse.pos, 1.0f};
SCollision::ColliderCollection<float> cMouseCircle;
cMouseCircle.circle.emplace_back(mouseCircle);
cMouseCircle.recalculate_bounds();
bool isMovingPoint = false;
bool clickedAway = false;
if(!pointDragging && drawP.controls.leftClick) {
for(HandleData& h : pointHandles) {
if(SCollision::collide(mouseCircle, SCollision::Circle<float>(drawP.world.drawData.cam.c.to_space(objInfoBeingEdited->obj->coords.from_space(*h.p)), drawP.drag_point_radius()))) {
pointDragging = &h;
isMovingPoint = true;
break;
}
}
if(!isMovingPoint && !objInfoBeingEdited->obj->collides_with_cam_coords(drawP.world.drawData.cam.c, cMouseCircle))
clickedAway = true;
}
else if(pointDragging) {
if(drawP.controls.leftClickHeld) {
Vector2f newPos = objInfoBeingEdited->obj->coords.get_mouse_pos(drawP.world);
if(newPos != *pointDragging->p) {
if(pointDragging->min)
newPos = cwise_vec_max((*pointDragging->min + Vector2f{pointDragging->minimumDistanceBetweenBoundsAndPoint, pointDragging->minimumDistanceBetweenBoundsAndPoint}).eval(), newPos);
if(pointDragging->max)
newPos = cwise_vec_min((*pointDragging->max - Vector2f{pointDragging->minimumDistanceBetweenBoundsAndPoint, pointDragging->minimumDistanceBetweenBoundsAndPoint}).eval(), newPos);
*pointDragging->p = newPos;
objInfoBeingEdited->obj->commit_update(drawP);
}
isMovingPoint = true;
}
else
pointDragging = nullptr;
}
objInfoBeingEdited->obj->send_comp_update(drawP, false);
bool shouldNotReset = compEditTool->edit_update(objInfoBeingEdited);
if(!shouldNotReset || clickedAway)
switch_tool(get_type());
}
}
bool EditTool::prevent_undo_or_redo() {
return isEditing;
}
void EditTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(isEditing) {
if(objInfoBeingEdited) {
for(HandleData& h : pointHandles)
drawP.draw_drag_circle(canvas, drawData.cam.c.to_space((objInfoBeingEdited->obj->coords.from_space(*h.p))), {0.1f, 0.9f, 0.9f, 1.0f}, drawData);
}
}
}

View file

@ -0,0 +1,38 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include <Helpers/SCollision.hpp>
#include <any>
#include "EditTools/DrawingProgramEditToolBase.hpp"
class DrawingProgram;
class EditTool : public DrawingProgramToolBase {
public:
struct HandleData {
Vector2f* p;
Vector2f* min;
Vector2f* max;
float minimumDistanceBetweenBoundsAndPoint = MINIMUM_DISTANCE_BETWEEN_BOUNDS;
};
EditTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void tool_update() override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
void add_point_handle(const HandleData& handle);
void edit_start(const CanvasComponentContainer::ObjInfoSharedPtr& comp);
bool is_editable(const CanvasComponentContainer::ObjInfoSharedPtr& comp);
std::unique_ptr<DrawingProgramEditToolBase> compEditTool;
std::vector<HandleData> pointHandles;
CanvasComponentContainer::ObjInfoSharedPtr objInfoBeingEdited;
bool isEditing = false;
HandleData* pointDragging = nullptr;
std::any prevData;
};

View file

@ -0,0 +1,14 @@
#include "DrawingProgramEditToolBase.hpp"
#include "../../../MainProgram.hpp"
DrawingProgramEditToolBase::DrawingProgramEditToolBase(DrawingProgram& initDrawP):
drawP(initDrawP)
{}
bool DrawingProgramEditToolBase::right_click_popup_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp, Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
DrawingProgramEditToolBase::~DrawingProgramEditToolBase() {}

View file

@ -0,0 +1,20 @@
#pragma once
#include <include/core/SkCanvas.h>
#include "../../../DrawData.hpp"
#include "../../../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
class EditTool;
class DrawingProgramEditToolBase {
public:
DrawingProgramEditToolBase(DrawingProgram& initDrawP);
virtual void edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& compContainer, std::any& prevData) = 0;
virtual void commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& compContainer, std::any& prevData) = 0;
virtual bool edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& compContainer) = 0;
virtual bool edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& compContainer) = 0;
virtual bool right_click_popup_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp, Vector2f popupPos);
virtual ~DrawingProgramEditToolBase();
protected:
DrawingProgram& drawP;
};

View file

@ -0,0 +1,58 @@
#include "EllipseDrawEditTool.hpp"
#include "../../DrawingProgram.hpp"
#include "../../../World.hpp"
#include "../../../MainProgram.hpp"
#include "../../../DrawData.hpp"
#include <cereal/types/vector.hpp>
#include <memory>
#include "../EditTool.hpp"
EllipseDrawEditTool::EllipseDrawEditTool(DrawingProgram& initDrawP):
DrawingProgramEditToolBase(initDrawP)
{}
bool EllipseDrawEditTool::edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
auto& a = static_cast<EllipseCanvasComponent&>(comp->obj->get_comp());
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("edit tool ellipse");
t.gui.text_label_centered("Edit Ellipse");
if(t.gui.radio_button_field("fillonly", "Fill only", a.d.fillStrokeMode == 0)) a.d.fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", a.d.fillStrokeMode == 1)) a.d.fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", a.d.fillStrokeMode == 2)) a.d.fillStrokeMode = 2;
if(a.d.fillStrokeMode == 0 || a.d.fillStrokeMode == 2) {
t.gui.left_to_right_line_layout([&]() {
if(t.gui.color_button_big("Fill Color", &a.d.fillColor, &a.d.fillColor == t.colorRight))
t.color_selector_right(&a.d.fillColor == t.colorRight ? nullptr : &a.d.fillColor);
t.gui.text_label("Fill Color");
});
}
if(a.d.fillStrokeMode == 1 || a.d.fillStrokeMode == 2) {
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &a.d.strokeWidth, 3.0f, 40.0f);
t.gui.left_to_right_line_layout([&]() {
if(t.gui.color_button_big("Outline Color", &a.d.strokeColor, &a.d.strokeColor == t.colorRight))
t.color_selector_right(&a.d.strokeColor == t.colorRight ? nullptr : &a.d.strokeColor);
t.gui.text_label("Outline Color");
});
}
t.gui.pop_id();
bool editHappened = (!oldData.has_value()) || (a.d != oldData);
oldData = a.d;
return editHappened;
}
void EllipseDrawEditTool::edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
auto& a = static_cast<EllipseCanvasComponent&>(comp->obj->get_comp());
prevData = a.d;
editTool.add_point_handle({&a.d.p1, nullptr, &a.d.p2});
editTool.add_point_handle({&a.d.p2, &a.d.p1, nullptr});
}
void EllipseDrawEditTool::commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
}
bool EllipseDrawEditTool::edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
return true;
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "DrawingProgramEditToolBase.hpp"
#include "../../../CanvasComponents/CanvasComponentContainer.hpp"
#include "../../../CanvasComponents//EllipseCanvasComponent.hpp"
#include <any>
#include <Helpers/SCollision.hpp>
class DrawingProgram;
class EllipseDrawEditTool : public DrawingProgramEditToolBase {
public:
EllipseDrawEditTool(DrawingProgram& initDrawP);
virtual void edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual void commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual bool edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
virtual bool edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
private:
void commit();
std::optional<EllipseCanvasComponent::Data> oldData;
};

View file

@ -0,0 +1,59 @@
#include "ImageEditTool.hpp"
#include "../../DrawingProgram.hpp"
#include "../../../CanvasComponents/ImageCanvasComponent.hpp"
#include "../../../World.hpp"
#include "../../../Toolbar.hpp"
#include "../../../MainProgram.hpp"
#include "../../../ResourceManager.hpp"
#include "DrawingProgramEditToolBase.hpp"
#include <fstream>
#ifdef __EMSCRIPTEN__
#include <EmscriptenHelpers/emscripten_browser_file.h>
#endif
ImageEditTool::ImageEditTool(DrawingProgram& initDrawP):
DrawingProgramEditToolBase(initDrawP)
{}
void ImageEditTool::edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
}
void ImageEditTool::commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
}
bool ImageEditTool::edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
return true;
}
bool ImageEditTool::edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
ImageCanvasComponent& a = static_cast<ImageCanvasComponent&>(comp->obj->get_comp());
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("edit tool image");
t.gui.text_label_centered("File Properties");
auto resourceData = drawP.world.netObjMan.get_obj_temporary_ref_from_id<ResourceData>(a.d.imageID);
if(resourceData) {
t.gui.text_label("Name: " + resourceData->name);
if(t.gui.text_button_wide("file download", "Download file")) {
#ifdef __EMSCRIPTEN__
emscripten_browser_file::download(
resourceData->name,
"application/octet-binary",
*resourceData->data
);
#endif
t.open_file_selector("Download File", {{"Any File", "*"}}, [resourceData](const std::filesystem::path& p, const auto& e) {
std::ofstream f(p, std::ios::binary);
if(f.is_open()) {
f << *resourceData->data;
f.close();
}
}, resourceData->name, true);
}
}
else
t.gui.text_label_centered("Loading resource...");
t.gui.pop_id();
return false;
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "DrawingProgramEditToolBase.hpp"
#include <any>
class DrawingProgram;
class ImageEditTool : public DrawingProgramEditToolBase {
public:
ImageEditTool(DrawingProgram& initDrawP);
virtual void edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual void commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual bool edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
virtual bool edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
};

View file

@ -0,0 +1,55 @@
#include "RectDrawEditTool.hpp"
#include "../../DrawingProgram.hpp"
#include "../../../World.hpp"
#include "../../../MainProgram.hpp"
#include <memory>
#include "../EditTool.hpp"
RectDrawEditTool::RectDrawEditTool(DrawingProgram& initDrawP):
DrawingProgramEditToolBase(initDrawP)
{}
bool RectDrawEditTool::edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
auto& a = static_cast<RectangleCanvasComponent&>(comp->obj->get_comp());
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("edit tool rectangle");
t.gui.text_label_centered("Edit Rectangle");
t.gui.slider_scalar_field("relradiuswidth", "Corner Radius", &a.d.cornerRadius, 0.0f, 40.0f);
if(t.gui.radio_button_field("fillonly", "Fill only", a.d.fillStrokeMode == 0)) a.d.fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", a.d.fillStrokeMode == 1)) a.d.fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", a.d.fillStrokeMode == 2)) a.d.fillStrokeMode = 2;
if(a.d.fillStrokeMode == 0 || a.d.fillStrokeMode == 2) {
t.gui.left_to_right_line_layout([&]() {
if(t.gui.color_button_big("Fill Color", &a.d.fillColor, &a.d.fillColor == t.colorRight))
t.color_selector_right(&a.d.fillColor == t.colorRight ? nullptr : &a.d.fillColor);
t.gui.text_label("Fill Color");
});
}
if(a.d.fillStrokeMode == 1 || a.d.fillStrokeMode == 2) {
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &a.d.strokeWidth, 3.0f, 40.0f);
t.gui.left_to_right_line_layout([&]() {
if(t.gui.color_button_big("Outline Color", &a.d.strokeColor, &a.d.strokeColor == t.colorRight))
t.color_selector_right(&a.d.strokeColor == t.colorRight ? nullptr : &a.d.strokeColor);
t.gui.text_label("Outline Color");
});
}
t.gui.pop_id();
bool editHappened = (!oldData.has_value()) || (a.d != oldData);
oldData = a.d;
return editHappened;
}
void RectDrawEditTool::edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
auto& a = static_cast<RectangleCanvasComponent&>(comp->obj->get_comp());
prevData = a.d;
editTool.add_point_handle({&a.d.p1, nullptr, &a.d.p2});
editTool.add_point_handle({&a.d.p2, &a.d.p1, nullptr});
}
void RectDrawEditTool::commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
}
bool RectDrawEditTool::edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
return true;
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "DrawingProgramEditToolBase.hpp"
#include "../../../CanvasComponents/RectangleCanvasComponent.hpp"
#include <Helpers/SCollision.hpp>
#include <any>
class DrawingProgram;
class RectDrawEditTool : public DrawingProgramEditToolBase {
public:
RectDrawEditTool(DrawingProgram& initDrawP);
virtual void edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual void commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual bool edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
virtual bool edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
private:
std::optional<RectangleCanvasComponent::Data> oldData;
};

View file

@ -0,0 +1,300 @@
#include "TextBoxEditTool.hpp"
#include "../../DrawingProgram.hpp"
#include "../../../MainProgram.hpp"
#include "../../../DrawData.hpp"
#include "Helpers/ConvertVec.hpp"
#include "Helpers/SCollision.hpp"
#include <cereal/types/vector.hpp>
#include <include/core/SkFontStyle.h>
#include <modules/skparagraph/include/DartTypes.h>
#include <modules/skparagraph/include/ParagraphStyle.h>
#include <modules/skparagraph/include/TextStyle.h>
#include <modules/skparagraph/src/ParagraphBuilderImpl.h>
#include <modules/skunicode/include/SkUnicode_icu.h>
#include <memory>
#include "../EditTool.hpp"
using namespace RichText;
TextBoxEditTool::TextBoxEditTool(DrawingProgram& initDrawP):
DrawingProgramEditToolBase(initDrawP)
{}
bool TextBoxEditTool::edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("edit tool text");
t.gui.text_label_centered("Edit Text");
t.gui.left_to_right_line_layout([&]() {
t.gui.text_label("Font");
if(t.gui.font_picker("font picker", &newFontName)) {
currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES] = std::make_shared<FontFamiliesTextStyleModifier>(std::vector<SkString>{SkString{newFontName.c_str(), newFontName.size()}});
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES]);});
}
});
t.gui.left_to_right_line_centered_layout([&]() {
if(t.gui.svg_icon_button("Bold button", "data/icons/RemixIcon/bold.svg", newIsBold)) {
newIsBold = !newIsBold;
currentMods[TextStyleModifier::ModifierType::WEIGHT] = std::make_shared<WeightTextStyleModifier>(newIsBold ? SkFontStyle::Weight::kBold_Weight : SkFontStyle::Weight::kNormal_Weight);
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::WEIGHT]);});
}
if(t.gui.svg_icon_button("Italic button", "data/icons/RemixIcon/italic.svg", newIsItalic)) {
newIsItalic = !newIsItalic;
currentMods[TextStyleModifier::ModifierType::SLANT] = std::make_shared<SlantTextStyleModifier>(newIsItalic ? SkFontStyle::Slant::kItalic_Slant : SkFontStyle::Slant::kUpright_Slant);
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::SLANT]);});
}
bool decorationEdited = false;
if(t.gui.svg_icon_button("Underline button", "data/icons/RemixIcon/underline.svg", newIsUnderlined)) {
newIsUnderlined = !newIsUnderlined;
decorationEdited = true;
}
if(t.gui.svg_icon_button("Strikethrough button", "data/icons/RemixIcon/strikethrough.svg", newIsLinethrough)) {
newIsLinethrough = !newIsLinethrough;
decorationEdited = true;
}
if(t.gui.svg_icon_button("Overline button", "data/icons/RemixIcon/overline.svg", newIsOverline)) {
newIsOverline = !newIsOverline;
decorationEdited = true;
}
if(decorationEdited) {
currentMods[TextStyleModifier::ModifierType::DECORATION] = std::make_shared<DecorationTextStyleModifier>(get_new_decoration_value());
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::DECORATION]);});
}
});
t.gui.left_to_right_line_centered_layout([&]() {
if(t.gui.svg_icon_button("Align left button", "data/icons/RemixIcon/align-left.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kLeft))
add_undo([&](){a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kLeft);});
if(t.gui.svg_icon_button("Align center button", "data/icons/RemixIcon/align-center.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kCenter))
add_undo([&](){a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kCenter);});
if(t.gui.svg_icon_button("Align right button", "data/icons/RemixIcon/align-right.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kRight))
add_undo([&](){a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kRight);});
if(t.gui.svg_icon_button("Align justify button", "data/icons/RemixIcon/align-justify.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kJustify))
add_undo([&](){a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kJustify);});
if(t.gui.svg_icon_button("Text direction left", "data/icons/RemixIcon/text-direction-l.svg", currentPStyle.textDirection == skia::textlayout::TextDirection::kLtr))
add_undo([&](){a.textBox->set_text_direction_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kLtr);});
if(t.gui.svg_icon_button("Text direction right", "data/icons/RemixIcon/text-direction-r.svg", currentPStyle.textDirection == skia::textlayout::TextDirection::kRtl))
add_undo([&](){a.textBox->set_text_direction_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kRtl);});
});
if(t.gui.slider_scalar_field<uint32_t>("Font Size Slider", "Font Size", &newFontSize, 3, 100)) {
hold_undo_data("Font Size", a);
currentMods[TextStyleModifier::ModifierType::SIZE] = std::make_shared<SizeTextStyleModifier>(newFontSize);
a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::SIZE]);
}
else
release_undo_data("Font Size");
// Text color
t.gui.left_to_right_line_layout([&]() {
if(t.gui.color_button_big("Text Color", &newTextColor, &newTextColor == t.colorRight))
t.color_selector_right(&newTextColor == t.colorRight ? nullptr : &newTextColor);
t.gui.text_label("Text Color");
});
if(&newTextColor == t.colorRight)
hold_undo_data("Text Color", a);
else
release_undo_data("Text Color");
if((&newTextColor == t.colorRight) && t.isUpdatingColorRight) {
currentMods[TextStyleModifier::ModifierType::COLOR] = std::make_shared<ColorTextStyleModifier>(newTextColor);
a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::COLOR]);
}
// Highlight color
t.gui.left_to_right_line_layout([&]() {
if(t.gui.color_button_big("Highlight Color", &newHighlightColor, &newHighlightColor == t.colorRight)) {
if(newHighlightColor.w() == 0.0f) { // Make highlight appear when the button is pressed
newHighlightColor.w() = 1.0f;
currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);});
}
t.color_selector_right(&newHighlightColor == t.colorRight ? nullptr : &newHighlightColor);
}
if(newHighlightColor.w() != 0.0f) {
if(t.gui.svg_icon_button("Remove Highlight Color", "data/icons/close.svg")) {
newHighlightColor = {0.0f, 0.0f, 0.0f, 0.0f};
currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);});
}
}
t.gui.text_label("Highlight Color");
});
if(&newHighlightColor == t.colorRight)
hold_undo_data("Highlight Color", a);
else
release_undo_data("Highlight Color");
if((&newHighlightColor == t.colorRight) && t.isUpdatingColorRight) {
currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);
}
t.gui.pop_id();
if(a.textBox->inputChangedTextBox)
set_styles_at_selection(a);
// NOTE: There should be a system that periodically sends updates even if no changes are made, since unreliable channels can drop update data
bool oldInputChangedTextBox = a.textBox->inputChangedTextBox;
a.textBox->inputChangedTextBox = false;
return oldInputChangedTextBox;
}
bool TextBoxEditTool::right_click_popup_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp, Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
bool shouldClose = false;
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
t.gui.list_popup_menu("Text popup menu", popupPos, [&]() {
t.gui.text_label_light("Text menu");
InputManager& input = drawP.world.main.input;
if(t.gui.text_button_left_transparent("Paste", "Paste")) {
input.call_text_paste(true);
shouldClose = true;
}
if(t.gui.text_button_left_transparent("Paste without formatting", "Paste without formatting")) {
input.call_text_paste(false);
shouldClose = true;
}
if(a.cursor->selectionBeginPos != a.cursor->selectionEndPos) {
if(t.gui.text_button_left_transparent("Copy", "Copy")) {
input.set_clipboard_plain_and_richtext_pair(a.textBox->process_copy(*a.cursor));
shouldClose = true;
}
if(t.gui.text_button_left_transparent("Cut", "Cut")) {
input.text.do_textbox_operation_with_undo([&]() {
input.set_clipboard_plain_and_richtext_pair(a.textBox->process_cut(*a.cursor));
});
shouldClose = true;
}
}
});
return !shouldClose;
}
void TextBoxEditTool::hold_undo_data(const std::string& undoName, TextBoxCanvasComponent& a) {
auto it = undoHeldData.find(undoName);
if(it != undoHeldData.end()) {
std::pair<RichText::TextBox::Cursor, RichText::TextData>& undoData = it->second;
if(undoData.first != *a.cursor)
undoHeldData.erase(it);
}
else if(a.cursor->selectionBeginPos != a.cursor->selectionEndPos)
undoHeldData.emplace(undoName, std::pair<RichText::TextBox::Cursor, RichText::TextData>{*a.cursor, a.textBox->get_rich_text_data()});
}
void TextBoxEditTool::release_undo_data(const std::string& undoName) {
auto it = undoHeldData.find(undoName);
if(it != undoHeldData.end()) {
std::pair<RichText::TextBox::Cursor, RichText::TextData>& undoData = it->second;
InputManager& input = drawP.world.main.input;
input.text.add_textbox_undo(undoData.first, undoData.second);
undoHeldData.erase(it);
}
}
void TextBoxEditTool::add_undo_if_selecting_area(TextBoxCanvasComponent& a, const std::function<void()>& func) {
if(a.cursor->selectionBeginPos == a.cursor->selectionEndPos)
func();
else {
InputManager& input = drawP.world.main.input;
input.text.do_textbox_operation_with_undo(func);
}
}
void TextBoxEditTool::add_undo(const std::function<void()>& func) {
InputManager& input = drawP.world.main.input;
input.text.do_textbox_operation_with_undo(func);
}
uint8_t TextBoxEditTool::get_new_decoration_value() {
uint8_t toRet = skia::textlayout::TextDecoration::kNoDecoration;
if(newIsUnderlined)
toRet |= skia::textlayout::TextDecoration::kUnderline;
if(newIsLinethrough)
toRet |= skia::textlayout::TextDecoration::kLineThrough;
if(newIsOverline)
toRet |= skia::textlayout::TextDecoration::kOverline;
return toRet;
}
void TextBoxEditTool::set_styles_at_selection(TextBoxCanvasComponent& a) {
TextPosition start = std::min(a.cursor->selectionBeginPos, a.cursor->selectionEndPos);
TextPosition end = std::max(a.cursor->selectionBeginPos, a.cursor->selectionEndPos);
if(start == end)
currentMods = a.textBox->get_mods_used_at_pos(a.textBox->move(TextBox::Movement::LEFT, start));
else
currentMods = a.textBox->get_mods_used_at_pos(start);
newFontName = std::static_pointer_cast<FontFamiliesTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES])->get_families().back().c_str();
newFontSize = std::static_pointer_cast<SizeTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::SIZE])->get_size();
newTextColor = std::static_pointer_cast<ColorTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::COLOR])->get_color();
newHighlightColor = std::static_pointer_cast<HighlightColorTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR])->get_color();
newIsBold = std::static_pointer_cast<WeightTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::WEIGHT])->get_weight() == SkFontStyle::Weight::kBold_Weight;
newIsItalic = std::static_pointer_cast<SlantTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::SLANT])->get_slant() == SkFontStyle::Slant::kItalic_Slant;
newIsUnderlined = std::static_pointer_cast<DecorationTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::DECORATION])->get_decoration_value() & skia::textlayout::TextDecoration::kUnderline;
newIsLinethrough = std::static_pointer_cast<DecorationTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::DECORATION])->get_decoration_value() & skia::textlayout::TextDecoration::kLineThrough;
newIsOverline = std::static_pointer_cast<DecorationTextStyleModifier>(currentMods[TextStyleModifier::ModifierType::DECORATION])->get_decoration_value() & skia::textlayout::TextDecoration::kOverline;
currentPStyle = a.textBox->get_paragraph_style_data_at(start.fParagraphIndex);
}
void TextBoxEditTool::commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
a.d.editing = false;
comp->obj->commit_update(drawP);
}
TextBoxEditTool::TextBoxEditToolAllData TextBoxEditTool::get_all_data(const TextBoxCanvasComponent& a) {
return {
.textboxData = a.d,
.richText = a.textBox->get_rich_text_data()
};
}
void TextBoxEditTool::edit_start(EditTool& editTool, const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
auto& cur = a.cursor;
auto& textbox = a.textBox;
cur = std::make_shared<TextBox::Cursor>();
Vector2f textSelectPos = a.get_mouse_pos(drawP);
textbox->process_mouse_left_button(*cur, textSelectPos, 1, false, false);
prevData = get_all_data(a);
a.d.editing = true;
a.textBox->inputChangedTextBox = true;
set_styles_at_selection(a);
editTool.add_point_handle({&a.d.p1, nullptr, &a.d.p2});
editTool.add_point_handle({&a.d.p2, &a.d.p1, nullptr});
}
bool TextBoxEditTool::edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
auto& a = static_cast<TextBoxCanvasComponent&>(comp->obj->get_comp());
SCollision::ColliderCollection<float> mousePointCollection;
mousePointCollection.circle.emplace_back(drawP.world.main.input.mouse.pos, 1.0f);
mousePointCollection.recalculate_bounds();
a.init_text_box(drawP);
InputManager& input = drawP.world.main.input;
bool collidesWithBox = comp->obj->collides_with_cam_coords(drawP.world.drawData.cam.c, mousePointCollection);
input.text.set_rich_text_box_input(a.textBox, a.cursor, true, currentMods);
a.textBox->process_mouse_left_button(*a.cursor, a.get_mouse_pos(drawP), (drawP.controls.leftClick && collidesWithBox) ? drawP.world.main.input.mouse.leftClicks : 0, drawP.controls.leftClickHeld, input.key(InputManager::KEY_GENERIC_LSHIFT).held);
return true;
}

View file

@ -0,0 +1,50 @@
#pragma once
#include "../../../CanvasComponents/TextBoxCanvasComponent.hpp"
#include "DrawingProgramEditToolBase.hpp"
#include <Helpers/SCollision.hpp>
#include <any>
class DrawingProgram;
class TextBoxEditTool : public DrawingProgramEditToolBase {
public:
TextBoxEditTool(DrawingProgram& initDrawP);
virtual void edit_start(EditTool& editTool,const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual void commit_edit_updates(const CanvasComponentContainer::ObjInfoSharedPtr& comp, std::any& prevData) override;
virtual bool edit_update(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
virtual bool edit_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp) override;
virtual bool right_click_popup_gui(const CanvasComponentContainer::ObjInfoSharedPtr& comp, Vector2f popupPos) override;
private:
struct TextBoxEditToolAllData {
TextBoxCanvasComponent::Data textboxData;
RichText::TextData richText;
};
uint32_t newFontSize = 15;
Vector4f newTextColor{1.0f, 1.0f, 1.0f, 1.0f};
Vector4f newHighlightColor{1.0f, 1.0f, 1.0f, 0.0f};
bool newIsBold = false;
bool newIsItalic = false;
bool newIsUnderlined = false;
bool newIsLinethrough = false;
bool newIsOverline = false;
std::string newFontName;
RichText::ParagraphStyleData currentPStyle;
uint8_t get_new_decoration_value();
void set_styles_at_selection(TextBoxCanvasComponent& a);
void add_undo_if_selecting_area(TextBoxCanvasComponent& a, const std::function<void()>& func);
void add_undo(const std::function<void()>& func);
void hold_undo_data(const std::string& undoName, TextBoxCanvasComponent& a);
void release_undo_data(const std::string& undoName);
std::unordered_map<std::string, std::pair<RichText::TextBox::Cursor, RichText::TextData>> undoHeldData;
RichText::TextStyleModifier::ModifierMap currentMods;
std::vector<std::string> sortedFontList;
TextBoxEditToolAllData get_all_data(const TextBoxCanvasComponent& a);
};

View file

@ -0,0 +1,109 @@
#include "EllipseDrawTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include "Helpers/MathExtras.hpp"
#include "../../InputManager.hpp"
#include <cereal/types/vector.hpp>
#include "../../CanvasComponents/EllipseCanvasComponent.hpp"
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
EllipseDrawTool::EllipseDrawTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType EllipseDrawTool::get_type() {
return DrawingProgramToolType::ELLIPSE;
}
void EllipseDrawTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("ellipse draw tool");
t.gui.text_label_centered("Draw Ellipse");
if(t.gui.radio_button_field("fillonly", "Fill only", fillStrokeMode == 0)) fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2;
if(fillStrokeMode == 1 || fillStrokeMode == 2)
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.pop_id();
}
void EllipseDrawTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
if(objInfoBeingEdited == erasedComp) {
objInfoBeingEdited = nullptr;
drawStage = 0;
}
}
bool EllipseDrawTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
void EllipseDrawTool::switch_tool(DrawingProgramToolType newTool) {
commit();
}
void EllipseDrawTool::tool_update() {
switch(drawStage) {
case 0: {
if(drawP.controls.leftClick) {
CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::ELLIPSE);
EllipseCanvasComponent& newEllipse = static_cast<EllipseCanvasComponent&>(newContainer->get_comp());
startAt = drawP.world.main.input.mouse.pos;
newEllipse.d.strokeColor = drawP.controls.foregroundColor;
newEllipse.d.fillColor = drawP.controls.backgroundColor;
newEllipse.d.strokeWidth = drawP.controls.relativeWidth;
newEllipse.d.p1 = startAt;
newEllipse.d.p2 = startAt;
newEllipse.d.p2 = ensure_points_have_distance(newEllipse.d.p1, newEllipse.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
newEllipse.d.fillStrokeMode = static_cast<uint8_t>(fillStrokeMode);
newContainer->coords = drawP.world.drawData.cam.c;
objInfoBeingEdited = drawP.components->push_back_and_send_create(drawP.components, newContainer);
drawStage = 1;
}
break;
}
case 1: {
if(drawP.controls.leftClickHeld) {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(startAt.y() - newPos.y());
newPos.x() = startAt.x() + (((newPos.x() - startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}
EllipseCanvasComponent& ellipse = static_cast<EllipseCanvasComponent&>(containerPtr->get_comp());
ellipse.d.p1 = cwise_vec_min(startAt, newPos);
ellipse.d.p2 = cwise_vec_max(startAt, newPos);
ellipse.d.p2 = ensure_points_have_distance(ellipse.d.p1, ellipse.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
containerPtr->send_comp_update(drawP, false);
containerPtr->commit_update(drawP);
}
else {
commit();
drawStage = 0;
}
break;
}
}
}
bool EllipseDrawTool::prevent_undo_or_redo() {
return (drawStage == 1);
}
void EllipseDrawTool::commit() {
if(objInfoBeingEdited) {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
containerPtr->commit_update(drawP);
containerPtr->send_comp_update(drawP, true);
objInfoBeingEdited = nullptr;
}
}
void EllipseDrawTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include <Helpers/SCollision.hpp>
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
class EllipseDrawTool : public DrawingProgramToolBase {
public:
EllipseDrawTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
private:
void commit();
Vector2f startAt;
int fillStrokeMode = 1;
int drawStage = 0;
CanvasComponentContainer::ObjInfoSharedPtr objInfoBeingEdited;
};

View file

@ -0,0 +1,100 @@
#include "EraserTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include <include/core/SkPathBuilder.h>
EraserTool::EraserTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType EraserTool::get_type() {
return DrawingProgramToolType::ERASER;
}
void EraserTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("eraser tool");
t.gui.text_label_centered("Eraser");
t.gui.slider_scalar_field("relwidth", "Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.pop_id();
}
bool EraserTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
void EraserTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
erasedComponents.erase(erasedComp);
}
void EraserTool::switch_tool(DrawingProgramToolType newTool) {
DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes);
if(!erasedComponents.empty()) {
drawP.erase_component_set(erasedComponents);
// NOTE: Ideally, you would just recalculate the lastDrawnPos of every cache instead of invalidating and redrawing the caches
// However, the eraser might have other visual glitches during drawing that i am unaware of, so for now, just refresh the
// cache after a draw
drawP.compCache.clear_own_cached_surfaces();
}
erasedComponents.clear();
erasedBVHNodes.clear();
}
void EraserTool::tool_update() {
if(drawP.controls.cursorHoveringOverCanvas)
drawP.world.main.input.hideCursor = true;
if(drawP.controls.leftClickHeld) {
const Vector2f& prevMousePos = drawP.controls.leftClick ? drawP.world.main.input.mouse.pos : drawP.world.main.input.mouse.lastPos;
SCollision::ColliderCollection<float> cC;
SCollision::generate_wide_line(cC, prevMousePos, drawP.world.main.input.mouse.pos, drawP.controls.relativeWidth * 2.0f, true);
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) {
if(bvhNode &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right())) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) {
erasedBVHNodes.emplace(bvhNode);
drawP.compCache.invalidate_cache_at_aabb_before_pos(bvhNode->bounds, 0);
return true;
}
std::erase_if(comps, [&](auto& c) {
if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
erasedComponents.emplace(c);
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
return true;
}
return false;
});
return false;
});
}
else
switch_tool(get_type());
}
bool EraserTool::prevent_undo_or_redo() {
return drawP.controls.leftClickHeld;
}
void EraserTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(!drawData.main->toolbar.io->hoverObstructed) {
SkColor4f c = drawP.world.canvasTheme.get_tool_front_color();
c.fA = 0.5f;
SkPaint linePaint;
linePaint.setColor4f(c);
linePaint.setStyle(SkPaint::kStroke_Style);
linePaint.setStrokeCap(SkPaint::kRound_Cap);
linePaint.setStrokeWidth(drawP.controls.relativeWidth * 2.0f);
SkPathBuilder erasePath;
erasePath.moveTo(convert_vec2<SkPoint>(drawData.main->input.mouse.lastPos));
erasePath.lineTo(convert_vec2<SkPoint>(drawData.main->input.mouse.pos));
canvas->drawPath(erasePath.detach(), linePaint);
}
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <include/core/SkCanvas.h>
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include "../DrawingProgramCache.hpp"
class DrawingProgram;
class EraserTool : public DrawingProgramToolBase {
public:
EraserTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> erasedComponents; // Pointers will be erased from this set if theyre erased in the main list (done by callback)
std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> erasedBVHNodes;
};

View file

@ -0,0 +1,106 @@
#include "EyeDropperTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include <include/core/SkAlphaType.h>
#include <include/core/SkCanvas.h>
#include <include/core/SkColor.h>
#include <include/core/SkColorType.h>
#include <include/core/SkRRect.h>
#include <include/core/SkRect.h>
#include <include/core/SkColorSpace.h>
#include <include/core/SkStream.h>
#include <include/core/SkSurface.h>
#ifdef USE_SKIA_BACKEND_GRAPHITE
#include <include/gpu/graphite/Surface.h>
#elif USE_SKIA_BACKEND_GANESH
#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#endif
#include <Helpers/Logger.hpp>
EyeDropperTool::EyeDropperTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{
}
DrawingProgramToolType EyeDropperTool::get_type() {
return DrawingProgramToolType::EYEDROPPER;
}
void EyeDropperTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("Color select tool");
t.gui.text_label_centered("Color Select");
if(t.gui.radio_button_field("Stroke Color", "Stroke Color", selectingStrokeColor)) selectingStrokeColor = true;
if(t.gui.radio_button_field("Fill Color", "Fill Color", !selectingStrokeColor)) selectingStrokeColor = false;
t.gui.pop_id();
}
bool EyeDropperTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
void EyeDropperTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
}
void EyeDropperTool::tool_update() {
if(drawP.controls.leftClick) {
SkSurfaceProps props;
#ifdef USE_BACKEND_OPENGLES_3_0
SkImageInfo imgInfo = SkImageInfo::Make(drawP.world.main.window.size.x(), drawP.world.main.window.size.y(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
#else
SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(drawP.world.main.window.size.x(), drawP.world.main.window.size.y());
#endif
#ifdef USE_SKIA_BACKEND_GRAPHITE
sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, &props);
#elif USE_SKIA_BACKEND_GANESH
sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, 0, &props);
#endif
if(!surface) {
Logger::get().log("INFO", "[SkSurfaces::WrapBackendRenderTarget] Eye Dropper Tool could not make surface");
return;
}
SkCanvas* eyeDropperCanvas = surface->getCanvas();
if(!eyeDropperCanvas) {
Logger::get().log("INFO", "Eye Dropper Tool could not make canvas");
return;
}
int xPos = std::clamp<int>(drawP.world.main.input.mouse.pos.x(), 0, drawP.world.main.window.size.x() - 1);
int yPos = std::clamp<int>(drawP.world.main.input.mouse.pos.y(), 0, drawP.world.main.window.size.y() - 1);
eyeDropperCanvas->save();
drawP.world.drawData.dontUseDrawProgCache = true;
drawP.world.main.draw(eyeDropperCanvas);
drawP.world.drawData.dontUseDrawProgCache = false;
eyeDropperCanvas->restore();
//drawP.world.main.window.ctx->flush();
Vector4<uint8_t> readData;
SkImageInfo readDataInfo = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
#ifdef USE_SKIA_BACKEND_GRAPHITE
// NOT WORKING IN GRAPHITE YET
//surface->makeImageSnapshot()->readPixels(drawP.world.main.window.ctx.get(), readDataInfo, (void*)readData.data(), 4, xPos, yPos);
#elif USE_SKIA_BACKEND_GANESH
surface->makeImageSnapshot()->readPixels(drawP.world.main.window.ctx.get(), readDataInfo, (void*)readData.data(), 4, xPos, yPos);
#endif
if(selectingStrokeColor)
drawP.controls.foregroundColor = readData.cast<float>() / 255.0f;
else
drawP.controls.backgroundColor = readData.cast<float>() / 255.0f;
}
}
bool EyeDropperTool::prevent_undo_or_redo() {
return false;
}
void EyeDropperTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}
void EyeDropperTool::switch_tool(DrawingProgramToolType newTool) {
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
class DrawingProgram;
class EyeDropperTool : public DrawingProgramToolBase {
public:
EyeDropperTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void tool_update() override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
private:
bool selectingStrokeColor = true;
};

View file

@ -0,0 +1,194 @@
#include "GridModifyTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "Helpers/Networking/NetLibrary.hpp"
#include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
#include <Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp>
#include <cstddef>
using namespace NetworkingObjects;
GridModifyTool::GridModifyTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{
}
void GridModifyTool::set_grid(const NetObjWeakPtr<WorldGrid>& newGrid) {
grid = newGrid;
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
if(gLock) {
WorldGrid& g = *gLock;
oldGrid = g;
CoordSpaceHelper newCam;
newCam.inverseScale = g.size / WorldScalar(WorldGrid::GRID_UNIT_PIXEL_SIZE);
newCam.pos = g.offset - drawP.world.main.window.size.cast<WorldScalar>() * newCam.inverseScale * WorldScalar(0.5);
newCam.rotation = 0.0;
drawP.world.drawData.cam.smooth_move_to(drawP.world, newCam, drawP.world.main.window.size.cast<float>());
}
}
DrawingProgramToolType GridModifyTool::get_type() {
return DrawingProgramToolType::GRIDMODIFY;
}
void GridModifyTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
}
void GridModifyTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("Grid modify tool");
t.gui.text_label_centered("Edit Grid");
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
if(gLock) {
WorldGrid& g = *gLock;
t.gui.input_text_field("grid name", "Name", &g.name);
t.gui.checkbox_field("Visible", "Visible", &g.visible);
t.gui.checkbox_field("Display in Front", "Display in front of canvas", &g.displayInFront);
size_t typeSelected = static_cast<size_t>(g.gridType);
std::vector<std::string> listOfGridTypes = {
"Circle Points",
"Square Points",
"Square Lines",
"Horizontal Lines"
};
t.gui.left_to_right_line_layout([&]() {
t.gui.text_label("Type");
t.gui.dropdown_select("filepicker select type", &typeSelected, listOfGridTypes, 200.0f);
});
g.gridType = static_cast<WorldGrid::GridType>(typeSelected);
uint32_t sDiv = g.subdivisions;
t.gui.input_scalar_field<uint32_t>("Subdivisions", "Subdivisions", &sDiv, 1, 10);
g.set_subdivisions(sDiv);
bool divOut = g.removeDivisionsOutwards;
t.gui.checkbox_field("Subdivide outwards", "Subdivide when zooming out", &divOut);
g.set_remove_divisions_outwards(divOut);
t.gui.left_to_right_line_layout([&]() {
if(t.gui.color_button_big("Grid Color", &g.color, &g.color == t.colorRight))
t.color_selector_right(&g.color == t.colorRight ? nullptr : &g.color);
t.gui.text_label("Grid Color");
});
bool bounded = g.bounds.has_value();
bool prevBoundedValue = bounded;
t.gui.checkbox_field("Bounded", "Bounded", &bounded);
if(bounded && !prevBoundedValue) {
SCollision::AABB<WorldScalar> newBounds;
newBounds.min = g.offset - drawP.world.drawData.cam.c.dir_from_space(drawP.world.main.window.size.cast<float>() * 0.3f);
newBounds.max = g.offset + drawP.world.drawData.cam.c.dir_from_space(drawP.world.main.window.size.cast<float>() * 0.3f);
g.bounds = newBounds;
}
else if(!bounded && prevBoundedValue)
g.bounds = std::nullopt;
t.gui.checkbox_field("Show Coordinates", "Show Coordinates (visible\nwhen canvas isn't rotated)", &g.showCoordinates);
}
t.gui.pop_id();
}
bool GridModifyTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
void GridModifyTool::tool_update() {
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
if(gLock) {
WorldGrid& g = *gLock;
switch(selectionMode) {
case 0:
if(drawP.controls.leftClick) {
Vector2f gOffsetScreenPos = drawP.world.drawData.cam.c.to_space(g.offset);
Vector2f gSizeScreenPos = drawP.world.drawData.cam.c.to_space(g.offset + WorldVec{g.size, 0});
if(SCollision::collide(SCollision::Circle(gOffsetScreenPos, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos))
selectionMode = 1;
else if(SCollision::collide(SCollision::Circle(gSizeScreenPos, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos))
selectionMode = 2;
else if(g.bounds.has_value()) {
const auto& b = g.bounds.value();
Vector2f bMin = drawP.world.drawData.cam.c.to_space(b.min);
Vector2f bMax = drawP.world.drawData.cam.c.to_space(b.max);
if(SCollision::collide(SCollision::Circle(bMin, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos))
selectionMode = 3;
else if(SCollision::collide(SCollision::Circle(bMax, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos))
selectionMode = 4;
}
}
break;
case 1:
if(drawP.controls.leftClickHeld)
g.offset = drawP.world.get_mouse_world_pos();
else
selectionMode = 0;
break;
case 2:
if(drawP.controls.leftClickHeld)
g.size = std::max(FixedPoint::abs(drawP.world.get_mouse_world_pos().x() - g.offset.x()), WorldScalar(1));
else
selectionMode = 0;
break;
case 3:
if(drawP.controls.leftClickHeld && g.bounds.has_value()) {
auto& b = g.bounds.value();
b.min = cwise_vec_min(drawP.world.get_mouse_world_pos(), b.max);
}
else
selectionMode = 0;
break;
case 4:
if(drawP.controls.leftClickHeld && g.bounds.has_value()) {
auto& b = g.bounds.value();
b.max = cwise_vec_max(drawP.world.get_mouse_world_pos(), b.min);
}
else
selectionMode = 0;
break;
}
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, false);
}
else
selectionMode = 0;
}
bool GridModifyTool::prevent_undo_or_redo() {
return false;
}
void GridModifyTool::draw(SkCanvas* canvas, const DrawData& drawData) {
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
if(gLock) {
WorldGrid& g = *gLock;
if(g.bounds.has_value()) {
const auto& b = g.bounds.value();
Vector2f bMin = drawP.world.drawData.cam.c.to_space(b.min);
Vector2f bMax = drawP.world.drawData.cam.c.to_space(b.max);
SkPaint rectBoundsPaint;
rectBoundsPaint.setColor4f(drawP.world.canvasTheme.get_tool_front_color());
rectBoundsPaint.setStroke(true);
rectBoundsPaint.setStrokeWidth(0.0f);
canvas->drawRect(SkRect::MakeLTRB(bMin.x(), bMin.y(), bMax.x(), bMax.y()), rectBoundsPaint);
if(selectionMode == 0 || selectionMode == 4)
drawP.draw_drag_circle(canvas, bMax, {0.9f, 0.5f, 0.1f, 1.0f}, drawData);
if(selectionMode == 0 || selectionMode == 3)
drawP.draw_drag_circle(canvas, bMin, {0.9f, 0.5f, 0.1f, 1.0f}, drawData);
}
if(selectionMode == 0 || selectionMode == 2) {
Vector2f gSizeScreenPos;
if(drawP.world.get_mouse_world_pos().x() < g.offset.x() && selectionMode == 2)
gSizeScreenPos = drawData.cam.c.to_space(g.offset - WorldVec{g.size, 0});
else
gSizeScreenPos = drawData.cam.c.to_space(g.offset + WorldVec{g.size, 0});
drawP.draw_drag_circle(canvas, gSizeScreenPos, {0.1f, 0.9f, 0.9f, 1.0f}, drawData);
}
if(selectionMode == 0 || selectionMode == 1 || selectionMode == 3 || selectionMode == 4) {
Vector2f gOffset = drawData.cam.c.to_space(g.offset);
drawP.draw_drag_circle(canvas, gOffset, {1.0f, 0.27f, 0.27f, 1.0f}, drawData);
}
}
}
void GridModifyTool::switch_tool(DrawingProgramToolType newTool) {
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
if(gLock)
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, true);
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include "../../WorldGrid.hpp"
#include "Helpers/NetworkingObjects/NetObjWeakPtr.hpp"
class DrawingProgram;
class GridModifyTool : public DrawingProgramToolBase {
public:
GridModifyTool(DrawingProgram& initDrawP);
void set_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& newGrid);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void tool_update() override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
private:
WorldGrid oldGrid;
NetworkingObjects::NetObjWeakPtr<WorldGrid> grid;
unsigned selectionMode = 0;
};

View file

@ -0,0 +1,131 @@
#include "LassoSelectTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include "Helpers/MathExtras.hpp"
#include "Helpers/SCollision.hpp"
#include "../../CoordSpaceHelper.hpp"
#include <ranges>
#include "../../DrawCollision.hpp"
#include <earcut.hpp>
#include <include/core/SkPathBuilder.h>
namespace mapbox {
namespace util {
template <>
struct nth<0, Vector2f> {
inline static auto get(const Vector2f &t) {
return t.x();
};
};
template <>
struct nth<1, Vector2f> {
inline static auto get(const Vector2f &t) {
return t.y();
};
};
} // namespace util
} // namespace mapbox
LassoSelectTool::LassoSelectTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType LassoSelectTool::get_type() {
return DrawingProgramToolType::LASSOSELECT;
}
void LassoSelectTool::gui_toolbox() {
drawP.world.main.toolbar.gui.text_label_centered("Lasso Select");
}
bool LassoSelectTool::right_click_popup_gui(Vector2f popupPos) {
return drawP.selection_action_menu(popupPos);
}
void LassoSelectTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
}
void LassoSelectTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
}
void LassoSelectTool::tool_update() {
switch(controls.selectionMode) {
case 0: {
if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
controls = LassoSelectControls();
controls.coords = drawP.world.drawData.cam.c;
controls.lassoPoints.emplace_back(controls.coords.get_mouse_pos(drawP.world));
controls.selectionMode = 1;
}
break;
}
case 1: {
using namespace SCollision;
Vector2f newLassoPoint = controls.coords.get_mouse_pos(drawP.world);
float lassoPointDist = vec_distance(controls.lassoPoints.back(), newLassoPoint);
if(lassoPointDist > 4.0f)
controls.lassoPoints.emplace_back(newLassoPoint);
if(!drawP.controls.leftClickHeld) {
if(controls.lassoPoints.size() > 3) {
SCollision::ColliderCollection<float> cC;
std::vector<std::vector<Vector2f>> polygon;
polygon.emplace_back(controls.lassoPoints);
auto& poly = polygon[0];
std::vector<unsigned> indices = mapbox::earcut(polygon);
for(size_t i = 0; i < indices.size(); i += 3) {
cC.triangle.emplace_back(
poly[indices[i]],
poly[indices[i + 1]],
poly[indices[i + 2]]
);
}
cC.recalculate_bounds();
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
}
}
else if(!drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.deselect_all();
controls.selectionMode = 0;
}
break;
}
}
}
bool LassoSelectTool::prevent_undo_or_redo() {
return drawP.selection.is_something_selected() || controls.selectionMode == 1;
}
void LassoSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(controls.selectionMode == 1) {
canvas->save();
controls.coords.transform_sk_canvas(canvas, drawData);
SkPathBuilder lassoPath;
lassoPath.moveTo(convert_vec2<SkPoint>(controls.lassoPoints.front()));
for(Vector2f& p : controls.lassoPoints | std::views::drop(1))
lassoPath.lineTo(convert_vec2<SkPoint>(p));
canvas->drawPath(lassoPath.detach(), drawP.select_tool_line_paint());
canvas->restore();
}
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <include/core/SkCanvas.h>
#include "../../DrawData.hpp"
#include <Helpers/SCollision.hpp>
#include "../../CoordSpaceHelper.hpp"
#include "DrawingProgramToolBase.hpp"
class DrawingProgram;
class LassoSelectTool : public DrawingProgramToolBase {
public:
LassoSelectTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual bool prevent_undo_or_redo() override;
private:
struct LassoSelectControls {
int selectionMode = 0;
CoordSpaceHelper coords;
std::vector<Vector2f> lassoPoints;
} controls;
};

View file

@ -0,0 +1,44 @@
#include "PanCanvasTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include <Helpers/Logger.hpp>
PanCanvasTool::PanCanvasTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{
}
DrawingProgramToolType PanCanvasTool::get_type() {
return DrawingProgramToolType::PAN;
}
void PanCanvasTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("Pan canvas tool");
t.gui.text_label_centered("Pan tool");
t.gui.pop_id();
}
bool PanCanvasTool::right_click_popup_gui(Vector2f popupPos) {
return drawP.selection_action_menu(popupPos);
}
void PanCanvasTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
}
void PanCanvasTool::tool_update() {
}
bool PanCanvasTool::prevent_undo_or_redo() {
return false;
}
void PanCanvasTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}
void PanCanvasTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
class DrawingProgram;
class PanCanvasTool : public DrawingProgramToolBase {
public:
PanCanvasTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
};

View file

@ -0,0 +1,108 @@
#include "RectDrawTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include "../../CanvasComponents/RectangleCanvasComponent.hpp"
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
RectDrawTool::RectDrawTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType RectDrawTool::get_type() {
return DrawingProgramToolType::RECTANGLE;
}
void RectDrawTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("rect draw tool");
t.gui.text_label_centered("Draw Rectangle");
t.gui.slider_scalar_field("relradiuswidth", "Corner Radius", &relativeRadiusWidth, 0.0f, 40.0f);
if(t.gui.radio_button_field("fillonly", "Fill only", fillStrokeMode == 0)) fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2;
if(fillStrokeMode == 1 || fillStrokeMode == 2)
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.pop_id();
}
void RectDrawTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
if(objInfoBeingEdited == erasedComp) {
objInfoBeingEdited = nullptr;
drawStage = 0;
}
}
bool RectDrawTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
void RectDrawTool::switch_tool(DrawingProgramToolType newTool) {
commit();
drawStage = 0;
}
void RectDrawTool::tool_update() {
switch(drawStage) {
case 0: {
if(drawP.controls.leftClick) {
CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::RECTANGLE);
RectangleCanvasComponent& newRectangle = static_cast<RectangleCanvasComponent&>(newContainer->get_comp());
startAt = drawP.world.main.input.mouse.pos;
newRectangle.d.strokeColor = drawP.controls.foregroundColor;
newRectangle.d.fillColor = drawP.controls.backgroundColor;
newRectangle.d.cornerRadius = relativeRadiusWidth;
newRectangle.d.strokeWidth = drawP.controls.relativeWidth;
newRectangle.d.p1 = startAt;
newRectangle.d.p2 = startAt;
newRectangle.d.p2 = ensure_points_have_distance(newRectangle.d.p1, newRectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
newRectangle.d.fillStrokeMode = static_cast<uint8_t>(fillStrokeMode);
newContainer->coords = drawP.world.drawData.cam.c;
objInfoBeingEdited = drawP.components->push_back_and_send_create(drawP.components, newContainer);
drawStage = 1;
}
break;
}
case 1: {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
if(drawP.controls.leftClickHeld) {
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(startAt.y() - newPos.y());
newPos.x() = startAt.x() + (((newPos.x() - startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}
RectangleCanvasComponent& rectangle = static_cast<RectangleCanvasComponent&>(containerPtr->get_comp());
rectangle.d.p1 = cwise_vec_min(startAt, newPos);
rectangle.d.p2 = cwise_vec_max(startAt, newPos);
rectangle.d.p2 = ensure_points_have_distance(rectangle.d.p1, rectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
containerPtr->send_comp_update(drawP, false);
containerPtr->commit_update(drawP);
}
else {
commit();
drawStage = 0;
}
break;
}
}
}
bool RectDrawTool::prevent_undo_or_redo() {
return (drawStage == 1);
}
void RectDrawTool::commit() {
if(objInfoBeingEdited) {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
containerPtr->commit_update(drawP);
containerPtr->send_comp_update(drawP, true);
objInfoBeingEdited = nullptr;
}
}
void RectDrawTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include <Helpers/SCollision.hpp>
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
class RectDrawTool : public DrawingProgramToolBase {
public:
RectDrawTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
private:
void commit();
Vector2f startAt;
float relativeRadiusWidth = 10.0f;
int fillStrokeMode = 1;
int drawStage = 0;
CanvasComponentContainer::ObjInfoSharedPtr objInfoBeingEdited;
};

View file

@ -0,0 +1,89 @@
#include "RectSelectTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include "Helpers/MathExtras.hpp"
#include "Helpers/SCollision.hpp"
#include "../../CoordSpaceHelper.hpp"
RectSelectTool::RectSelectTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{
}
DrawingProgramToolType RectSelectTool::get_type() {
return DrawingProgramToolType::RECTSELECT;
}
void RectSelectTool::gui_toolbox() {
drawP.world.main.toolbar.gui.text_label_centered("Rectangle Select");
}
void RectSelectTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
}
bool RectSelectTool::right_click_popup_gui(Vector2f popupPos) {
return drawP.selection_action_menu(popupPos);
}
void RectSelectTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
}
void RectSelectTool::tool_update() {
switch(controls.selectionMode) {
case 0: {
if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
controls = RectSelectControls();
controls.coords = drawP.world.drawData.cam.c;
controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world);
controls.selectionMode = 1;
}
break;
}
case 1: {
using namespace SCollision;
Vector2f mPos = controls.coords.get_mouse_pos(drawP.world);
Vector2f newP1 = cwise_vec_min(controls.selectStartAt, mPos);
Vector2f newP2 = cwise_vec_max(controls.selectStartAt, mPos);
controls.newT = triangle_from_rect_points(newP1, newP2);
if(!drawP.controls.leftClickHeld) {
ColliderCollection<float> cC;
cC.triangle.emplace_back(controls.newT[0], controls.newT[1], controls.newT[2]);
cC.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]);
cC.recalculate_bounds();
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
}
controls.selectionMode = 0;
}
break;
}
}
}
bool RectSelectTool::prevent_undo_or_redo() {
return drawP.selection.is_something_selected() || controls.selectionMode == 1;
}
void RectSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(controls.selectionMode == 1) {
canvas->save();
controls.coords.transform_sk_canvas(canvas, drawData);
SkRect r = SkRect::MakeLTRB(controls.newT[0].x(), controls.newT[0].y(), controls.newT[2].x(), controls.newT[2].y());
canvas->drawRect(r, drawP.select_tool_line_paint());
canvas->restore();
canvas->restore();
}
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include <Helpers/SCollision.hpp>
#include "../../CoordSpaceHelper.hpp"
class DrawingProgram;
class RectSelectTool : public DrawingProgramToolBase {
public:
RectSelectTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void tool_update() override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual bool prevent_undo_or_redo() override;
private:
struct RectSelectControls {
int selectionMode = 0;
CoordSpaceHelper coords;
Vector2f selectStartAt;
std::array<Vector2f, 4> newT;
} controls;
};

View file

@ -0,0 +1,526 @@
#include "ScreenshotTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include "Helpers/SCollision.hpp"
#include <include/core/SkAlphaType.h>
#include <include/core/SkColorType.h>
#include <include/core/SkPaint.h>
#include <include/core/SkPathTypes.h>
#include <include/core/SkStream.h>
#include <include/core/SkSurface.h>
#include <include/core/SkImage.h>
#include <include/core/SkData.h>
#include <include/core/SkPathBuilder.h>
#include <include/svg/SkSVGCanvas.h>
#include <include/encode/SkPngEncoder.h>
#include <include/encode/SkWebpEncoder.h>
#include <include/encode/SkJpegEncoder.h>
#include <fstream>
#include <include/gpu/GpuTypes.h>
#include "../../FileHelpers.hpp"
#include <filesystem>
#ifdef USE_SKIA_BACKEND_GRAPHITE
#include <include/gpu/graphite/Surface.h>
#elif USE_SKIA_BACKEND_GANESH
#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#endif
#include "../../GridManager.hpp"
#include <Helpers/Logger.hpp>
#include "../../MainProgram.hpp"
#ifdef __EMSCRIPTEN__
#include <EmscriptenHelpers/emscripten_browser_file.h>
#endif
#define SECTION_SIZE 4000
#define AA_LEVEL 4
ScreenshotTool::ScreenshotTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType ScreenshotTool::get_type() {
return DrawingProgramToolType::SCREENSHOT;
}
void ScreenshotTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("screenshot tool");
t.gui.text_label_centered("Screenshot");
auto oldImgSize = controls.imageSize;
if(controls.selectionMode == 0)
t.gui.text_label("Select an area on the canvas...");
if(controls.selectionMode != 0 && controls.selectedType != SCREENSHOT_SVG)
t.gui.input_scalar_fields("Image Size", "Image Size", &controls.imageSize, 2, 0, 999999999);
if(controls.selectionMode == 2) {
t.gui.left_to_right_line_layout([&]() {
t.gui.text_label("Image Type");
t.gui.dropdown_select("image type select", &controls.selectedType, controls.typeSelections);
});
if(controls.selectedType != SCREENSHOT_SVG)
t.gui.checkbox_field("Display Grid", "Display Grid", &controls.displayGrid);
if(controls.selectedType != 0)
t.gui.checkbox_field("Transparent Background", "Transparent Background", &controls.transparentBackground);
if(controls.imageSize.x() != oldImgSize.x()) {
controls.setDimensionSize = controls.imageSize.x();
controls.setDimensionIsX = true;
controls.imageSize.y() = controls.imageSize.x() * (controls.rectY2 - controls.rectY1) / (controls.rectX2 - controls.rectX1);
}
else if(controls.imageSize.y() != oldImgSize.y()) {
controls.setDimensionSize = controls.imageSize.y();
controls.setDimensionIsX = false;
controls.imageSize.x() = controls.imageSize.y() * (controls.rectX2 - controls.rectX1) / (controls.rectY2 - controls.rectY1);
}
if(t.gui.text_button_wide("Take Screenshot", "Take Screenshot")) {
controls.selectionMode = 0;
#ifdef __EMSCRIPTEN__
take_screenshot("a" + controls.typeSelections[controls.selectedType]);
#else
// We can't actually use the extension from the callback, so we have to set the extension of choice beforehand
Toolbar::ExtensionFilter setExtensionFilter;
switch(controls.selectedType) {
case SCREENSHOT_JPG:
setExtensionFilter = {"JPEG", "jpg;jpeg"};
break;
case SCREENSHOT_PNG:
setExtensionFilter = {"PNG", "png"};
break;
case SCREENSHOT_WEBP:
setExtensionFilter = {"WEBP", "webp"};
break;
case SCREENSHOT_SVG:
setExtensionFilter = {"SVG", "svg"};
break;
}
t.open_file_selector("Export", {setExtensionFilter}, [setExtensionFilter, w = make_weak_ptr(drawP.world.main.world)](const std::filesystem::path& p, const auto& e) {
auto world = w.lock();
if(world && world->drawProg.drawTool->get_type() == DrawingProgramToolType::SCREENSHOT) {
ScreenshotTool* screenshotTool = static_cast<ScreenshotTool*>(world->drawProg.drawTool.get());
screenshotTool->controls.screenshotSavePath = force_extension_on_path(p, setExtensionFilter.extensions);
screenshotTool->controls.setToTakeScreenshot = true;
}
}, "screenshot", true);
#endif
}
}
t.gui.pop_id();
}
bool ScreenshotTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
void ScreenshotTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
}
void ScreenshotTool::take_screenshot(const std::filesystem::path& filePath) {
if(controls.imageSize.x() <= 0 || controls.imageSize.y() <= 0) {
std::cout << "[ScreenshotTool::take_screenshot] Image size is 0 or negative" << std::endl;
return;
}
std::string ext = filePath.extension().string();
size_t extType = SCREENSHOT_JPG;
if(ext == ".jpg" || ext == ".jpeg")
extType = SCREENSHOT_JPG;
else if(ext == ".png")
extType = SCREENSHOT_PNG;
else if(ext == ".webp")
extType = SCREENSHOT_WEBP;
else if(ext == ".svg")
extType = SCREENSHOT_SVG;
if(extType != SCREENSHOT_SVG) {
size_t imageByteSize = (size_t)controls.imageSize.x() * (size_t)controls.imageSize.y() * 4;
size_t imageRowSize = 4 * controls.imageSize.x();
SkImageInfo finalImgInfo = SkImageInfo::Make(controls.imageSize.x(), controls.imageSize.y(), kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
std::vector<uint8_t> finalImgRawData(imageByteSize);
SkPixmap finalImgData(finalImgInfo, finalImgRawData.data(), imageRowSize);
#ifdef USE_BACKEND_OPENGLES_3_0
SkImageInfo imgInfo = SkImageInfo::Make(drawP.world.main.window.size.x(), drawP.world.main.window.size.y(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
#else
SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(drawP.world.main.window.size.x(), drawP.world.main.window.size.y());
#endif
#ifdef USE_SKIA_BACKEND_GRAPHITE
sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, &drawP.world.main.window.defaultMSAASurfaceProps);
#elif USE_SKIA_BACKEND_GANESH
sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, drawP.world.main.window.defaultMSAASampleCount, &drawP.world.main.window.defaultMSAASurfaceProps);
#endif
if(!surface) {
Logger::get().log("INFO", "[SkSurfaces::WrapBackendRenderTarget] Screenshot Tool could not make surface");
return;
}
SkCanvas* screenshotCanvas = surface->getCanvas();
if(!screenshotCanvas) {
Logger::get().log("INFO", "Screenshot Tool could not make canvas");
return;
}
for(int i = 0; i < controls.imageSize.x(); i += drawP.world.main.window.size.x())
for(int j = 0; j < controls.imageSize.y(); j += drawP.world.main.window.size.y())
take_screenshot_area_hw(surface, screenshotCanvas, finalImgRawData.data(), controls.imageSize, Vector2i{i, j}, Vector2i{std::min(drawP.world.main.window.size.x(), controls.imageSize.x() - i), std::min(drawP.world.main.window.size.y(), controls.imageSize.y() - j)}, drawP.world.main.window.size, extType != 0 && controls.transparentBackground);
bool success = false;
SkDynamicMemoryWStream out;
switch(extType) {
case SCREENSHOT_JPG:
success = SkJpegEncoder::Encode(&out, finalImgData, {});
break;
case SCREENSHOT_PNG:
success = SkPngEncoder::Encode(&out, finalImgData, {});
break;
case SCREENSHOT_WEBP:
success = SkWebpEncoder::Encode(&out, finalImgData, {});
break;
default:
break;
}
if(!success) {
Logger::get().log("WORLDFATAL", "[ScreenshotTool::take_screenshot] Could not encode and write screenshot");
return;
}
out.flush();
#ifndef __EMSCRIPTEN__
try {
std::ofstream fi(filePath, std::ios::out | std::ios::binary);
auto skData = out.detachAsData();
fi.write((const char*)skData->bytes(), skData->size());
fi.close();
}
catch(const std::exception& e) {
Logger::get().log("WORLDFATAL", std::string("[ScreenshotTool::take_screenshot] Save screenshot error: ") + e.what());
}
#else
if(success) {
std::string mimeTypeArray[] = {"image/jpeg", "image/png", "image/webp"};
auto skData = out.detachAsData();
emscripten_browser_file::download(
"screenshot" + controls.typeSelections[extType],
mimeTypeArray[extType],
std::string_view((const char*)skData->bytes(), skData->size())
);
}
#endif
}
else {
SkDynamicMemoryWStream out;
Vector2f canvasSize{controls.rectX2 - controls.rectX1, controls.rectY2 - controls.rectY1};
SkRect canvasBounds = SkRect::MakeLTRB(0.0f, 0.0f, canvasSize.x(), canvasSize.y());
std::unique_ptr<SkCanvas> canvas = SkSVGCanvas::Make(canvasBounds, &out, SkSVGCanvas::kConvertTextToPaths_Flag | SkSVGCanvas::kNoPrettyXML_Flag);
take_screenshot_svg(canvas.get(), controls.transparentBackground);
canvas = nullptr; // Ensure that SVG is completely written into the stream
out.flush();
#ifndef __EMSCRIPTEN__
try {
std::ofstream fi(filePath, std::ios::out | std::ios::binary);
auto skData = out.detachAsData();
fi.write((const char*)skData->bytes(), skData->size());
fi.close();
}
catch(const std::exception& e) {
Logger::get().log("WORLDFATAL", std::string("[ScreenshotTool::take_screenshot] Save screenshot error: ") + e.what());
}
#else
auto skData = out.detachAsData();
emscripten_browser_file::download(
"screenshot.svg",
"image/svg+xml",
std::string_view((const char*)skData->bytes(), skData->size())
);
#endif
}
}
void ScreenshotTool::take_screenshot_svg(SkCanvas* canvas, bool transparentBackground) {
float secRectX1 = controls.rectX1;
float secRectX2 = controls.rectX2;
float secRectY1 = controls.rectY1;
float secRectY2 = controls.rectY2;
Vector2f canvasSize{controls.rectX2 - controls.rectX1, controls.rectY2 - controls.rectY1};
DrawData d = drawP.world.drawData;
WorldVec topLeft = controls.coords.from_space({secRectX1, secRectY1});
WorldVec topRight = controls.coords.from_space({secRectX2, secRectY1});
WorldVec bottomLeft = controls.coords.from_space({secRectX1, secRectY2});
WorldVec bottomRight = controls.coords.from_space({secRectX2, secRectY2});
WorldVec camCenter = (topLeft + bottomRight) / WorldScalar(2);
WorldVec vectorZoom;
WorldScalar distX = (camCenter - (topLeft + bottomLeft) * WorldScalar(0.5)).norm();
WorldScalar distY = (camCenter - (topLeft + topRight) * WorldScalar(0.5)).norm();
vectorZoom.x() = distX / WorldScalar(canvasSize.x() * 0.5);
vectorZoom.y() = distY / WorldScalar(canvasSize.y() * 0.5);
WorldScalar newInverseScale = (vectorZoom.x() + vectorZoom.y()) * WorldScalar(0.5);
drawP.world.drawData.cam.set_based_on_properties(drawP.world, topLeft, newInverseScale, controls.coords.rotation);
drawP.world.drawData.cam.set_viewing_area(canvasSize);
drawP.world.drawData.refresh_draw_optimizing_values();
bool oldDrawGrids = drawP.world.drawData.drawGrids;
drawP.world.main.takingScreenshot = true;
drawP.world.main.transparentBackground = transparentBackground;
drawP.world.drawData.drawGrids = false;
drawP.world.drawData.dontUseDrawProgCache = true;
drawP.world.main.draw(canvas);
drawP.world.main.takingScreenshot = false;
drawP.world.main.transparentBackground = false;
drawP.world.drawData.drawGrids = oldDrawGrids;
drawP.world.drawData = d;
}
void ScreenshotTool::take_screenshot_area_hw(const sk_sp<SkSurface>& surface, SkCanvas* canvas, void* fullImgRawData, const Vector2i& fullImageSize, const Vector2i& sectionImagePos, const Vector2i& sectionImageSize, const Vector2i& canvasSize, bool transparentBackground) {
//std::cout << "[ScreenshotTool::take_screenshot_area_hw] Screenshot Pos: " << "[" << sectionImagePos.x() << " " << sectionImagePos.y() << "]" << std::endl;
float secRectX1 = controls.rectX1 + (controls.rectX2 - controls.rectX1) * (sectionImagePos.x() / (double)fullImageSize.x());
float secRectX2 = controls.rectX1 + (controls.rectX2 - controls.rectX1) * ((sectionImagePos.x() + canvasSize.x()) / (double)fullImageSize.x());
float secRectY1 = controls.rectY1 + (controls.rectY2 - controls.rectY1) * (sectionImagePos.y() / (double)fullImageSize.y());
float secRectY2 = controls.rectY1 + (controls.rectY2 - controls.rectY1) * ((sectionImagePos.y() + canvasSize.y()) / (double)fullImageSize.y());
DrawData d = drawP.world.drawData;
WorldVec topLeft = controls.coords.from_space({secRectX1, secRectY1});
WorldVec topRight = controls.coords.from_space({secRectX2, secRectY1});
WorldVec bottomLeft = controls.coords.from_space({secRectX1, secRectY2});
WorldVec bottomRight = controls.coords.from_space({secRectX2, secRectY2});
WorldVec camCenter = (topLeft + bottomRight) / WorldScalar(2);
WorldVec vectorZoom;
WorldScalar distX = (camCenter - (topLeft + bottomLeft) * WorldScalar(0.5)).norm();
WorldScalar distY = (camCenter - (topLeft + topRight) * WorldScalar(0.5)).norm();
vectorZoom.x() = distX / WorldScalar(canvasSize.x() * 0.5);
vectorZoom.y() = distY / WorldScalar(canvasSize.y() * 0.5);
WorldScalar newInverseScale = (vectorZoom.x() + vectorZoom.y()) * WorldScalar(0.5);
drawP.world.drawData.cam.set_based_on_properties(drawP.world, topLeft, newInverseScale, controls.coords.rotation);
drawP.world.drawData.cam.set_viewing_area(sectionImageSize.cast<float>());
drawP.world.drawData.refresh_draw_optimizing_values();
bool oldDrawGrids = drawP.world.drawData.drawGrids;
drawP.world.main.takingScreenshot = true;
drawP.world.main.transparentBackground = transparentBackground;
drawP.world.drawData.drawGrids = controls.displayGrid;
drawP.world.drawData.dontUseDrawProgCache = true;
drawP.world.main.draw(canvas);
drawP.world.main.takingScreenshot = false;
drawP.world.main.transparentBackground = false;
drawP.world.drawData.drawGrids = oldDrawGrids;
drawP.world.drawData = d;
SkImageInfo aaImgInfo = SkImageInfo::Make(sectionImageSize.x(), sectionImageSize.y(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
void* fullImgRawDataStartPt = (uint8_t*)fullImgRawData + 4 * (size_t)sectionImagePos.x() + 4 * (size_t)fullImageSize.x() * (size_t)sectionImagePos.y();
SkPixmap aaImgData(aaImgInfo, fullImgRawDataStartPt, fullImageSize.x() * 4);
if(!surface->readPixels(aaImgData, 0, 0))
throw std::runtime_error("[ScreenshotTool::take_screenshot_area_hw] Error copy pixmap");
}
void ScreenshotTool::switch_tool(DrawingProgramToolType newTool) {
controls.selectionMode = 0;
}
void ScreenshotTool::tool_update() {
if(controls.setToTakeScreenshot) {
controls.setToTakeScreenshot = false;
take_screenshot(controls.screenshotSavePath);
}
switch(controls.selectionMode) {
case 0: {
if(drawP.controls.leftClick) {
controls.rectX1 = controls.rectX2 = drawP.world.main.input.mouse.pos.x();
controls.rectY1 = controls.rectY2 = drawP.world.main.input.mouse.pos.y();
controls.coords = drawP.world.drawData.cam.c;
controls.dragType = 0;
controls.selectionMode = 1;
}
break;
}
case 1: {
Vector2f curPos = controls.coords.get_mouse_pos(drawP.world);
float oldX1 = controls.rectX1;
float oldX2 = controls.rectX2;
float oldY1 = controls.rectY1;
float oldY2 = controls.rectY2;
switch(controls.dragType) {
case 0:
controls.rectX2 = curPos.x();
controls.rectY2 = curPos.y();
break;
case 1:
controls.rectY2 = curPos.y();
break;
case 2:
controls.rectX1 = curPos.x();
controls.rectY2 = curPos.y();
break;
case 3:
controls.rectX1 = curPos.x();
break;
case 4:
controls.rectX1 = curPos.x();
controls.rectY1 = curPos.y();
break;
case 5:
controls.rectY1 = curPos.y();
break;
case 6:
controls.rectX2 = curPos.x();
controls.rectY1 = curPos.y();
break;
case 7:
controls.rectX2 = curPos.x();
break;
}
if(std::fabs(controls.rectX1 - controls.rectX2) > 1e10 || std::fabs(controls.rectY1 - controls.rectY2) > 1e10) { // Limit before screenshot tool starts breaking
controls.rectX1 = oldX1;
controls.rectX2 = oldX2;
controls.rectY1 = oldY1;
controls.rectY2 = oldY2;
}
if((drawP.world.drawData.cam.c.inverseScale << 8) < controls.coords.inverseScale || (drawP.world.drawData.cam.c.inverseScale >> 15) > controls.coords.inverseScale) {
controls.selectionMode = 0;
break;
}
float tempX1 = std::min(controls.rectX1, controls.rectX2);
float tempX2 = std::max(controls.rectX1, controls.rectX2);
float tempY1 = std::min(controls.rectY1, controls.rectY2);
float tempY2 = std::max(controls.rectY1, controls.rectY2);
if(controls.setDimensionIsX) {
controls.imageSize.x() = controls.setDimensionSize;
controls.imageSize.y() = controls.imageSize.x() * (tempY2 - tempY1) / (tempX2 - tempX1);
}
else {
controls.imageSize.y() = controls.setDimensionSize;
controls.imageSize.x() = controls.imageSize.y() * (tempX2 - tempX1) / (tempY2 - tempY1);
}
if(!drawP.controls.leftClickHeld) {
controls.selectionMode = 2;
commit_rect();
}
break;
}
case 2: {
commit_rect();
controls.dragType = -1;
if((drawP.world.drawData.cam.c.inverseScale << 8) < controls.coords.inverseScale || (drawP.world.drawData.cam.c.inverseScale >> 15) > controls.coords.inverseScale) {
controls.selectionMode = 0;
break;
}
if(drawP.controls.cursorHoveringOverCanvas && drawP.controls.leftClick) {
for(int i = 0; i < 8; i++)
if(SCollision::collide(controls.circles[i], drawP.world.main.input.mouse.pos))
controls.dragType = i;
if(controls.dragType == -1) {
Vector2f mousePosScreenshotCoords = controls.coords.get_mouse_pos(drawP.world);
if(SCollision::collide(mousePosScreenshotCoords, SCollision::AABB<float>({controls.rectX1, controls.rectY1}, {controls.rectX2, controls.rectY2})))
controls.dragType = -2;
}
if(controls.dragType == -1)
controls.selectionMode = 0;
else if(controls.dragType == -2) {
controls.translateBeginPos = drawP.world.get_mouse_world_pos();
controls.translateBeginCoords = controls.coords;
controls.selectionMode = 3;
}
else
controls.selectionMode = 1;
}
break;
}
case 3: {
controls.coords = controls.translateBeginCoords;
controls.coords.translate(drawP.world.get_mouse_world_pos() - controls.translateBeginPos);
if((drawP.world.drawData.cam.c.inverseScale << 8) < controls.coords.inverseScale || (drawP.world.drawData.cam.c.inverseScale >> 15) > controls.coords.inverseScale) {
controls.selectionMode = 0;
break;
}
if(!drawP.controls.leftClickHeld) {
controls.selectionMode = 2;
commit_rect();
}
break;
}
}
}
void ScreenshotTool::commit_rect() {
float x1 = controls.rectX1;
float x2 = controls.rectX2;
float y1 = controls.rectY1;
float y2 = controls.rectY2;
controls.rectX1 = std::min(x1, x2);
controls.rectX2 = std::max(x1, x2);
controls.rectY1 = std::min(y1, y2);
controls.rectY2 = std::max(y1, y2);
controls.circles[0] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({controls.rectX2 , controls.rectY2})), drawP.drag_point_radius()};
controls.circles[1] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({(controls.rectX1 + controls.rectX2) * 0.5, controls.rectY2})), drawP.drag_point_radius()};
controls.circles[2] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({controls.rectX1 , controls.rectY2})), drawP.drag_point_radius()};
controls.circles[3] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({controls.rectX1, (controls.rectY1 + controls.rectY2) * 0.5})), drawP.drag_point_radius()};
controls.circles[4] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({controls.rectX1 , controls.rectY1})), drawP.drag_point_radius()};
controls.circles[5] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({(controls.rectX1 + controls.rectX2) * 0.5, controls.rectY1})), drawP.drag_point_radius()};
controls.circles[6] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({controls.rectX2 , controls.rectY1})), drawP.drag_point_radius()};
controls.circles[7] = {drawP.world.drawData.cam.c.to_space(controls.coords.from_space({controls.rectX2, (controls.rectY1 + controls.rectY2) * 0.5})), drawP.drag_point_radius()};
}
bool ScreenshotTool::prevent_undo_or_redo() {
return false;
}
void ScreenshotTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(!drawData.main->takingScreenshot) {
if(controls.selectionMode == 0) {
SkPaint paint1;
paint1.setColor4f({0.0f, 0.0f, 0.0f, 0.3f});
canvas->drawPaint(paint1);
}
else {
canvas->save();
controls.coords.transform_sk_canvas(canvas, drawData);
float x1 = std::min(controls.rectX1, controls.rectX2);
float x2 = std::max(controls.rectX1, controls.rectX2);
float y1 = std::min(controls.rectY1, controls.rectY2);
float y2 = std::max(controls.rectY1, controls.rectY2);
SkPoint p1{x1, y1};
SkPoint p2{x2, y2};
SkPathBuilder path1B;
path1B.addRect(SkRect::MakeLTRB(p1.x(), p1.y(), p2.x(), p2.y()));
path1B.setFillType(SkPathFillType::kInverseWinding);
SkPath path1 = path1B.detach();
SkPaint paint1;
paint1.setColor4f({0.0f, 0.0f, 0.0f, 0.3f});
canvas->drawPath(path1, paint1);
path1.setFillType(SkPathFillType::kWinding);
SkPaint paint2;
paint2.setColor4f({1.0f, 1.0f, 1.0f, 0.9f});
paint2.setStyle(SkPaint::kStroke_Style);
canvas->drawPath(path1, paint2);
canvas->restore();
if(controls.selectionMode == 2) {
paint2.setStyle(SkPaint::kFill_Style);
for(auto& circ : controls.circles)
drawP.draw_drag_circle(canvas, circ.pos, {0.1f, 0.9f, 0.9f, 1.0f}, drawData, 1.0f);
}
}
}
}

View file

@ -0,0 +1,70 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include <Helpers/SCollision.hpp>
#include <filesystem>
#include "../../InputManager.hpp"
#include "../../CoordSpaceHelper.hpp"
class DrawingProgram;
class ScreenshotTool : public DrawingProgramToolBase {
public:
ScreenshotTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual bool prevent_undo_or_redo() override;
private:
void commit_rect();
void take_screenshot(const std::filesystem::path& filePath);
void take_screenshot_area_hw(const sk_sp<SkSurface>& surface, SkCanvas* canvas, void* fullImgRawData, const Vector2i& fullImageSize, const Vector2i& sectionImagePos, const Vector2i& sectionImageSize, const Vector2i& canvasSize, bool transparentBackground);
void take_screenshot_svg(SkCanvas* canvas, bool transparentBackground);
//static constexpr std::array<InputManager::SystemCursorType, 8> cursors{
// InputManager::SystemCursorType::SE_RESIZE,
// InputManager::SystemCursorType::S_RESIZE,
// InputManager::SystemCursorType::SW_RESIZE,
// InputManager::SystemCursorType::W_RESIZE,
// InputManager::SystemCursorType::NW_RESIZE,
// InputManager::SystemCursorType::N_RESIZE,
// InputManager::SystemCursorType::NE_RESIZE,
// InputManager::SystemCursorType::E_RESIZE
//};
enum ScreenshotType : size_t {
SCREENSHOT_JPG,
SCREENSHOT_PNG,
SCREENSHOT_WEBP,
SCREENSHOT_SVG
};
struct ScreenshotControls {
CoordSpaceHelper translateBeginCoords;
WorldVec translateBeginPos;
CoordSpaceHelper coords;
float rectX1;
float rectX2;
float rectY1;
float rectY2;
int selectionMode = 0;
int dragType = 0;
Vector2i imageSize = {0, 0};
int setDimensionSize = 1000;
bool setDimensionIsX = true;
std::array<SCollision::Circle<float>, 8> circles;
bool displayGrid = true;
bool transparentBackground = false;
size_t selectedType = SCREENSHOT_JPG;
std::vector<std::string> typeSelections = {".jpg", ".png", ".webp", ".svg"};
std::atomic<bool> setToTakeScreenshot = false;
std::filesystem::path screenshotSavePath;
} controls;
};

View file

@ -0,0 +1,108 @@
#include "TextBoxTool.hpp"
#include <chrono>
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "Helpers/MathExtras.hpp"
#include "Helpers/SCollision.hpp"
#include <cereal/types/vector.hpp>
#include <memory>
#include "../../CanvasComponents/TextBoxCanvasComponent.hpp"
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
#include "EditTool.hpp"
TextBoxTool::TextBoxTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{}
DrawingProgramToolType TextBoxTool::get_type() {
return DrawingProgramToolType::TEXTBOX;
}
void TextBoxTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("textbox tool");
t.gui.text_label_centered("Textbox");
t.gui.pop_id();
}
void TextBoxTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
if(objInfoBeingEdited == erasedComp) {
objInfoBeingEdited = nullptr;
drawStage = 0;
}
}
bool TextBoxTool::right_click_popup_gui(Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
void TextBoxTool::switch_tool(DrawingProgramToolType newTool) {
commit();
drawStage = 0;
}
void TextBoxTool::tool_update() {
switch(drawStage) {
case 0: {
if(drawP.controls.leftClick) {
startAt = drawP.world.main.input.mouse.pos;
endAt = startAt;
CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::TEXTBOX);
TextBoxCanvasComponent& newTextBox = static_cast<TextBoxCanvasComponent&>(newContainer->get_comp());
newTextBox.d.p1 = newTextBox.d.p2 = startAt;
newTextBox.d.p2 = ensure_points_have_distance(newTextBox.d.p1, newTextBox.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
newTextBox.d.editing = true;
newContainer->coords = drawP.world.drawData.cam.c;
objInfoBeingEdited = drawP.components->push_back_and_send_create(drawP.components, newContainer);
drawStage = 1;
}
break;
}
case 1: {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
TextBoxCanvasComponent& textBox = static_cast<TextBoxCanvasComponent&>(containerPtr->get_comp());
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(startAt.y() - newPos.y());
newPos.x() = startAt.x() + (((newPos.x() - startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}
endAt = newPos;
textBox.d.p1 = cwise_vec_min(endAt, startAt);
textBox.d.p2 = cwise_vec_max(endAt, startAt);
textBox.d.p2 = ensure_points_have_distance(textBox.d.p1, textBox.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
if(!drawP.controls.leftClickHeld) {
auto editTool = std::make_unique<EditTool>(drawP);
editTool->edit_start(objInfoBeingEdited);
drawP.toolToSwitchToAfterUpdate = std::move(editTool);
return;
}
else {
containerPtr->send_comp_update(drawP, false);
containerPtr->commit_update(drawP);
}
break;
}
}
}
bool TextBoxTool::prevent_undo_or_redo() {
return (drawStage == 1);
}
void TextBoxTool::commit() {
if(objInfoBeingEdited) {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
containerPtr->commit_update(drawP);
containerPtr->send_comp_update(drawP, true);
objInfoBeingEdited = nullptr;
}
}
void TextBoxTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include <Helpers/SCollision.hpp>
#include "../../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
class TextBoxTool : public DrawingProgramToolBase {
public:
TextBoxTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual bool prevent_undo_or_redo() override;
private:
void commit();
Vector2f startAt;
Vector2f endAt;
int drawStage = 0;
CanvasComponentContainer::ObjInfoSharedPtr objInfoBeingEdited;
};

View file

@ -0,0 +1,44 @@
#include "ZoomCanvasTool.hpp"
#include "../DrawingProgram.hpp"
#include "../../MainProgram.hpp"
#include "../../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include <Helpers/Logger.hpp>
ZoomCanvasTool::ZoomCanvasTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
{
}
DrawingProgramToolType ZoomCanvasTool::get_type() {
return DrawingProgramToolType::ZOOM;
}
void ZoomCanvasTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("Zoom canvas tool");
t.gui.text_label_centered("Zoom tool");
t.gui.pop_id();
}
bool ZoomCanvasTool::right_click_popup_gui(Vector2f popupPos) {
return drawP.selection_action_menu(popupPos);
}
void ZoomCanvasTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
}
void ZoomCanvasTool::tool_update() {
}
bool ZoomCanvasTool::prevent_undo_or_redo() {
return false;
}
void ZoomCanvasTool::draw(SkCanvas* canvas, const DrawData& drawData) {
}
void ZoomCanvasTool::switch_tool(DrawingProgramToolType newTool) {
if(!drawP.is_selection_allowing_tool(newTool))
drawP.selection.deselect_all();
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
class DrawingProgram;
class ZoomCanvasTool : public DrawingProgramToolBase {
public:
ZoomCanvasTool(DrawingProgram& initDrawP);
virtual DrawingProgramToolType get_type() override;
virtual void gui_toolbox() override;
virtual bool right_click_popup_gui(Vector2f popupPos) override;
virtual void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) override;
virtual void tool_update() override;
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
};