From 438d349a475e85d4d88ca0149fe63fceaf7a855c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 3 Dec 2023 19:50:24 +0100 Subject: [PATCH 1/4] Add Comment class --- CMakeLists.txt | 1 + include/scratchcpp/comment.h | 48 +++++++++++ src/scratch/CMakeLists.txt | 3 + src/scratch/comment.cpp | 117 ++++++++++++++++++++++++++ src/scratch/comment_p.cpp | 11 +++ src/scratch/comment_p.h | 28 ++++++ test/scratch_classes/CMakeLists.txt | 14 +++ test/scratch_classes/comment_test.cpp | 100 ++++++++++++++++++++++ 8 files changed, 322 insertions(+) create mode 100644 include/scratchcpp/comment.h create mode 100644 src/scratch/comment.cpp create mode 100644 src/scratch/comment_p.cpp create mode 100644 src/scratch/comment_p.h create mode 100644 test/scratch_classes/comment_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bcdaf5b..d25f0be2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ target_sources(scratchcpp include/scratchcpp/iimageformatfactory.h include/scratchcpp/rect.h include/scratchcpp/igraphicseffect.h + include/scratchcpp/comment.h ) add_library(zip SHARED diff --git a/include/scratchcpp/comment.h b/include/scratchcpp/comment.h new file mode 100644 index 00000000..edd886b9 --- /dev/null +++ b/include/scratchcpp/comment.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include "entity.h" + +namespace libscratchcpp +{ + +class Block; +class CommentPrivate; + +/*! \brief The Comment class represents a comment in the code area. */ +class LIBSCRATCHCPP_EXPORT Comment : public Entity +{ + public: + Comment(const std::string &id, double x = 0, double y = 0); + Comment(const Comment &) = delete; + + const std::string &blockId() const; + void setBlockId(const std::string id); + + std::shared_ptr block() const; + void setBlock(std::shared_ptr block); + + double x() const; + void setX(double x); + + double y() const; + void setY(double y); + + double width() const; + void setWidth(double width); + + double height() const; + void setHeight(double height); + + bool minimized() const; + void setMinimized(bool minimized); + + const std::string &text() const; + void setText(const std::string &text); + + private: + spimpl::unique_impl_ptr impl; +}; + +} // namespace libscratchcpp diff --git a/src/scratch/CMakeLists.txt b/src/scratch/CMakeLists.txt index 6cde28d3..ddc6648a 100644 --- a/src/scratch/CMakeLists.txt +++ b/src/scratch/CMakeLists.txt @@ -48,4 +48,7 @@ target_sources(scratchcpp keyevent.cpp keyevent_p.cpp keyevent_p.h + comment.cpp + comment_p.cpp + comment_p.h ) diff --git a/src/scratch/comment.cpp b/src/scratch/comment.cpp new file mode 100644 index 00000000..72df0d1b --- /dev/null +++ b/src/scratch/comment.cpp @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include + +#include "comment_p.h" + +using namespace libscratchcpp; + +/*! Constructs Comment at the given position in the code area. */ +Comment::Comment(const std::string &id, double x, double y) : + Entity(id), + impl(spimpl::make_unique_impl(x, y)) +{ +} + +/*! Returns the ID of the block the comment is attached to. */ +const std::string &Comment::blockId() const +{ + return impl->blockId; +} + +/*! Sets the ID of the block the comment is attached to. */ +void Comment::setBlockId(const std::string id) +{ + impl->blockId = id; + impl->block = nullptr; +} + +/*! Returns the block the comment is attached to. */ +std::shared_ptr Comment::block() const +{ + return impl->block; +} + +/*! Sets the block the comment is attached to. */ +void Comment::setBlock(std::shared_ptr block) +{ + impl->block = block; + + if (block) + impl->blockId = block->id(); + else + impl->blockId = ""; +} + +/*! Returns the x-coordinate of the comment in the code area. */ +double Comment::x() const +{ + return impl->x; +} + +/*! Sets the x-coordinate of the comment in the code area. */ +void Comment::setX(double x) +{ + impl->x = x; +} + +/*! Returns the y-coordinate of the comment in the code area. */ +double Comment::y() const +{ + return impl->y; +} + +/*! Sets the x-coordinate of the comment in the code area. */ +void Comment::setY(double y) +{ + impl->y = y; +} + +/*! Returns the width. */ +double Comment::width() const +{ + return impl->width; +} + +/*! Sets the width. */ +void Comment::setWidth(double width) +{ + impl->width = width; +} + +/*! Returns the height. */ +double Comment::height() const +{ + return impl->height; +} + +/*! Sets the height. */ +void Comment::setHeight(double height) +{ + impl->height = height; +} + +/*! Returns true if the comment is collapsed and false otherwise. */ +bool Comment::minimized() const +{ + return impl->minimized; +} + +/*! Sets whether the comment is collapsed. */ +void Comment::setMinimized(bool minimized) +{ + impl->minimized = minimized; +} + +/*! Returns the text. */ +const std::string &Comment::text() const +{ + return impl->text; +} + +/*! Sets the text. */ +void Comment::setText(const std::string &text) +{ + impl->text = text; +} diff --git a/src/scratch/comment_p.cpp b/src/scratch/comment_p.cpp new file mode 100644 index 00000000..669e53ea --- /dev/null +++ b/src/scratch/comment_p.cpp @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "comment_p.h" + +using namespace libscratchcpp; + +CommentPrivate::CommentPrivate(double x, double y) : + x(x), + y(y) +{ +} diff --git a/src/scratch/comment_p.h b/src/scratch/comment_p.h new file mode 100644 index 00000000..df6d9512 --- /dev/null +++ b/src/scratch/comment_p.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +namespace libscratchcpp +{ + +class Block; + +struct CommentPrivate +{ + CommentPrivate(double x, double y); + CommentPrivate(const CommentPrivate &) = delete; + + std::string blockId; + std::shared_ptr block; + double x = 0; + double y = 0; + double width = 200; + double height = 200; + bool minimized = false; + std::string text; +}; + +} // namespace libscratchcpp diff --git a/test/scratch_classes/CMakeLists.txt b/test/scratch_classes/CMakeLists.txt index e17cbbf6..b09b46ce 100644 --- a/test/scratch_classes/CMakeLists.txt +++ b/test/scratch_classes/CMakeLists.txt @@ -203,3 +203,17 @@ target_link_libraries( ) gtest_discover_tests(keyevent_test) + +# comment_test +add_executable( + comment_test + comment_test.cpp +) + +target_link_libraries( + comment_test + GTest::gtest_main + scratchcpp +) + +gtest_discover_tests(comment_test) diff --git a/test/scratch_classes/comment_test.cpp b/test/scratch_classes/comment_test.cpp new file mode 100644 index 00000000..38f965a0 --- /dev/null +++ b/test/scratch_classes/comment_test.cpp @@ -0,0 +1,100 @@ +#include +#include + +#include "../common.h" + +using namespace libscratchcpp; + +TEST(CommentTest, Constructors) +{ + { + Comment comment("abc"); + ASSERT_EQ(comment.id(), "abc"); + ASSERT_EQ(comment.x(), 0); + ASSERT_EQ(comment.y(), 0); + } + + { + Comment comment("def", 46.09, -64.12); + ASSERT_EQ(comment.id(), "def"); + ASSERT_EQ(comment.x(), 46.09); + ASSERT_EQ(comment.y(), -64.12); + } +} + +TEST(CommentTest, Block) +{ + Comment comment("a"); + ASSERT_EQ(comment.block(), nullptr); + ASSERT_TRUE(comment.blockId().empty()); + + auto block = std::make_shared("abc", ""); + comment.setBlock(block); + ASSERT_EQ(comment.block(), block); + ASSERT_EQ(comment.blockId(), "abc"); + + comment.setBlock(nullptr); + ASSERT_EQ(comment.block(), nullptr); + ASSERT_TRUE(comment.blockId().empty()); + + comment.setBlockId("hello"); + ASSERT_EQ(comment.blockId(), "hello"); + ASSERT_EQ(comment.block(), nullptr); +} + +TEST(CommentTest, X) +{ + Comment comment("a"); + ASSERT_EQ(comment.x(), 0); + + comment.setX(-76.29); + ASSERT_EQ(comment.x(), -76.29); +} + +TEST(CommentTest, Y) +{ + Comment comment("a"); + ASSERT_EQ(comment.y(), 0); + + comment.setY(38.16); + ASSERT_EQ(comment.y(), 38.16); +} + +TEST(CommentTest, Width) +{ + Comment comment("a"); + ASSERT_EQ(comment.width(), 200); + + comment.setWidth(64.49); + ASSERT_EQ(comment.width(), 64.49); +} + +TEST(CommentTest, Height) +{ + Comment comment("a"); + ASSERT_EQ(comment.height(), 200); + + comment.setHeight(-89.65); + ASSERT_EQ(comment.height(), -89.65); +} + +TEST(CommentTest, Minimized) +{ + Comment comment("a"); + ASSERT_FALSE(comment.minimized()); + + comment.setMinimized(true); + ASSERT_TRUE(comment.minimized()); + + comment.setMinimized(false); + ASSERT_FALSE(comment.minimized()); +} + +TEST(CommentTest, Text) +{ + Comment comment("a"); + ASSERT_TRUE(comment.text().empty()); + + comment.setText("Hello, world!"); + ASSERT_EQ(comment.text(), "Hello, world!"); +} From 4274191caaa4b7652ec05eb40dcc9c671a8d7f65 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 3 Dec 2023 20:42:13 +0100 Subject: [PATCH 2/4] Add comment API to Target --- include/scratchcpp/target.h | 8 ++- src/scratch/target.cpp | 55 ++++++++++++++++++ src/scratch/target_p.h | 2 + test/scratch_classes/target_test.cpp | 84 ++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 1 deletion(-) diff --git a/include/scratchcpp/target.h b/include/scratchcpp/target.h index fbb453e5..08547155 100644 --- a/include/scratchcpp/target.h +++ b/include/scratchcpp/target.h @@ -14,6 +14,7 @@ class IEngine; class Variable; class List; class Block; +class Comment; class Costume; class Sound; class TargetPrivate; @@ -50,6 +51,11 @@ class LIBSCRATCHCPP_EXPORT Target int findBlock(const std::string &id) const; std::vector> greenFlagBlocks() const; + const std::vector> &comments() const; + int addComment(std::shared_ptr comment); + std::shared_ptr commentAt(int index) const; + int findComment(const std::string &id) const; + int costumeIndex() const; void setCostumeIndex(int newCostumeIndex); @@ -75,7 +81,7 @@ class LIBSCRATCHCPP_EXPORT Target void setEngine(IEngine *engine); protected: - /*! Override this method to set a custom data source for blocks and assets. */ + /*! Override this method to set a custom data source for blocks, assets, comments, etc. */ virtual Target *dataSource() const { return nullptr; } private: diff --git a/src/scratch/target.cpp b/src/scratch/target.cpp index 690037d3..89c2af92 100644 --- a/src/scratch/target.cpp +++ b/src/scratch/target.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "target_p.h" @@ -199,6 +200,60 @@ std::vector> Target::greenFlagBlocks() const return ret; } +/*! Returns the list of comments in the code area. */ +const std::vector> &Target::comments() const +{ + if (Target *source = dataSource()) + return source->comments(); + + return impl->comments; +} + +/*! Adds a comment and returns its index. */ +int Target::addComment(std::shared_ptr comment) +{ + if (Target *source = dataSource()) + return source->addComment(comment); + + auto it = std::find(impl->comments.begin(), impl->comments.end(), comment); + + if (it != impl->comments.end()) + return it - impl->comments.begin(); + + impl->comments.push_back(comment); + return impl->comments.size() - 1; +} + +/*! Returns the comment at index. */ +std::shared_ptr Target::commentAt(int index) const +{ + if (Target *source = dataSource()) + return source->commentAt(index); + + if (index < 0 || index >= impl->comments.size()) + return nullptr; + + return impl->comments[index]; +} + +/*! Returns the index of the comment with the given ID. */ +int Target::findComment(const std::string &id) const +{ + if (Target *source = dataSource()) + return source->findComment(id); + + int i = 0; + + for (auto comment : impl->comments) { + if (comment->id() == id) + return i; + + i++; + } + + return -1; +} + /*! Returns the index of the current costume. */ int Target::costumeIndex() const { diff --git a/src/scratch/target_p.h b/src/scratch/target_p.h index 6b20f085..2231e146 100644 --- a/src/scratch/target_p.h +++ b/src/scratch/target_p.h @@ -15,6 +15,7 @@ class IEngine; class Variable; class List; class Block; +class Comment; struct TargetPrivate { @@ -26,6 +27,7 @@ struct TargetPrivate std::vector> variables; std::vector> lists; std::vector> blocks; + std::vector> comments; int costumeIndex = -1; std::vector> costumes; std::vector> sounds; diff --git a/test/scratch_classes/target_test.cpp b/test/scratch_classes/target_test.cpp index 4e6ad92c..d3bde5b4 100644 --- a/test/scratch_classes/target_test.cpp +++ b/test/scratch_classes/target_test.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,89 @@ TEST(TargetTest, Blocks) ASSERT_EQ(source.greenFlagBlocks(), std::vector>({ b1, b4 })); } +TEST(TargetTest, Comments) +{ + auto c1 = std::make_shared("a"); + auto c2 = std::make_shared("b"); + auto c3 = std::make_shared("c"); + auto c4 = std::make_shared("d"); + + TargetMock target; + EXPECT_CALL(target, dataSource()).Times(17).WillRepeatedly(Return(nullptr)); + + ASSERT_EQ(target.addComment(c1), 0); + ASSERT_EQ(target.addComment(c2), 1); + ASSERT_EQ(target.addComment(c3), 2); + ASSERT_EQ(target.addComment(c4), 3); + ASSERT_EQ(target.addComment(c2), 1); // add existing block + + ASSERT_EQ(target.comments(), std::vector>({ c1, c2, c3, c4 })); + ASSERT_EQ(target.commentAt(0), c1); + ASSERT_EQ(target.commentAt(1), c2); + ASSERT_EQ(target.commentAt(2), c3); + ASSERT_EQ(target.commentAt(3), c4); + ASSERT_EQ(target.commentAt(4), nullptr); + ASSERT_EQ(target.commentAt(-1), nullptr); + + ASSERT_EQ(target.findComment("e"), -1); + ASSERT_EQ(target.findComment("a"), 0); + ASSERT_EQ(target.findComment("b"), 1); + ASSERT_EQ(target.findComment("c"), 2); + ASSERT_EQ(target.findComment("d"), 3); + + // Test with custom data source + Target source; + + EXPECT_CALL(target, dataSource()).WillOnce(Return(&source)); + + ASSERT_TRUE(target.comments().empty()); + + TargetMock target2; + EXPECT_CALL(target2, dataSource()).Times(17).WillRepeatedly(Return(&source)); + + ASSERT_EQ(target2.addComment(c1), 0); + ASSERT_EQ(target2.addComment(c2), 1); + ASSERT_EQ(target2.addComment(c3), 2); + ASSERT_EQ(target2.addComment(c4), 3); + ASSERT_EQ(target2.addComment(c2), 1); // add existing block + + ASSERT_EQ(target2.commentAt(0), c1); + ASSERT_EQ(target2.commentAt(1), c2); + ASSERT_EQ(target2.commentAt(2), c3); + ASSERT_EQ(target2.commentAt(3), c4); + ASSERT_EQ(target2.commentAt(4), nullptr); + ASSERT_EQ(target2.commentAt(-1), nullptr); + + ASSERT_EQ(target2.findComment("e"), -1); + ASSERT_EQ(target2.findComment("a"), 0); + ASSERT_EQ(target2.findComment("b"), 1); + ASSERT_EQ(target2.findComment("c"), 2); + ASSERT_EQ(target2.findComment("d"), 3); + + ASSERT_EQ(target2.comments(), source.comments()); + + auto c5 = std::make_shared("e"); + ASSERT_EQ(source.addComment(c5), 4); + + EXPECT_CALL(target2, dataSource()).WillOnce(Return(&source)); + ASSERT_EQ(target2.comments(), source.comments()); + + ASSERT_EQ(source.commentAt(0), c1); + ASSERT_EQ(source.commentAt(1), c2); + ASSERT_EQ(source.commentAt(2), c3); + ASSERT_EQ(source.commentAt(3), c4); + ASSERT_EQ(source.commentAt(4), c5); + ASSERT_EQ(source.commentAt(5), nullptr); + ASSERT_EQ(source.commentAt(-1), nullptr); + + ASSERT_EQ(source.findComment("f"), -1); + ASSERT_EQ(source.findComment("a"), 0); + ASSERT_EQ(source.findComment("b"), 1); + ASSERT_EQ(source.findComment("c"), 2); + ASSERT_EQ(source.findComment("d"), 3); + ASSERT_EQ(source.findComment("e"), 4); +} + TEST(TargetTest, CostumeIndex) { Target target; From a148c4b330499749dcd5c2221d8bf44b3fef439b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 3 Dec 2023 21:01:16 +0100 Subject: [PATCH 3/4] Add comment property to Block --- include/scratchcpp/block.h | 6 ++++++ src/scratch/block.cpp | 31 +++++++++++++++++++++++++++++ src/scratch/block_p.h | 3 +++ test/scratch_classes/block_test.cpp | 25 +++++++++++++++++++++++ 4 files changed, 65 insertions(+) diff --git a/include/scratchcpp/block.h b/include/scratchcpp/block.h index 48c319cd..f632420a 100644 --- a/include/scratchcpp/block.h +++ b/include/scratchcpp/block.h @@ -12,6 +12,7 @@ class IEngine; class Target; class Input; class Field; +class Comment; class InputValue; class BlockPrivate; @@ -55,6 +56,11 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity bool topLevel() const; + std::shared_ptr comment() const; + std::string commentId() const; + void setComment(std::shared_ptr comment); + void setCommentId(const std::string &commentId); + void setEngine(IEngine *newEngine); IEngine *engine() const; diff --git a/src/scratch/block.cpp b/src/scratch/block.cpp index 5ad69cd6..b36e5f48 100644 --- a/src/scratch/block.cpp +++ b/src/scratch/block.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "block_p.h" @@ -295,6 +296,36 @@ bool Block::topLevel() const return (impl->parentId == "" && !impl->parent); } +/*! Returns the comment which is attached to this block. */ +std::shared_ptr Block::comment() const +{ + return impl->comment; +} + +/*! Returns the ID of the comment which is attached to this block. */ +std::string Block::commentId() const +{ + return impl->commentId; +} + +/*! Sets the comment which is attached to this block. */ +void Block::setComment(std::shared_ptr comment) +{ + impl->comment = comment; + + if (comment) + impl->commentId = comment->id(); + else + impl->commentId = ""; +} + +/*! Sets the ID of the comment which is attached to this block. */ +void Block::setCommentId(const std::string &commentId) +{ + impl->commentId = commentId; + impl->comment = nullptr; +} + /*! Sets the Engine. */ void Block::setEngine(IEngine *newEngine) { diff --git a/src/scratch/block_p.h b/src/scratch/block_p.h index a8b36b97..15615bc6 100644 --- a/src/scratch/block_p.h +++ b/src/scratch/block_p.h @@ -14,6 +14,7 @@ class IEngine; class Target; class Input; class Field; +class Comment; struct BlockPrivate { @@ -31,6 +32,8 @@ struct BlockPrivate std::vector> fields; std::unordered_map fieldMap; bool shadow = false; + std::string commentId; + std::shared_ptr comment = nullptr; IEngine *engine = nullptr; Target *target = nullptr; BlockPrototype mutationPrototype; diff --git a/test/scratch_classes/block_test.cpp b/test/scratch_classes/block_test.cpp index cec36baf..e762b77f 100644 --- a/test/scratch_classes/block_test.cpp +++ b/test/scratch_classes/block_test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -193,6 +194,30 @@ TEST_F(BlockTest, TopLevel) ASSERT_TRUE(block.topLevel()); } +TEST_F(BlockTest, Comment) +{ + Block block("", ""); + ASSERT_EQ(block.comment(), nullptr); + ASSERT_TRUE(block.commentId().empty()); + + block.setCommentId("hello"); + ASSERT_EQ(block.comment(), nullptr); + ASSERT_EQ(block.commentId(), "hello"); + + auto comment = std::make_shared("abc"); + block.setComment(comment); + ASSERT_EQ(block.comment(), comment); + ASSERT_EQ(block.commentId(), "abc"); + + block.setCommentId("def"); + ASSERT_EQ(block.comment(), nullptr); + ASSERT_EQ(block.commentId(), "def"); + + block.setComment(nullptr); + ASSERT_EQ(block.comment(), nullptr); + ASSERT_EQ(block.commentId(), ""); +} + TEST_F(BlockTest, Engine) { Block block("", ""); From 19283edc2f00e17676ba8da3500584b377f44f7e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 3 Dec 2023 21:57:04 +0100 Subject: [PATCH 4/4] Load comments --- src/engine/internal/engine.cpp | 24 ++++++++++ src/engine/internal/engine.h | 1 + src/internal/scratch3reader.cpp | 30 +++++++++++- test/load_project/load_project_test.cpp | 60 +++++++++++++++++++++++- test/load_test.sb3 | Bin 48765 -> 48398 bytes 5 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index 2473119a..67f2a44d 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,14 @@ void Engine::resolveIds() block->updateInputMap(); block->updateFieldMap(); + + auto comment = getComment(block->commentId()); + block->setComment(comment); + + if (comment) { + comment->setBlock(block); + assert(comment->blockId() == block->id()); + } } } } @@ -1078,6 +1087,21 @@ std::shared_ptr Engine::getBroadcast(const std::string &id) return nullptr; } +// Returns the comment with the given ID. +std::shared_ptr Engine::getComment(const std::string &id) +{ + if (id.empty()) + return nullptr; + + for (auto target : m_targets) { + int index = target->findComment(id); + if (index != -1) + return target->commentAt(index); + } + + return nullptr; +} + // Returns the entity with the given ID. \see IEntity std::shared_ptr Engine::getEntity(const std::string &id) { diff --git a/src/engine/internal/engine.h b/src/engine/internal/engine.h index 33ebbc3b..26eda988 100644 --- a/src/engine/internal/engine.h +++ b/src/engine/internal/engine.h @@ -143,6 +143,7 @@ class Engine : public IEngine std::shared_ptr getVariable(const std::string &id); std::shared_ptr getList(const std::string &id); std::shared_ptr getBroadcast(const std::string &id); + std::shared_ptr getComment(const std::string &id); std::shared_ptr getEntity(const std::string &id); std::shared_ptr blockSection(const std::string &opcode) const; diff --git a/src/internal/scratch3reader.cpp b/src/internal/scratch3reader.cpp index d2168656..2602b177 100644 --- a/src/internal/scratch3reader.cpp +++ b/src/internal/scratch3reader.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -188,10 +189,37 @@ bool Scratch3Reader::load() READER_STEP(step, "target -> block -> shadow"); block->setShadow(blockInfo["shadow"]); + // comment + READER_STEP(step, "target -> block -> comment"); + if (!blockInfo["comment"].is_null()) + block->setCommentId(blockInfo["comment"]); + target->addBlock(block); } - // TODO: Add comments + // comments + READER_STEP(step, "target -> comments"); + auto comments = jsonTarget["comments"]; + for (json::iterator it = comments.begin(); it != comments.end(); ++it) { + auto commentInfo = it.value(); + READER_STEP(step, "target -> comment -> { id, x, y }"); + auto comment = std::make_shared(it.key(), jsonToValue(commentInfo["x"]).toDouble(), jsonToValue(commentInfo["y"]).toDouble()); + READER_STEP(step, "target -> comment -> blockId"); + + if (!commentInfo["blockId"].is_null()) + comment->setBlockId(commentInfo["blockId"]); + + READER_STEP(step, "target -> comment -> width"); + comment->setWidth(jsonToValue(commentInfo["width"]).toDouble()); + READER_STEP(step, "target -> comment -> height"); + comment->setHeight(jsonToValue(commentInfo["height"]).toDouble()); + READER_STEP(step, "target -> comment -> minimized"); + comment->setMinimized(commentInfo["minimized"]); + READER_STEP(step, "target -> comment -> text"); + comment->setText(commentInfo["text"]); + + target->addComment(comment); + } // costumes READER_STEP(step, "target -> costumes"); diff --git a/test/load_project/load_project_test.cpp b/test/load_project/load_project_test.cpp index 855fce1a..47cc5d96 100644 --- a/test/load_project/load_project_test.cpp +++ b/test/load_project/load_project_test.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../common.h" @@ -39,7 +40,7 @@ TEST(LoadProjectTest, EmptyProject) ASSERT_EQ(stage->lists().size(), 0); ASSERT_EQ(stage->blocks().size(), 0); ASSERT_EQ(stage->costumes().size(), 1); - // TODO: Add comments + ASSERT_TRUE(stage->comments().empty()); ASSERT_EQ(stage->costumeIndex(), 0); ASSERT_EQ(stage->sounds().size(), 0); ASSERT_EQ(stage->layerOrder(), 0); @@ -82,9 +83,22 @@ TEST(LoadProjectTest, LoadTestProject) // Stage Stage *stage = engine->stage(); + ASSERT_EQ(stage->comments().size(), 1); ASSERT_EQ(stage->variables().size(), 2); ASSERT_EQ(stage->lists().size(), 1); + // Stage: comments + auto comment = stage->commentAt(0); + ASSERT_EQ(comment->id(), "y"); + ASSERT_EQ(comment->blockId(), ""); + ASSERT_EQ(comment->block(), nullptr); + ASSERT_EQ(comment->x(), 202.96296296296296); + ASSERT_EQ(comment->y(), 293.3333333333335); + ASSERT_EQ(comment->width(), 124.4444580078125); + ASSERT_EQ(comment->height(), 114.0740966796875); + ASSERT_EQ(comment->minimized(), false); + ASSERT_EQ(comment->text(), "TEST"); + // Stage: variables ASSERT_VAR(stage, "var1"); ASSERT_EQ(GET_VAR(stage, "var1")->value().toString(), "Hello, world!"); @@ -104,6 +118,7 @@ TEST(LoadProjectTest, LoadTestProject) ASSERT_EQ(sprite1->lists().size(), 1); ASSERT_EQ(sprite1->blocks().size(), 18); ASSERT_EQ(sprite1->costumes().size(), 2); + ASSERT_EQ(sprite1->comments().size(), 1); ASSERT_EQ(sprite1->costumeIndex(), 1); ASSERT_EQ(sprite1->sounds().size(), 1); ASSERT_TRUE(sprite1->visible()); @@ -115,6 +130,18 @@ TEST(LoadProjectTest, LoadTestProject) ASSERT_EQ(sprite1->rotationStyleStr(), "all around"); ASSERT_EQ(sprite1->rotationStyle(), Sprite::RotationStyle::AllAround); + // Sprite1: comments + comment = sprite1->commentAt(0); + ASSERT_EQ(comment->id(), "z"); + ASSERT_EQ(comment->blockId(), ""); + ASSERT_EQ(comment->block(), nullptr); + ASSERT_EQ(comment->x(), 509.6296296296298); + ASSERT_EQ(comment->y(), 386.66666666666674); + ASSERT_EQ(comment->width(), 171.85186767578125); + ASSERT_EQ(comment->height(), 97.77777099609375); + ASSERT_EQ(comment->minimized(), false); + ASSERT_EQ(comment->text(), "Hello, world!"); + // Sprite1: sounds auto sound = sprite1->soundAt(0); ASSERT_EQ(sound->name(), "Meow"); @@ -160,6 +187,8 @@ TEST(LoadProjectTest, LoadTestProject) ASSERT_EQ(GET_FIELD(block, "VARIABLE")->valuePtr(), GET_VAR(stage, "var1")); ASSERT_INPUT(block, "VALUE"); ASSERT_EQ(GET_INPUT(block, "VALUE")->primaryValue()->value().toInt(), 0); + ASSERT_TRUE(block->commentId().empty()); + ASSERT_EQ(block->comment(), nullptr); block = block->next(); ASSERT_TRUE(block); @@ -184,6 +213,8 @@ TEST(LoadProjectTest, LoadTestProject) ASSERT_EQ(GET_INPUT(block, prototype->argumentIds()[0])->primaryValue()->value().toString(), "test"); ASSERT_INPUT(block, prototype->argumentIds()[1]); ASSERT_TRUE(GET_INPUT(block, prototype->argumentIds()[1])->valueBlock()); + ASSERT_TRUE(block->commentId().empty()); + ASSERT_EQ(block->comment(), nullptr); ASSERT_FALSE(block->next()); @@ -234,6 +265,7 @@ TEST(LoadProjectTest, LoadTestProject) ASSERT_EQ(sprite2->lists().size(), 1); ASSERT_EQ(sprite2->blocks().size(), 5); ASSERT_EQ(sprite2->costumes().size(), 3); + ASSERT_EQ(sprite2->comments().size(), 2); ASSERT_EQ(sprite2->sounds().size(), 1); ASSERT_FALSE(sprite2->visible()); ASSERT_EQ(std::round(sprite2->x()), 143); @@ -244,6 +276,32 @@ TEST(LoadProjectTest, LoadTestProject) ASSERT_EQ(sprite2->rotationStyleStr(), "don't rotate"); ASSERT_EQ(sprite2->rotationStyle(), Sprite::RotationStyle::DoNotRotate); + // Balloon1: comments + comment = sprite2->commentAt(0); + ASSERT_EQ(comment->id(), "A"); + ASSERT_EQ(comment->blockId(), ""); + ASSERT_EQ(comment->block(), nullptr); + ASSERT_EQ(comment->x(), 459.25925925925924); + ASSERT_EQ(comment->y(), -45.925925925925924); + ASSERT_EQ(comment->width(), 200); + ASSERT_EQ(comment->height(), 200); + ASSERT_EQ(comment->minimized(), true); + ASSERT_EQ(comment->text(), "test"); + + comment = sprite2->commentAt(1); + auto commentBlock = sprite2->blockAt(sprite2->findBlock("e")); + ASSERT_EQ(comment->id(), "w"); + ASSERT_EQ(comment->blockId(), "e"); + ASSERT_EQ(comment->block(), commentBlock); + ASSERT_EQ(comment->x(), 247.3981475830078); + ASSERT_EQ(comment->y(), 208); + ASSERT_EQ(comment->width(), 189.62969970703125); + ASSERT_EQ(comment->height(), 93.33334350585938); + ASSERT_EQ(comment->minimized(), false); + ASSERT_EQ(comment->text(), "..."); + ASSERT_EQ(commentBlock->commentId(), "w"); + ASSERT_EQ(commentBlock->comment(), comment); + // Balloon1: sounds sound = sprite2->soundAt(0); ASSERT_EQ(sound->name(), "Pop"); diff --git a/test/load_test.sb3 b/test/load_test.sb3 index cd7c8eb45057ed72ff0f996f5828bad212de63f3..f64d6f38fa11693433e6cf65e46f9cd912d95d55 100644 GIT binary patch delta 2818 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 delta 3187 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