Skip to content

Commit e58c9ee

Browse files
committed
Implement data_listcontents block
1 parent 8f90f33 commit e58c9ee

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed

src/blocks/listblocks.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <scratchcpp/iengine.h>
44
#include <scratchcpp/compiler.h>
55
#include <scratchcpp/compilerconstant.h>
6+
#include <scratchcpp/block.h>
67
#include <scratchcpp/field.h>
78
#include <scratchcpp/list.h>
89

@@ -27,6 +28,8 @@ Rgb ListBlocks::color() const
2728

2829
void ListBlocks::registerBlocks(IEngine *engine)
2930
{
31+
// Blocks
32+
engine->addCompileFunction(this, "data_listcontents", &compileListContents);
3033
engine->addCompileFunction(this, "data_addtolist", &compileAddToList);
3134
engine->addCompileFunction(this, "data_deleteoflist", &compileDeleteOfList);
3235
engine->addCompileFunction(this, "data_deletealloflist", &compileDeleteAllOfList);
@@ -36,6 +39,24 @@ void ListBlocks::registerBlocks(IEngine *engine)
3639
engine->addCompileFunction(this, "data_itemnumoflist", &compileItemNumOfList);
3740
engine->addCompileFunction(this, "data_lengthoflist", &compileLengthOfList);
3841
engine->addCompileFunction(this, "data_listcontainsitem", &compileListContainsItem);
42+
43+
// Monitor names
44+
engine->addMonitorNameFunction(this, "data_listcontents", &listContentsMonitorName);
45+
}
46+
47+
CompilerValue *ListBlocks::compileListContents(Compiler *compiler)
48+
{
49+
auto list = compiler->field("LIST")->valuePtr();
50+
assert(list);
51+
52+
// If this block is used in a list monitor, return the list pointer
53+
if (compiler->block()->isMonitorBlock()) {
54+
// TODO: Refactor this
55+
// List monitors expect a reference to the list
56+
// We don't have a list reference type, so cast the pointer to number
57+
return compiler->addConstValue((uintptr_t)list.get());
58+
} else
59+
return compiler->addListContents(static_cast<List *>(list.get()));
3960
}
4061

4162
CompilerValue *ListBlocks::compileAddToList(Compiler *compiler)
@@ -194,3 +215,19 @@ CompilerValue *ListBlocks::compileListContainsItem(Compiler *compiler)
194215

195216
return nullptr;
196217
}
218+
219+
const std::string &ListBlocks::listContentsMonitorName(Block *block)
220+
{
221+
static const std::string empty = "";
222+
auto field = block->fieldAt(block->findField("LIST"));
223+
224+
if (!field)
225+
return empty;
226+
227+
List *list = dynamic_cast<List *>(field->valuePtr().get());
228+
229+
if (list)
230+
return list->name();
231+
else
232+
return empty;
233+
}

src/blocks/listblocks.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class ListBlocks : public IExtension
1919
void registerBlocks(IEngine *engine) override;
2020

2121
private:
22+
static CompilerValue *compileListContents(Compiler *compiler);
2223
static CompilerValue *compileAddToList(Compiler *compiler);
2324
static CompilerValue *getListIndex(Compiler *compiler, CompilerValue *input, List *list, CompilerValue *listSize);
2425
static CompilerValue *compileDeleteOfList(Compiler *compiler);
@@ -29,6 +30,8 @@ class ListBlocks : public IExtension
2930
static CompilerValue *compileItemNumOfList(Compiler *compiler);
3031
static CompilerValue *compileLengthOfList(Compiler *compiler);
3132
static CompilerValue *compileListContainsItem(Compiler *compiler);
33+
34+
static const std::string &listContentsMonitorName(Block *block);
3235
};
3336

3437
} // namespace libscratchcpp

test/blocks/list_blocks_test.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
#include <scratchcpp/block.h>
99
#include <scratchcpp/input.h>
1010
#include <scratchcpp/field.h>
11+
#include <scratchcpp/monitor.h>
1112
#include <scratchcpp/executioncontext.h>
1213
#include <scratchcpp/executablecode.h>
1314
#include <enginemock.h>
1415
#include <randomgeneratormock.h>
16+
#include <monitorhandlermock.h>
1517

1618
#include "../common.h"
1719
#include "util.h"
@@ -21,6 +23,8 @@ using namespace libscratchcpp;
2123
using namespace libscratchcpp::test;
2224

2325
using ::testing::Return;
26+
using ::testing::Invoke;
27+
using ::testing::_;
2428

2529
class ListBlocksTest : public testing::Test
2630
{
@@ -40,6 +44,81 @@ class ListBlocksTest : public testing::Test
4044
RandomGeneratorMock m_rng;
4145
};
4246

47+
TEST_F(ListBlocksTest, ListContents)
48+
{
49+
auto target = std::make_shared<Sprite>();
50+
auto list1 = std::make_shared<List>("", "");
51+
target->addList(list1);
52+
auto list2 = std::make_shared<List>("", "");
53+
target->addList(list2);
54+
55+
list1->append(1);
56+
list1->append(2);
57+
list1->append(4);
58+
59+
list2->append("Lorem");
60+
list2->append("ipsum");
61+
list2->append("dolor");
62+
list2->append("sit");
63+
list2->append("amet");
64+
65+
ScriptBuilder builder(m_extension.get(), m_engine, target);
66+
67+
builder.addBlock("data_listcontents");
68+
builder.addEntityField("LIST", list1);
69+
builder.captureBlockReturnValue();
70+
71+
builder.addBlock("data_listcontents");
72+
builder.addEntityField("LIST", list2);
73+
builder.captureBlockReturnValue();
74+
75+
builder.build();
76+
77+
builder.run();
78+
List *list = builder.capturedValues();
79+
ValueData *data = list->data();
80+
ASSERT_EQ(list->size(), 2);
81+
ASSERT_EQ(Value(data[0]).toString(), "124");
82+
ASSERT_EQ(Value(data[1]).toString(), "Lorem ipsum dolor sit amet");
83+
}
84+
85+
TEST_F(ListBlocksTest, ListMonitor)
86+
{
87+
auto target = std::make_shared<Sprite>();
88+
auto list1 = std::make_shared<List>("", "list1");
89+
target->addList(list1);
90+
auto list2 = std::make_shared<List>("", "list2");
91+
target->addList(list2);
92+
93+
MonitorHandlerMock iface1, iface2;
94+
EXPECT_CALL(iface1, init);
95+
EXPECT_CALL(iface2, init);
96+
97+
auto monitor1 = std::make_shared<Monitor>("monitor", "data_listcontents");
98+
auto monitor2 = std::make_shared<Monitor>("monitor", "data_listcontents");
99+
monitor1->block()->setTarget(target.get());
100+
monitor1->setInterface(&iface1);
101+
monitor2->block()->setTarget(target.get());
102+
monitor2->setInterface(&iface2);
103+
m_engine->setMonitors({ monitor1, monitor2 });
104+
105+
ScriptBuilder builder1(m_extension.get(), m_engine, target);
106+
builder1.addBlock(monitor1->block());
107+
builder1.addEntityField("LIST", list1);
108+
109+
ScriptBuilder builder2(m_extension.get(), m_engine, target);
110+
builder2.addBlock(monitor2->block());
111+
builder2.addEntityField("LIST", list2);
112+
113+
m_engine->compile();
114+
ASSERT_EQ(monitor1->name(), list1->name());
115+
ASSERT_EQ(monitor2->name(), list2->name());
116+
117+
EXPECT_CALL(iface1, onValueChanged(_)).WillOnce(Invoke([list1](const Value &value) { ASSERT_EQ(value.toDouble(), (uintptr_t)list1.get()); }));
118+
EXPECT_CALL(iface2, onValueChanged(_)).WillOnce(Invoke([list2](const Value &value) { ASSERT_EQ(value.toDouble(), (uintptr_t)list2.get()); }));
119+
m_engine->updateMonitors();
120+
}
121+
43122
TEST_F(ListBlocksTest, AddToList)
44123
{
45124
auto target = std::make_shared<Sprite>();

0 commit comments

Comments
 (0)