Put drawing program tools code in folder
This commit is contained in:
parent
702df53794
commit
783bb7dacb
45 changed files with 133 additions and 139 deletions
199
src/DrawingProgram/Tools/BrushTool.cpp
Normal file
199
src/DrawingProgram/Tools/BrushTool.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
46
src/DrawingProgram/Tools/BrushTool.hpp
Normal file
46
src/DrawingProgram/Tools/BrushTool.hpp
Normal 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};
|
||||
};
|
||||
53
src/DrawingProgram/Tools/DrawingProgramToolBase.cpp
Normal file
53
src/DrawingProgram/Tools/DrawingProgramToolBase.cpp
Normal 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;
|
||||
}
|
||||
41
src/DrawingProgram/Tools/DrawingProgramToolBase.hpp
Normal file
41
src/DrawingProgram/Tools/DrawingProgramToolBase.hpp
Normal 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;
|
||||
};
|
||||
194
src/DrawingProgram/Tools/EditTool.cpp
Normal file
194
src/DrawingProgram/Tools/EditTool.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/DrawingProgram/Tools/EditTool.hpp
Normal file
38
src/DrawingProgram/Tools/EditTool.hpp
Normal 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;
|
||||
};
|
||||
|
|
@ -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() {}
|
||||
|
|
@ -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;
|
||||
};
|
||||
58
src/DrawingProgram/Tools/EditTools/EllipseDrawEditTool.cpp
Normal file
58
src/DrawingProgram/Tools/EditTools/EllipseDrawEditTool.cpp
Normal 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;
|
||||
}
|
||||
21
src/DrawingProgram/Tools/EditTools/EllipseDrawEditTool.hpp
Normal file
21
src/DrawingProgram/Tools/EditTools/EllipseDrawEditTool.hpp
Normal 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;
|
||||
};
|
||||
59
src/DrawingProgram/Tools/EditTools/ImageEditTool.cpp
Normal file
59
src/DrawingProgram/Tools/EditTools/ImageEditTool.cpp
Normal 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;
|
||||
}
|
||||
14
src/DrawingProgram/Tools/EditTools/ImageEditTool.hpp
Normal file
14
src/DrawingProgram/Tools/EditTools/ImageEditTool.hpp
Normal 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;
|
||||
};
|
||||
55
src/DrawingProgram/Tools/EditTools/RectDrawEditTool.cpp
Normal file
55
src/DrawingProgram/Tools/EditTools/RectDrawEditTool.cpp
Normal 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;
|
||||
}
|
||||
18
src/DrawingProgram/Tools/EditTools/RectDrawEditTool.hpp
Normal file
18
src/DrawingProgram/Tools/EditTools/RectDrawEditTool.hpp
Normal 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;
|
||||
};
|
||||
300
src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp
Normal file
300
src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp
Normal 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;
|
||||
}
|
||||
50
src/DrawingProgram/Tools/EditTools/TextBoxEditTool.hpp
Normal file
50
src/DrawingProgram/Tools/EditTools/TextBoxEditTool.hpp
Normal 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);
|
||||
};
|
||||
109
src/DrawingProgram/Tools/EllipseDrawTool.cpp
Normal file
109
src/DrawingProgram/Tools/EllipseDrawTool.cpp
Normal 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) {
|
||||
}
|
||||
27
src/DrawingProgram/Tools/EllipseDrawTool.hpp
Normal file
27
src/DrawingProgram/Tools/EllipseDrawTool.hpp
Normal 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;
|
||||
};
|
||||
100
src/DrawingProgram/Tools/EraserTool.cpp
Normal file
100
src/DrawingProgram/Tools/EraserTool.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
23
src/DrawingProgram/Tools/EraserTool.hpp
Normal file
23
src/DrawingProgram/Tools/EraserTool.hpp
Normal 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;
|
||||
};
|
||||
106
src/DrawingProgram/Tools/EyeDropperTool.cpp
Normal file
106
src/DrawingProgram/Tools/EyeDropperTool.cpp
Normal 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) {
|
||||
}
|
||||
19
src/DrawingProgram/Tools/EyeDropperTool.hpp
Normal file
19
src/DrawingProgram/Tools/EyeDropperTool.hpp
Normal 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;
|
||||
};
|
||||
194
src/DrawingProgram/Tools/GridModifyTool.cpp
Normal file
194
src/DrawingProgram/Tools/GridModifyTool.cpp
Normal 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);
|
||||
}
|
||||
24
src/DrawingProgram/Tools/GridModifyTool.hpp
Normal file
24
src/DrawingProgram/Tools/GridModifyTool.hpp
Normal 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;
|
||||
};
|
||||
131
src/DrawingProgram/Tools/LassoSelectTool.cpp
Normal file
131
src/DrawingProgram/Tools/LassoSelectTool.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
30
src/DrawingProgram/Tools/LassoSelectTool.hpp
Normal file
30
src/DrawingProgram/Tools/LassoSelectTool.hpp
Normal 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;
|
||||
};
|
||||
44
src/DrawingProgram/Tools/PanCanvasTool.cpp
Normal file
44
src/DrawingProgram/Tools/PanCanvasTool.cpp
Normal 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();
|
||||
}
|
||||
17
src/DrawingProgram/Tools/PanCanvasTool.hpp
Normal file
17
src/DrawingProgram/Tools/PanCanvasTool.hpp
Normal 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;
|
||||
};
|
||||
108
src/DrawingProgram/Tools/RectDrawTool.cpp
Normal file
108
src/DrawingProgram/Tools/RectDrawTool.cpp
Normal 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) {
|
||||
}
|
||||
27
src/DrawingProgram/Tools/RectDrawTool.hpp
Normal file
27
src/DrawingProgram/Tools/RectDrawTool.hpp
Normal 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;
|
||||
};
|
||||
89
src/DrawingProgram/Tools/RectSelectTool.cpp
Normal file
89
src/DrawingProgram/Tools/RectSelectTool.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
26
src/DrawingProgram/Tools/RectSelectTool.hpp
Normal file
26
src/DrawingProgram/Tools/RectSelectTool.hpp
Normal 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;
|
||||
};
|
||||
526
src/DrawingProgram/Tools/ScreenshotTool.cpp
Normal file
526
src/DrawingProgram/Tools/ScreenshotTool.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/DrawingProgram/Tools/ScreenshotTool.hpp
Normal file
70
src/DrawingProgram/Tools/ScreenshotTool.hpp
Normal 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;
|
||||
};
|
||||
108
src/DrawingProgram/Tools/TextBoxTool.cpp
Normal file
108
src/DrawingProgram/Tools/TextBoxTool.cpp
Normal 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) {
|
||||
}
|
||||
26
src/DrawingProgram/Tools/TextBoxTool.hpp
Normal file
26
src/DrawingProgram/Tools/TextBoxTool.hpp
Normal 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;
|
||||
};
|
||||
44
src/DrawingProgram/Tools/ZoomCanvasTool.cpp
Normal file
44
src/DrawingProgram/Tools/ZoomCanvasTool.cpp
Normal 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();
|
||||
}
|
||||
17
src/DrawingProgram/Tools/ZoomCanvasTool.hpp
Normal file
17
src/DrawingProgram/Tools/ZoomCanvasTool.hpp
Normal 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;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue