diff --git a/CMakeLists.txt b/CMakeLists.txt index d2919ae..57f58a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,4 +7,6 @@ set(CMAKE_CXX_STANDARD 20) file(GLOB_RECURSE HEADER_FILES project/include/*.h) file(GLOB_RECURSE SOURCE_FILES project/src/*.cpp) +add_subdirectory(project/client) + add_executable(main ${HEADER_FILES} ${SOURCE_FILES}) \ No newline at end of file diff --git a/brush.png b/brush.png new file mode 100644 index 0000000..db8c9ed Binary files /dev/null and b/brush.png differ diff --git a/project/client/CMakeLists.txt b/project/client/CMakeLists.txt new file mode 100644 index 0000000..6fddf04 --- /dev/null +++ b/project/client/CMakeLists.txt @@ -0,0 +1,59 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +find_package(Qt5 COMPONENTS + Core + Gui + Widgets + REQUIRED) + +find_package(Boost 1.40.0 REQUIRED system) +find_package(Boost COMPONENTS log log_setup REQUIRED) +link_directories(${Boost_LIBRARY_DIR}) + +find_package(nlohmann_json 3.2.0 REQUIRED) + +set(WITH_SANITIZER ON) +if (WITH_SANITIZER) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif () + + +set(SOURCES src/MainWindow.cpp src/PaintScene.cpp src/Palette.cpp src/Changer.cpp + src/ToolsPanel.cpp src/ParametersPanel.cpp src/Brush.cpp src/Canvas.cpp + src/Connector.cpp src/ServerConnection.cpp) +set(HEADER_FILES include/Palette.h include/MainWindow.h include/PaintScene.h include/Changer.h + include/ToolsPanel.h include/ParametersPanel.h include/Brush.h include/Canvas.h + include/Connector.h include/ServerConnection.h ${Boost_INCLUDE_DIR}) + +#include_directories(include ${Boost_INCLUDE_DIR}) +#aux_source_directory(src SRC) + +add_executable(client src/main.cpp ${SOURCES} ${HEADER_FILES}) +target_link_libraries(client + Qt5::Core + Qt5::Gui + Qt5::Widgets + boost_thread + pthread + ${BOOST_LIBRARIES} + nlohmann_json::nlohmann_json + ) + +target_include_directories(client PUBLIC include) + +#add_executable(test src/main_window_test.cpp ${SOURCES} ${HEADER_FILES}) +#find_package(GTest) + +#target_link_libraries(test PRIVATE +# GTest::Main +# Qt5::Core +# Qt5::Gui +# Qt5::Widgets +# ) + +#add_test(NAME test COMMAND test) +#target_include_directories(test PUBLIC include) + diff --git a/project/client/include/Brush.h b/project/client/include/Brush.h new file mode 100644 index 0000000..37c254b --- /dev/null +++ b/project/client/include/Brush.h @@ -0,0 +1,28 @@ +// +// Created by eremey on 27.11.2021. +// +#pragma once +#include + +class Brush : public QAction { + Q_OBJECT + +public: + explicit Brush(qreal brushSize = 10, const QColor& brushColor = Qt::red, QWidget *parent = nullptr); + ~Brush() = default; + +signals: + void BrushTriggered(qreal brushSize, const QColor& brushColor); + +public slots: + void SetSizeSlot(int value); + void SetRedSlot(int value); + void SetGreenSlot(int value); + void SetBlueSlot(int value); + void SetOpacitySlot(int value); +private slots: + void BrushTriggeredSlot(); +private: + qreal brushSize; + QColor brushColor; +}; \ No newline at end of file diff --git a/project/client/include/Canvas.h b/project/client/include/Canvas.h new file mode 100644 index 0000000..ffb7284 --- /dev/null +++ b/project/client/include/Canvas.h @@ -0,0 +1,29 @@ +// +// Created by eremey on 01.12.2021. +// +#pragma once +#include +#include +#include +#include + +class Canvas : public QGraphicsView +{ + Q_OBJECT + +public: + explicit Canvas(QWidget *parent = 0); + ~Canvas() = default; + +public slots: + void openImageSlot(); + void saveImageSlot(); + void saveAsImageSlot(); + void closeImageSlot(); + + +private: + QString filePath = nullptr; + + size_t default_width; +}; \ No newline at end of file diff --git a/project/client/include/Changer.h b/project/client/include/Changer.h new file mode 100644 index 0000000..7ad82eb --- /dev/null +++ b/project/client/include/Changer.h @@ -0,0 +1,31 @@ +// +// Created by eremey on 16.11.2021. +// +#pragma once +#include + +#include +#include + +class Changer : public QToolBar +{ + Q_OBJECT + +public: + explicit Changer(QString name = "", int value = 30, int max_value = 255, QWidget *parent = 0); + ~Changer() = default; + +private: + int _value; + + QSpinBox *spin_box; + QSlider *slider; + +public: + +signals: + void valueChanged(int value); + +private slots: + void valueChangedSlot(int value); +}; \ No newline at end of file diff --git a/project/client/include/Connector.h b/project/client/include/Connector.h new file mode 100644 index 0000000..49179bb --- /dev/null +++ b/project/client/include/Connector.h @@ -0,0 +1,50 @@ +// +// Created by eremey on 08.12.2021. +// +#pragma once +#include +#include +#include + +class Connector : public QToolBar { + Q_OBJECT + +public: + explicit Connector(QWidget *parent = nullptr); + ~Connector(); + +signals: + void writeSignal(std::string& message); + +public slots: + void failedShareSlot(); + void successfulShareSlot(std::string& message); + + void failedConnectSlot(); + void successfulConnectSlot(std::string& message); + +private: + QPushButton* connectButton; + QPushButton* shareButton; + QPushButton* canselButton; + //QLabel* message; + + std::string address = ""; + std::string token = ""; + +signals: + void initSignal(); +// void shareSignal(); +// void connectSignal(); +private slots: + void initSlot(); + void connectSlot(); + + void writeConnectionSlot(); + void writeShareSlot(); + + + void addressChangedSlot(const QString &text); + void tokenChangedSlot(const QString &text); + +}; diff --git a/project/client/include/MainWindow.h b/project/client/include/MainWindow.h new file mode 100644 index 0000000..49c480c --- /dev/null +++ b/project/client/include/MainWindow.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include +#include +#include +#include + +#include "PaintScene.h" +#include "ToolsPanel.h" +#include "ParametersPanel.h" +#include "Canvas.h" + +#include "ServerConnection.h" + +#include "Connector.h" + + +class MainWindow : public QMainWindow +{ +Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + PaintScene *scene; + Canvas* canvas; + + QToolBar *parameters_panel; + + ToolsPanel *toolsPanel; + + ParametersPanel *parametersPanel; + + Connector *connector; + + QTimer *timer; + + std::unique_ptr serverConnection_; + std::unique_ptr forDocumentConnection_; + +signals: + void failedShareSignal(); + void successfulShareSignal(std::string& message); + + void failedConnectSignal(); + void successfulConnectSignal(std::string& message); + + void addCurve(std::string& message); + +private: + long temporary_read_position = 0; + +private slots: + void slotBrush(qreal brushSize = 10, const QColor& brushColor = Qt::red); + void slotTimer(); + +// void + +public slots: + void writeSlot(std::string& message); + + +public: + void execute(std::string&& message); + //void executeBrush(const Curve& curve); +}; diff --git a/project/client/include/PaintScene.h b/project/client/include/PaintScene.h new file mode 100644 index 0000000..226ddac --- /dev/null +++ b/project/client/include/PaintScene.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +struct Point { + int32_t i = 0; + int32_t j = 0; +}; + +struct Curve { + int32_t brush_size = 0; + int32_t color_red = 0; + int32_t color_green = 0; + int32_t color_blue = 0; + int32_t opacity = 0; + + std::vector coords; +}; + +class PaintScene : public QGraphicsScene +{ + + Q_OBJECT + +public: + explicit PaintScene(QObject *parent = 0); + ~PaintScene() = default; + + void ChangeBrushStatus(); + bool BrushStatus(); + void SetBrushSize(qreal brush_size = 10); + void SetBrush(qreal brushSize = 10, const QColor& brushColor = Qt::red); + +signals: +// void PushCurve(const Curve& curve); + void writeCurveSignal(std::string& curve); + +public slots: + void SetBrushSizeSlot(int size); + void SetTransparencySlot(int transparency); + void SetRedSlot(int transparency); + void SetGreenSlot(int transparency); + void SetBlueSlot(int transparency); + +// void PaintCurveSlot(const Curve& curve); + + void readCurveSlot(std::string& message); + +private: + bool is_brush = false; + + QPoint previous_point; + + std::vector curve; + + int32_t brush_size = 10; + + QColor brush_color; + +private: + void mousePressEvent(QGraphicsSceneMouseEvent * event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + +}; diff --git a/project/client/include/Palette.h b/project/client/include/Palette.h new file mode 100644 index 0000000..caf3223 --- /dev/null +++ b/project/client/include/Palette.h @@ -0,0 +1,32 @@ +// +// Created by eremey on 16.11.2021. +// +#pragma once +#include +#include +#include + +class Palette : public QLabel +{ + Q_OBJECT + +public: + explicit Palette(QWidget *parent = 0); + ~Palette() = default; + + void SetColor(int red, int green, int blue, int opacity = 255); + + //void SetGreen(int value); +private: + QColor color; + //QPixmap *pixmap; + +public slots: + void SetRed(int value); + void SetGreen(int value); + void SetBlue(int value); + +private slots: + //void slotTimer(); + //void slotBrush(); +}; \ No newline at end of file diff --git a/project/client/include/ParametersPanel.h b/project/client/include/ParametersPanel.h new file mode 100644 index 0000000..e5a3520 --- /dev/null +++ b/project/client/include/ParametersPanel.h @@ -0,0 +1,37 @@ +// +// Created by eremey on 18.11.2021. +// +#pragma once +#include +#include "Changer.h" +#include "Palette.h" +#include + +class ParametersPanel : public QToolBar { + Q_OBJECT + +public: + explicit ParametersPanel(QWidget *parent = 0); + ~ParametersPanel() = default; + +signals: + void BrushSizeChanged(int newSize); + void BrushRedChanged(int newValue); + void BrushGreenChanged(int newValue); + void BrushBlueChanged(int newValue); + void BrushOpacityChanged(int newValue); +public slots: + void setBrush(qreal brushSize = 10, const QColor& brushColor = Qt::red); + +private: + Changer* changers; + +private slots: + void BrushSizeChangedSlot(int newSize); + void BrushRedChangedSlot(int newValue); + void BrushGreenChangedSlot(int newValue); + void BrushBlueChangedSlot(int newValue); + void BrushOpacityChangedSlot(int newValue); + +}; + diff --git a/project/client/include/ServerConnection.h b/project/client/include/ServerConnection.h new file mode 100644 index 0000000..47b1109 --- /dev/null +++ b/project/client/include/ServerConnection.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_LENGTH 10000 +#define END_STR "\r\n" + +#define SERVER_PORT 8080 + + +/* + * Структура с колбеками, которые будут вызываться после успешного чтения и записи соответственно. + */ +struct ServerConnectionCallbacks { + std::function onReadCb; + std::function onWriteCb; +}; + +class ServerConnection { +public: + ServerConnection(std::string &&url, int port, ServerConnectionCallbacks &&callbacks); + + /* + * Выполняет подключение к серверу и начинает бесконечный цикл чтения сообщений с сервера. + * После того как прочитали сообщение, вызовется onReadCb из ServerConnectionCallbacks. + */ + void start(); + + /** + * Пишет серверу какое-либо сообщение. После записи вызовет onWriteCb, переданный в структуре ServerConnectionCallbacks + * @param message само сообщение + */ + void write(std::string &&message); + + void setServerUrl(std::string &&url); + + void setServerPort(int port); + + std::string getServerUrl() { + return serverUrl_; + } + + int getServerPort() { + return serverPort_; + }; + +private: + void on_resolve(boost::system::error_code err, boost::asio::ip::tcp::resolver::results_type result); + + void on_connect(boost::system::error_code err); + + void read(); + + void connectionToServer(); + + void readHandler(const boost::system::error_code &err, size_t bytes_transferred); + + void writeHandler(const boost::system::error_code &err); + + void run_ioc(); + + std::string serverUrl_; + int serverPort_; + + boost::asio::io_context service_; + boost::asio::ip::tcp::socket socket_; + ServerConnectionCallbacks callbacks_; + + std::mutex writeMutex_; + std::condition_variable writeCv_; + std::atomic canWrite_{true}; + + boost::beast::flat_buffer buffer_{BUFFER_LENGTH}; + boost::beast::http::request request_; + boost::beast::http::response response_; + boost::asio::ip::tcp::resolver resolver_; + +}; + diff --git a/project/client/include/ToolsPanel.h b/project/client/include/ToolsPanel.h new file mode 100644 index 0000000..b746c28 --- /dev/null +++ b/project/client/include/ToolsPanel.h @@ -0,0 +1,33 @@ +// +// Created by eremey on 18.11.2021. +// +#pragma once +#include + +class ToolsPanel : public QToolBar { + Q_OBJECT + +public: + explicit ToolsPanel(QWidget *parent = nullptr); + ~ToolsPanel() = default; + +public slots: + void SetBrushSizeSlot(int value); + void SetBrushRedSlot(int value); + void SetBrushGreenSlot(int value); + void SetBrushBlueSlot(int value); + void SetBrushOpacitySlot(int value); + +private slots: + void BrushTriggeredSlot(qreal brushSize, const QColor& brushColor); +private: +signals: + void BrushTriggered(qreal brushSize, const QColor& brushColor); + + void SetBrushSize(int value); + void SetBrushRed(int value); + void SetBrushGreen(int value); + void SetBrushBlue(int value); + void SetBrushOpacity(int value); +}; + diff --git a/project/client/main.cpp b/project/client/main.cpp new file mode 100644 index 0000000..bc8f460 --- /dev/null +++ b/project/client/main.cpp @@ -0,0 +1,6 @@ +#include + +int main() { + std::cout << "Hello, World!" << std::endl; + return 0; +} diff --git a/project/client/src/Brush.cpp b/project/client/src/Brush.cpp new file mode 100644 index 0000000..f9da2ad --- /dev/null +++ b/project/client/src/Brush.cpp @@ -0,0 +1,36 @@ +// +// Created by eremey on 27.11.2021. +// +#include "Brush.h" +#include +#include + +#include + +Brush::Brush(qreal brushSize, const QColor& brushColor, QWidget *parent) : + brushSize(brushSize), brushColor(brushColor), QAction(parent) { + assert(parent != nullptr); + connect(this, &QAction::triggered, this, &Brush::BrushTriggeredSlot); + + QPixmap brush_pm("brush.png"); + setIcon(QIcon(brush_pm)); +} +void Brush::BrushTriggeredSlot() { + emit(BrushTriggered(brushSize, brushColor)); +} +void Brush::SetSizeSlot(int value) { + brushSize = value; +} + +void Brush::SetRedSlot(int value) { + brushColor.setRed(value); +} +void Brush::SetGreenSlot(int value) { + brushColor.setGreen(value); +} +void Brush::SetBlueSlot(int value) { + brushColor.setBlue(value); +} +void Brush::SetOpacitySlot(int value) { + brushColor.setAlpha(value); +} diff --git a/project/client/src/Canvas.cpp b/project/client/src/Canvas.cpp new file mode 100644 index 0000000..659a2b3 --- /dev/null +++ b/project/client/src/Canvas.cpp @@ -0,0 +1,53 @@ +// +// Created by eremey on 01.12.2021. +// + +#include "Canvas.h" +#include +#include //// + +Canvas::Canvas(QWidget *parent) : + QGraphicsView(parent) { +} + +void Canvas::openImageSlot() { + QString path = QFileDialog::getOpenFileName(this, + tr("Open Image"), ".", tr("Image Files (*.png *.jpg *.bmp)")); + auto *image = new QImage; + image->load(path); + + if(image->height() < height()) { + setFixedHeight(image->height() + 16); // 16 - slider height + } + if(image->width() < width()) { + setFixedWidth(image->width() + 16); + } + + setSceneRect(0, 0, image->width(), image->height()); + scene()->addPixmap(QPixmap::fromImage(*image)); + delete image; +} + +void Canvas::saveImageSlot() { + if (filePath == nullptr) { + filePath = QFileDialog::getSaveFileName(this, + tr("Save Image"), "."); + } + + QImage image(scene()->width(), scene()->height(), QImage::Format_ARGB32_Premultiplied); + QPainter painter(&image); + scene()->render(&painter); + image.save(filePath, "JPG"); +} +void Canvas::saveAsImageSlot() { + filePath = QFileDialog::getSaveFileName(this, + tr("Save Image"), "."); + + QImage image(scene()->width(), scene()->height(), QImage::Format_ARGB32_Premultiplied); + QPainter painter(&image); + scene()->render(&painter); + image.save(filePath, "JPG"); +} +void Canvas::closeImageSlot() { + +} diff --git a/project/client/src/Changer.cpp b/project/client/src/Changer.cpp new file mode 100644 index 0000000..1db0493 --- /dev/null +++ b/project/client/src/Changer.cpp @@ -0,0 +1,46 @@ +// +// Created by eremey on 16.11.2021. +// +#include "Changer.h" + +#include +#include + +#include + +Changer::Changer(QString name, int value, int max_value, QWidget *parent) : + QToolBar(parent) { + + QLabel *size_label = new QLabel(name, this); + this->addWidget(size_label); + size_label->setFixedWidth(name.length() * 9); + + spin_box = new QSpinBox; + spin_box->setValue(value); + spin_box->setMaximum(max_value); + spin_box->setFixedWidth(50); + this->addWidget(spin_box); + + slider = new QSlider(Qt::Horizontal); + slider->setFixedWidth(100); + slider->setValue(value); + slider->setMaximum(max_value); + this->addWidget(slider); + + this->setFixedWidth(size_label->width() + spin_box->width() + slider->width() + 20); + std::cout << size_label->width() << " " << spin_box->width() << " " << slider->width(); + + connect(slider, QOverload::of(&QSlider::valueChanged), this, + (&Changer::valueChangedSlot)); + connect(spin_box, QOverload::of(&QSpinBox::valueChanged), this, + (&Changer::valueChangedSlot)); + + this->setStyleSheet(QString("QToolBar {spacing: %1}").arg(5)); + + +} +void Changer::valueChangedSlot(int value) { + _value = value; + spin_box->value() == value ? slider->setValue(value) : spin_box->setValue(value); + emit valueChanged(value); +} diff --git a/project/client/src/Connector.cpp b/project/client/src/Connector.cpp new file mode 100644 index 0000000..4e41048 --- /dev/null +++ b/project/client/src/Connector.cpp @@ -0,0 +1,149 @@ +// +// Created by eremey on 08.12.2021. +// +#include + +#include "Connector.h" + +#include +#include +#include +#include + +#include "iostream" + +using json = nlohmann::json; + +Connector::Connector(QWidget *parent) : QToolBar(parent) { + connect(this, &Connector::initSignal, this, &Connector::initSlot); +// connect(this, &Connector::connectSignal, this, &Connector::connectSlot); +// connect(this, &Connector::shareSignal, this, &Connector::shareSlot); + emit(initSignal()); +} + +void Connector::initSlot() { + clear(); + shareButton = new QPushButton("Share document", this); + //connect(shareButton, &QPushButton::clicked, this, &Connector::shareSlot); + connect(shareButton, &QPushButton::clicked, this, &Connector::writeShareSlot); + connectButton = new QPushButton("Connect to document", this); + connect(connectButton, &QPushButton::clicked, this, &Connector::connectSlot); + +// connect(this, &Connector::connectSignal, this, &Connector::connectSlot); +// connect(this, &Connector::shareSignal, this, &Connector::shareSlot); + + addWidget(shareButton); + addWidget(connectButton); +} + +void Connector::connectSlot() { + clear(); + auto addressLbl = new QLabel("Address:", this); + addWidget(addressLbl); + auto addressEdt = new QLineEdit(this); + connect(addressEdt, &QLineEdit::textEdited, this, &Connector::addressChangedSlot); + addWidget(addressEdt); + auto tokenLbl = new QLabel("Token:", this); + addWidget(tokenLbl); + auto tokenEdt = new QLineEdit(this); + connect(tokenEdt, &QLineEdit::textEdited, this, &Connector::tokenChangedSlot); + addWidget(tokenEdt); + + canselButton = new QPushButton("Cansel", this); + connect(canselButton, &QPushButton::clicked, this, &Connector::initSlot); + connectButton = new QPushButton("Connect", this); + connect(connectButton, &QPushButton::clicked, this, &Connector::writeConnectionSlot); + + + addSeparator(); + addWidget(connectButton); + addWidget(canselButton); + addSeparator(); +} + +Connector::~Connector() { + clear(); +} + +void Connector::addressChangedSlot(const QString &text) { + address = text.toStdString(); +} + +void Connector::tokenChangedSlot(const QString &text) { + token = text.toStdString(); +} + +void Connector::writeConnectionSlot() { + // TODO: 2) change connection message + json message_json = {{"target", "auth"}, + {"auth_token", token}, + {"address", address}}; + std::string message = message_json.dump(); + emit(writeSignal(message)); +} + +void Connector::writeShareSlot() { + json message_json = {{"target", "sharing_document"}, + {"auth_token", token}, + {"address", address}}; + std::string message = message_json.dump(); + emit(writeSignal(message)); +} + +void Connector::failedShareSlot() { + canselButton = new QPushButton("Cansel", this); + connect(canselButton, &QPushButton::clicked, this, &Connector::initSlot); + auto message = new QLabel(this); + + clear(); + message->setText("failed"); + addWidget(message); + addWidget(canselButton); +} + +void Connector::failedConnectSlot() { + canselButton = new QPushButton("Cansel", this); + connect(canselButton, &QPushButton::clicked, this, &Connector::initSlot); + auto message = new QLabel(this); + + clear(); + message->setText("failed"); + addWidget(message); + addWidget(canselButton); +} + +void separate(const std::string &message, std::string &l, std::string &r, char separator) { + l = message.substr(0, message.find(separator)); + r = message.substr(message.find(separator) + 1, message.size() - 1); +} + +void Connector::successfulShareSlot(std::string &message) { +// std::string separator = "|"; +// address = message.substr(0, message.find(separator)); +// token = message.substr(message.find(separator) + 1, message.size() - 1); + separate(message, address, token, '|'); + + canselButton = new QPushButton("Cansel", this); + connect(canselButton, &QPushButton::clicked, this, &Connector::initSlot); + auto label = new QLabel(this); + + clear(); + QString msg = QString::fromStdString("You're sharing the document.\nAddress: " + address + "\nToken: " + token); + label->setText(msg); + addWidget(label); + addWidget(canselButton); +} + +void Connector::successfulConnectSlot(std::string &message) { + separate(message, address, token, '|'); + + canselButton = new QPushButton("Cansel", this); + connect(canselButton, &QPushButton::clicked, this, &Connector::initSlot); + auto label = new QLabel(this); + + clear(); + QString msg = QString::fromStdString("You've joined the document.\nAddress: " + address + "\nToken: " + token); + label->setText(msg); + addWidget(label); + addWidget(canselButton); +} diff --git a/project/client/src/MainWindow.cpp b/project/client/src/MainWindow.cpp new file mode 100644 index 0000000..1fd0d5e --- /dev/null +++ b/project/client/src/MainWindow.cpp @@ -0,0 +1,232 @@ +#include "MainWindow.h" +#include "Changer.h" +#include "Palette.h" +#include "QGraphicsView" +#include "Connector.h" + +#include +#include +#include +#include +#include +#include +#include + +// Temporary +#include + +#include + +//// +#include +#include +//// + +#include + +#include + +using json = nlohmann::json; + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent) { + + scene = new PaintScene(this); + connect(scene, &PaintScene::writeCurveSignal, this, &MainWindow::writeSlot); + connect(this, &MainWindow::addCurve, scene, &PaintScene::readCurveSlot); + + canvas = new Canvas; + canvas->setScene(scene); + + setCentralWidget(canvas); + + // MenuBar + auto *file_open = new QAction("&Open", this); + connect(file_open, &QAction::triggered, canvas, &Canvas::openImageSlot); + auto *file_close = new QAction("&Close", this); + connect(file_close, &QAction::triggered, canvas, &Canvas::closeImageSlot); + auto *file_save = new QAction("&Save", this); + connect(file_save, &QAction::triggered, canvas, &Canvas::saveImageSlot); + auto *file_save_as = new QAction("&Save as", this); + connect(file_save_as, &QAction::triggered, canvas, &Canvas::saveAsImageSlot); + QMenu *file; + file = menuBar()->addMenu("File"); + file->addAction(file_open); + file->addAction(file_close); + file->addAction(file_save); + file->addAction(file_save_as); + + auto *edit_undo = new QAction("&Undo", this); + auto *edit_redo = new QAction("&Redo", this); + QMenu *edit; + edit = menuBar()->addMenu("Edit"); + edit->addAction(edit_undo); + edit->addAction(edit_redo); + + auto *help_help = new QAction("&Help", this); + QMenu *help; + help = menuBar()->addMenu("Help"); + help->addAction(help_help); + + // ToolBars + parametersPanel = new ParametersPanel(this); + addToolBar(parametersPanel); + parameters_panel = new QToolBar; + addToolBar(parameters_panel); + parameters_panel->setFixedHeight(40); + + connector = new Connector(this); + addToolBar(Qt::RightToolBarArea, connector); + connector->setFixedWidth(400); + + connect(connector, &Connector::writeSignal, this, &MainWindow::writeSlot); + connect(this, &MainWindow::failedShareSignal, connector, &Connector::failedShareSlot); + connect(this, &MainWindow::failedConnectSignal, connector, &Connector::failedConnectSlot); + connect(this, &MainWindow::successfulConnectSignal, connector, &Connector::successfulConnectSlot); + connect(this, &MainWindow::successfulShareSignal, connector, &Connector::successfulShareSlot); + + + toolsPanel = new ToolsPanel; + addToolBar(Qt::LeftToolBarArea, toolsPanel); + + connect(toolsPanel, &ToolsPanel::BrushTriggered, this, &MainWindow::slotBrush); + //connect(this, &MainWindow::addCurve, scene, &PaintScene::PaintCurveSlot); + //connect(scene, &PaintScene::PushCurve, this, &MainWindow::TemporaryWriterSlot); + + + // Status bar + statusBar()->showMessage("Status bar"); + + timer = new QTimer(); + connect(timer, &QTimer::timeout, this, &MainWindow::slotTimer); + timer->start(500); + serverConnection_ = std::make_unique(std::string("127.0.0.1"), 8080, ServerConnectionCallbacks{ + [this](std::string &&message) { this->execute(std::move(message)); }, + {} + }); + + serverConnection_->start(); +} + +MainWindow::~MainWindow() { +} + +void MainWindow::slotTimer() { + scene->setSceneRect(0, 0, canvas->width() - 20, canvas->height() - 20); +} + +void MainWindow::slotBrush(qreal brushSize, const QColor &brushColor) { + + if (scene->BrushStatus()) { + parametersPanel->clear(); + } else { + scene->SetBrush(brushSize, brushColor); + parametersPanel->setBrush(brushSize, brushColor); + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushSizeChanged), scene, &PaintScene::SetBrushSize); + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushSizeChanged), toolsPanel, &ToolsPanel::SetBrushSizeSlot); + + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushRedChanged), scene, &PaintScene::SetRedSlot); + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushRedChanged), toolsPanel, &ToolsPanel::SetBrushRedSlot); + + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushGreenChanged), scene, &PaintScene::SetGreenSlot); + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushGreenChanged), toolsPanel, &ToolsPanel::SetBrushGreenSlot); + + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushBlueChanged), scene, &PaintScene::SetBlueSlot); + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushBlueChanged), toolsPanel, &ToolsPanel::SetBrushBlueSlot); + + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushOpacityChanged), scene, &PaintScene::SetTransparencySlot); + connect(parametersPanel, QOverload::of(&ParametersPanel::BrushOpacityChanged), toolsPanel, &ToolsPanel::SetBrushOpacitySlot); + } + scene->ChangeBrushStatus(); +} + + +//void MainWindow::executeBrush(const Curve& curve) { +// if (curve.brush_size < 0) { +// throw std::invalid_argument("Invalid size"); +// } +// if (curve.color_red < 0 || curve.color_red > 255) { +// throw std::invalid_argument("Invalid red color"); +// } +// if (curve.color_green < 0 || curve.color_green > 255) { +// throw std::invalid_argument("Invalid green color"); +// } +// if (curve.color_blue < 0 || curve.color_blue > 255) { +// throw std::invalid_argument("Invalid blue color"); +// } +// if (curve.coords.size() < 2) { +// throw std::invalid_argument("Invalid curve size"); +// } +// emit(addCurve(curve)); +//} +void MainWindow::execute(std::string &&message) { + std::cout << "MainWindow::execute(), message = " << message << std::endl; + + // TODO: 4) add parser + auto parse_message = json::parse(message); + + // TODO: if sharing failed + if (parse_message.contains("status") && parse_message["target"] == "sharing_document" && parse_message["status"] == "FAIL") { + emit(failedShareSignal()); + } else + + // TODO: if sharing succeeded + if (parse_message.contains("status") && parse_message["target"] == "sharing_document" && parse_message["status"] == "OK") { + std::cout << "// TODO: if sharing succeeded" << std::endl; + forDocumentConnection_ = std::make_unique(std::string("127.0.0.1"), parse_message["port"], ServerConnectionCallbacks{ + [this](std::string &&message) { this->execute(std::move(message)); }, + {} + }); + forDocumentConnection_->start(); + std::string auth_token = to_string(parse_message["address"]) + std::string(":") + to_string(parse_message["port"]) + + to_string(parse_message["auth_token"]); + json to_connect = {{"target", "auth"}, + {"auth_token", parse_message["auth_token"]}}; + forDocumentConnection_->write(to_connect.dump()); + emit(successfulShareSignal(auth_token)); + } else + + // TODO: if joining failed + if (parse_message.contains("status") && parse_message["target"] == "auth" && parse_message["status"] == "FAIL") { + std::cout << "TODO: if joining failed" << std::endl; + emit(failedConnectSignal()); + } else + + // TODO: if joining succeeded + if (parse_message.contains("status") && parse_message["target"] == "auth" && parse_message["status"] == "OK") { + std::string auth_token = to_string(parse_message["address"]) + std::string(":") + to_string(parse_message["port"]) + std::string("|") + + to_string(parse_message["auth_token"]); + std::cout << "// TODO: if joining succeeded" << std::endl; + //emit(successfulConnectSignal(auth_token)); + } else + + // TODO: if need to draw a line + if (parse_message["target"] == "sharing_command") { + std::cout << "TODO: if need to draw a line" << std::endl; + std::string command = parse_message["command"]; + emit(addCurve(command)); + } +} + + +void MainWindow::writeSlot(std::string &message) { + std::string target = json::parse(message)["target"]; + std::cerr << "MainWindow::writeSlot, message = " << message << std::endl; + if (target == "sharing_document" && serverConnection_) { + serverConnection_->write(std::move(message)); + } else if (forDocumentConnection_) { + forDocumentConnection_->write(std::move(message)); + } else { + forDocumentConnection_ = std::make_unique(std::string("127.0.0.1"), 6070, ServerConnectionCallbacks{ + [this](std::string &&message) { this->execute(std::move(message)); }, + {} + }); + forDocumentConnection_->start(); + } +} + +//void MainWindow::resizeEvent(QResizeEvent *event) +//{ +// timer->start(100); +// QWidget::resizeEvent(event); +//} diff --git a/project/client/src/PaintScene.cpp b/project/client/src/PaintScene.cpp new file mode 100644 index 0000000..89510d3 --- /dev/null +++ b/project/client/src/PaintScene.cpp @@ -0,0 +1,124 @@ +#include "PaintScene.h" +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +void PaintScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { + if (is_brush) { + addEllipse(event->scenePos().x() - brush_size / 2, + event->scenePos().y() - brush_size / 2, + brush_size, + brush_size, + QPen(Qt::NoPen), + QBrush(brush_color)); + previous_point = event->scenePos().toPoint(); + curve.push_back(previous_point); + } + +} + +void PaintScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + if (is_brush) { + addLine(previous_point.x(), + previous_point.y(), + event->scenePos().x(), + event->scenePos().y(), + QPen(brush_color, brush_size, Qt::SolidLine, Qt::RoundCap)); + previous_point = event->scenePos().toPoint(); + curve.push_back(previous_point); + } +} + + +void PaintScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + char separator = ' '; + std::string message = std::to_string(brush_size) + separator + + std::to_string(brush_color.red()) + separator + + std::to_string(brush_color.green()) + separator + + std::to_string(brush_color.blue()) + separator + + std::to_string(brush_color.alpha()) + separator; + + + for (auto& point: curve) { + message += std::to_string(point.x()) + separator + std::to_string(point.y()) + separator; + } + + // TODO: 1) message -> command + json command_json = {{"target", "sharing_command"}, {"command", message}}; + std::string command_str = command_json.dump(); + + emit(writeCurveSignal(command_str)); + + curve.clear(); +} +void PaintScene::ChangeBrushStatus() { + is_brush = !is_brush; +} +PaintScene::PaintScene(QObject *parent) { + //connect(this, &PaintScene::writeCurveSignal, this, &PaintScene::readCurveSlot); // + brush_color.setRgb(0,255,0); + +} +void PaintScene::SetBrushSize(qreal brush_size) { + this->brush_size = brush_size; +} +bool PaintScene::BrushStatus() { + return is_brush; +} +void PaintScene::SetBrushSizeSlot(int size) { + brush_size = size; +} +void PaintScene::SetTransparencySlot(int value) { + assert(value < 256 && value >= 0); + brush_color.setAlpha(value); +} +void PaintScene::SetRedSlot(int value) { + assert(value < 256 && value >= 0); + brush_color.setRed(value); +} + +void PaintScene::SetGreenSlot(int value) { + assert(value < 256 && value >= 0); + brush_color.setGreen(value); +} + +void PaintScene::SetBlueSlot(int value) { + assert(value < 256 && value >= 0); + brush_color.setBlue(value); +} +void PaintScene::SetBrush(qreal brushSize, const QColor& brushColor) { + brush_size = brushSize; + brush_color = brushColor; +} + +void PaintScene::readCurveSlot(std::string &message) { + std::stringstream curveStr; + curveStr << message; // bad idea + + Curve brushCurve; + curveStr >> brushCurve.brush_size >> brushCurve.color_red >> brushCurve.color_green >> brushCurve.color_blue >> brushCurve.opacity; + int32_t point_x = 0; + int32_t point_y = 0; + + while (curveStr >> point_x >> point_y){ + brushCurve.coords.emplace_back(point_x, point_y); + } + + assert(brushCurve.coords.size() > 1); + assert(brushCurve.color_red < 256 && brushCurve.color_red >= 0); + assert(brushCurve.color_green < 256 && brushCurve.color_green >= 0); + assert(brushCurve.color_blue < 256 && brushCurve.color_blue >= 0); + + for (int i = 1; i < brushCurve.coords.size(); ++i) { + addLine(brushCurve.coords[i].x(), + brushCurve.coords[i].y() , + brushCurve.coords[i - 1].x(), + brushCurve.coords[i - 1].y(), + QPen(QColor(brushCurve.color_red, brushCurve.color_green, brushCurve.color_blue, brushCurve.opacity), + brushCurve.brush_size, Qt::SolidLine, Qt::RoundCap)); + } +} diff --git a/project/client/src/Palette.cpp b/project/client/src/Palette.cpp new file mode 100644 index 0000000..5daf887 --- /dev/null +++ b/project/client/src/Palette.cpp @@ -0,0 +1,38 @@ +// +// Created by eremey on 16.11.2021. +// +#include "Palette.h" +#include + +Palette::Palette(QWidget *parent) : + QLabel(parent), color(0,0,0,255) { + SetRed(255); +} +void Palette::SetGreen(int value) { + color.setGreen(value); + QPixmap pixmap(15, 15); + pixmap.fill( color ); + + this->setPixmap(pixmap); +} +void Palette::SetRed(int value) { + color.setRed(value); + QPixmap pixmap(15, 15); + pixmap.fill( color ); + + this->setPixmap(pixmap); +} +void Palette::SetBlue(int value) { + color.setBlue(value); + QPixmap pixmap(15, 15); + pixmap.fill( color ); + + this->setPixmap(pixmap); +} +void Palette::SetColor(int red, int green, int blue, int opacity) { + color.setRgb(red, green, blue, opacity); + QPixmap pixmap(15, 15); + pixmap.fill( color ); + + this->setPixmap(pixmap); +} diff --git a/project/client/src/ParametersPanel.cpp b/project/client/src/ParametersPanel.cpp new file mode 100644 index 0000000..e68a67c --- /dev/null +++ b/project/client/src/ParametersPanel.cpp @@ -0,0 +1,69 @@ +// +// Created by eremey on 18.11.2021. +// + +#include "ParametersPanel.h" + +ParametersPanel::ParametersPanel(QWidget *parent) : QToolBar(parent) { + setFixedHeight(40); +}; + +void ParametersPanel::setBrush(qreal brushSize, const QColor& brushColor) { + QWidget *dummy = new QWidget(); + dummy->setFixedWidth(40); + addWidget(dummy); + + Changer *size_changer = new Changer("Size", brushSize, 500, this); + addWidget(size_changer); + connect(size_changer, QOverload::of(&Changer::valueChanged), this, &ParametersPanel::BrushSizeChangedSlot); + + addSeparator(); + + + Palette *palette = new Palette; + palette->SetColor(brushColor.red(), brushColor.green(), brushColor.blue()); + + Changer *red_changer = new Changer("Red", brushColor.red(), 255, this); + connect(red_changer, QOverload::of(&Changer::valueChanged), this, &ParametersPanel::BrushRedChangedSlot); + connect(red_changer, QOverload::of(&Changer::valueChanged), palette, (&Palette::SetRed)); + addWidget(red_changer); + + Changer *green_changer = new Changer("Green", brushColor.green(), 255, this); + connect(green_changer, QOverload::of(&Changer::valueChanged), this, &ParametersPanel::BrushGreenChangedSlot); + connect(green_changer, QOverload::of(&Changer::valueChanged), palette, (&Palette::SetGreen)); + + addWidget(green_changer); + + Changer *blue_changer = new Changer("Blue", brushColor.blue(), 255, this); + connect(blue_changer, QOverload::of(&Changer::valueChanged), this, &ParametersPanel::BrushBlueChangedSlot); + connect(blue_changer, QOverload::of(&Changer::valueChanged), palette, (&Palette::SetBlue)); + + addWidget(blue_changer); + + + // Palette palette; + addWidget(palette); + addSeparator(); + + Changer *opacity_changer = new Changer("Opacity", 100, 100, this); + connect(opacity_changer, QOverload::of(&Changer::valueChanged), this, &ParametersPanel::BrushOpacityChangedSlot); + addWidget(opacity_changer); + + setStyleSheet(QString("QToolBar {spacing: %1}").arg(10)); +} + +void ParametersPanel::BrushSizeChangedSlot(int newSize) { + emit(BrushSizeChanged(newSize)); +} +void ParametersPanel::BrushRedChangedSlot(int newValue) { + emit(BrushRedChanged(newValue)); +} +void ParametersPanel::BrushGreenChangedSlot(int newValue) { + emit(BrushGreenChanged(newValue)); +} +void ParametersPanel::BrushBlueChangedSlot(int newValue) { + emit(BrushBlueChanged(newValue)); +} +void ParametersPanel::BrushOpacityChangedSlot(int newValue) { + emit(BrushOpacityChanged((newValue * 255) / 100)); +} diff --git a/project/client/src/ServerConnection.cpp b/project/client/src/ServerConnection.cpp new file mode 100644 index 0000000..1c08062 --- /dev/null +++ b/project/client/src/ServerConnection.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include "ServerConnection.h" + +ServerConnection::ServerConnection(std::string &&url, int port, ServerConnectionCallbacks &&callbacks) : socket_(service_), + serverUrl_(std::move(url)), + serverPort_(port), + callbacks_(std::move(callbacks)), + resolver_(service_) { +} + +void ServerConnection::on_resolve(boost::system::error_code err, boost::asio::ip::tcp::resolver::results_type result) { + boost::asio::async_connect(socket_, result.begin(), result.end(), + std::bind(&ServerConnection::on_connect, this, std::placeholders::_1)); +} + +void ServerConnection::connectionToServer() { + resolver_.async_resolve(serverUrl_, std::to_string(serverPort_), std::bind(&ServerConnection::on_resolve, this, + std::placeholders::_1, std::placeholders::_2)); +} + +void ServerConnection::on_connect(boost::system::error_code err) { + if (!err) { + read(); + } +} + +void ServerConnection::read() { + boost::beast::http::async_read(socket_, buffer_, response_, [this](const boost::system::error_code err, size_t bytes_transferred) { + this->readHandler(err, bytes_transferred); + }); +} + +void ServerConnection::readHandler(const boost::system::error_code &err, size_t bytes_transferred) { + if (err) { + std::cerr << "ServerConnection::readHandler error" << std::endl; + return; + } + + if (callbacks_.onReadCb) { + callbacks_.onReadCb(response_.body().data()); + } + + response_.clear(); + response_.body().clear(); + + read(); +} + +void ServerConnection::run_ioc() { + std::thread run_thread([this]() { service_.run(); }); + run_thread.detach(); +} + +void ServerConnection::start() { + connectionToServer(); + run_ioc(); +} + +void ServerConnection::write(std::string &&message) { + std::unique_lock ulock(writeMutex_); + writeCv_.wait(ulock, [this]() { return bool(canWrite_); }); + canWrite_ = false; + + request_.clear(); + request_.body().clear(); + + request_.method(boost::beast::http::verb::post); + request_.target("/"); + request_.body() = message; + request_.prepare_payload(); + + std::cout << "ServerConnection::write()" << std::endl; + + boost::beast::http::async_write(socket_, request_, [this](boost::system::error_code err, size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + this->writeHandler(err); + }); +} + +void ServerConnection::writeHandler(const boost::system::error_code &err) { + if (err) { + std::cerr << "ServerConnection::writeHandler error = " << err.message() << std::endl; + + return; + } + + if (callbacks_.onWriteCb) { + callbacks_.onWriteCb(err); + } + + std::cout << "ServerConnection::writeHandler()" << std::endl; + canWrite_ = true; + writeCv_.notify_one(); +} + + +void ServerConnection::setServerUrl(std::string &&url) { + serverUrl_ = url; +} + +void ServerConnection::setServerPort(int port) { + serverPort_ = port; +} + + diff --git a/project/client/src/ToolsPanel.cpp b/project/client/src/ToolsPanel.cpp new file mode 100644 index 0000000..297ac3e --- /dev/null +++ b/project/client/src/ToolsPanel.cpp @@ -0,0 +1,52 @@ +// +// Created by eremey on 18.11.2021. +// + +#include +#include +#include "ToolsPanel.h" + +#include "Brush.h" + +ToolsPanel::ToolsPanel(QWidget *parent) : QToolBar(parent) { + QPixmap brush_pm("brush.png"); + + auto *brush = new Brush(20, Qt::darkGreen, this); + addAction(brush); + + QPixmap pixmap(brush_pm.size()); + pixmap.fill( Qt::white); + + QAction *lasso = addAction(pixmap, "L"); + QAction *elaser = addAction(pixmap, "E"); + QAction *filler = addAction(pixmap, "F"); + QAction *scaling = addAction(pixmap, "S"); + QAction *loupe = addAction(pixmap, "L"); + QAction *primitives = addAction(pixmap, "P"); + + connect(brush, &Brush::BrushTriggered, this, &ToolsPanel::BrushTriggeredSlot); + + connect(this, &ToolsPanel::SetBrushSize, brush, &Brush::SetSizeSlot); + connect(this, &ToolsPanel::SetBrushRed, brush, &Brush::SetRedSlot); + connect(this, &ToolsPanel::SetBrushGreen, brush, &Brush::SetGreenSlot); + connect(this, &ToolsPanel::SetBrushBlue, brush, &Brush::SetBlueSlot); + connect(this, &ToolsPanel::SetBrushOpacity, brush, &Brush::SetOpacitySlot); +} +void ToolsPanel::BrushTriggeredSlot(qreal brushSize, const QColor& brushColor) { + emit(BrushTriggered(brushSize, brushColor)); +} +void ToolsPanel::SetBrushSizeSlot(int value) { + emit(SetBrushSize(value)); +} +void ToolsPanel::SetBrushRedSlot(int value) { + emit(SetBrushRed(value)); +} +void ToolsPanel::SetBrushGreenSlot(int value) { + emit(SetBrushGreen(value)); +} +void ToolsPanel::SetBrushBlueSlot(int value) { + emit(SetBrushBlue(value)); +} +void ToolsPanel::SetBrushOpacitySlot(int value) { + emit(SetBrushOpacity(value)); +} diff --git a/project/client/src/main.cpp b/project/client/src/main.cpp new file mode 100644 index 0000000..5d259ae --- /dev/null +++ b/project/client/src/main.cpp @@ -0,0 +1,13 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow window; + window.setMinimumSize(500, 500); + window.setWindowTitle("DeepPic"); + window.showMaximized(); + return a.exec(); +} diff --git a/project/client/src/main_window_test.cpp b/project/client/src/main_window_test.cpp new file mode 100644 index 0000000..5a5358c --- /dev/null +++ b/project/client/src/main_window_test.cpp @@ -0,0 +1,217 @@ +// +// Created by eremey on 18.11.2021. +// +#include +#include "MainWindow.h" +#include + +#include +#include + +TEST(INVALID_SIZE, test1) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = -1; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid size") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_COLOR, invalidRed1) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = -1; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid red color") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_COLOR, invalidRed2) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 300; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid red color") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_COLOR, invalidGreen1) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 20; + curve.color_green = -1; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid green color") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_COLOR, invalidGreen2) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 20; + curve.color_green = 300; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid green color") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_COLOR, invalidBlue1) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 20; + curve.color_green = 40; + curve.color_blue = -1; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid blue color") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_COLOR, invalidBlue2) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 20; + curve.color_green = 40; + curve.color_blue = 300; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid blue color") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_LENGTH, invalidLength1) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 20; + curve.color_green = 40; + curve.color_blue = 30; + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid curve size") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(INVALID_LENGTH, invalidLength2) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 20; + curve.color_green = 40; + curve.color_blue = 30; + curve.coords.emplace_back(QPointF(1,1)); + + bool isSuccessTest = false; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + if (strcmp(ex.what(), "Invalid curve size") == 0) { + isSuccessTest = true; + } + } + EXPECT_EQ(isSuccessTest, true); +} + +TEST(VALID_CURVE, validCurve) { + MainWindow mainWindow; + Curve curve; + curve.brush_size = 10; + curve.color_red = 20; + curve.color_green = 40; + curve.color_blue = 30; + curve.coords.emplace_back(QPointF(1,1)); + curve.coords.emplace_back(QPointF(2,2)); + + bool isSuccessTest = true; + try { + mainWindow.executeBrush(curve); + } + + catch (std::invalid_argument const& ex) { + isSuccessTest = false; + } + EXPECT_EQ(isSuccessTest, true); +} + +int main(int argc, char **argv) { + QApplication a(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file