Skip to content

Commit 55b0221

Browse files
committed
Implement data_showlist block
1 parent c9d3037 commit 55b0221

File tree

3 files changed

+269
-1
lines changed

3 files changed

+269
-1
lines changed

src/blocks/listblocks.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <scratchcpp/block.h>
77
#include <scratchcpp/field.h>
88
#include <scratchcpp/list.h>
9+
#include <scratchcpp/target.h>
10+
#include <scratchcpp/monitor.h>
911

1012
#include "listblocks.h"
1113

@@ -39,6 +41,7 @@ void ListBlocks::registerBlocks(IEngine *engine)
3941
engine->addCompileFunction(this, "data_itemnumoflist", &compileItemNumOfList);
4042
engine->addCompileFunction(this, "data_lengthoflist", &compileLengthOfList);
4143
engine->addCompileFunction(this, "data_listcontainsitem", &compileListContainsItem);
44+
engine->addCompileFunction(this, "data_showlist", &compileShowList);
4245

4346
// Monitor names
4447
engine->addMonitorNameFunction(this, "data_listcontents", &listContentsMonitorName);
@@ -213,6 +216,19 @@ CompilerValue *ListBlocks::compileListContainsItem(Compiler *compiler)
213216
return nullptr;
214217
}
215218

219+
CompilerValue *ListBlocks::compileShowList(Compiler *compiler)
220+
{
221+
Field *field = compiler->field("LIST");
222+
assert(field);
223+
List *list = static_cast<List *>(field->valuePtr().get());
224+
assert(list);
225+
226+
CompilerConstant *listPtr = compiler->addConstValue(list);
227+
compiler->addTargetFunctionCall("data_showlist", Compiler::StaticType::Void, { Compiler::StaticType::Pointer }, { listPtr });
228+
229+
return nullptr;
230+
}
231+
216232
const std::string &ListBlocks::listContentsMonitorName(Block *block)
217233
{
218234
static const std::string empty = "";
@@ -228,3 +244,20 @@ const std::string &ListBlocks::listContentsMonitorName(Block *block)
228244
else
229245
return empty;
230246
}
247+
248+
extern "C" void data_showlist(Target *target, List *list)
249+
{
250+
Monitor *monitor = list->monitor();
251+
252+
if (!monitor) {
253+
/*
254+
* We need shared_ptr.
255+
* Since this case doesn't occur frequently,
256+
* we can look up the list by ID.
257+
*/
258+
auto index = target->findListById(list->id());
259+
monitor = target->engine()->createListMonitor(target->listAt(index), "data_listcontents", "LIST", -1, &ListBlocks::compileListContents);
260+
}
261+
262+
monitor->setVisible(true);
263+
}

src/blocks/listblocks.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ class ListBlocks : public IExtension
1818

1919
void registerBlocks(IEngine *engine) override;
2020

21-
private:
21+
// NOTE: Must be public because of monitor show/hide blocks
2222
static CompilerValue *compileListContents(Compiler *compiler);
23+
24+
private:
2325
static CompilerValue *compileAddToList(Compiler *compiler);
2426
static CompilerValue *getListIndex(Compiler *compiler, CompilerValue *input, List *list, CompilerValue *listSize);
2527
static CompilerValue *compileDeleteOfList(Compiler *compiler);
@@ -30,6 +32,7 @@ class ListBlocks : public IExtension
3032
static CompilerValue *compileItemNumOfList(Compiler *compiler);
3133
static CompilerValue *compileLengthOfList(Compiler *compiler);
3234
static CompilerValue *compileListContainsItem(Compiler *compiler);
35+
static CompilerValue *compileShowList(Compiler *compiler);
3336

3437
static const std::string &listContentsMonitorName(Block *block);
3538
};

test/blocks/list_blocks_test.cpp

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <scratchcpp/project.h>
22
#include <scratchcpp/sprite.h>
3+
#include <scratchcpp/stage.h>
34
#include <scratchcpp/list.h>
45
#include <scratchcpp/compiler.h>
56
#include <scratchcpp/test/scriptbuilder.h>
@@ -567,3 +568,234 @@ TEST_F(ListBlocksTest, ListContainsItem)
567568
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
568569
ASSERT_EQ(list->toString(), "Lorem ipsum dolor 123 true dolor");
569570
}
571+
572+
TEST_F(ListBlocksTest, ShowList_Global_Existent)
573+
{
574+
auto stage = std::make_shared<Stage>();
575+
auto list1 = std::make_shared<List>("a", "list1");
576+
list1->append("item1");
577+
list1->append("item2");
578+
stage->addList(list1);
579+
auto list2 = std::make_shared<List>("b", "list2");
580+
list2->append("Hello");
581+
list2->append("world");
582+
stage->addList(list2);
583+
584+
auto monitor1 = std::make_shared<Monitor>("monitor", "data_listcontents");
585+
monitor1->block()->addField(std::make_shared<Field>("LIST", list1->name(), list1));
586+
monitor1->setVisible(true);
587+
588+
auto monitor2 = std::make_shared<Monitor>("monitor", "data_listcontents");
589+
monitor2->block()->addField(std::make_shared<Field>("LIST", list2->name(), list2));
590+
monitor2->setVisible(false);
591+
592+
m_engine->setMonitors({ monitor1, monitor2 });
593+
list1->setMonitor(monitor1.get());
594+
list2->setMonitor(monitor2.get());
595+
596+
m_engine->setTargets({ stage });
597+
598+
ScriptBuilder builder1(m_extension.get(), m_engine, stage);
599+
builder1.addBlock("data_showlist");
600+
builder1.addEntityField("LIST", list1);
601+
602+
ScriptBuilder builder2(m_extension.get(), m_engine, stage);
603+
builder2.addBlock("data_showlist");
604+
builder2.addEntityField("LIST", list2);
605+
606+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
607+
608+
builder1.run();
609+
ASSERT_TRUE(monitor1->visible());
610+
ASSERT_FALSE(monitor2->visible());
611+
612+
builder2.run();
613+
ASSERT_TRUE(monitor1->visible());
614+
ASSERT_TRUE(monitor2->visible());
615+
}
616+
617+
TEST_F(ListBlocksTest, ShowList_Global_Nonexistent)
618+
{
619+
auto stage = std::make_shared<Stage>();
620+
auto list1 = std::make_shared<List>("a", "list1");
621+
list1->append("item1");
622+
list1->append("item2");
623+
stage->addList(list1);
624+
auto list2 = std::make_shared<List>("b", "list2");
625+
list2->append("Hello");
626+
list2->append("world");
627+
stage->addList(list2);
628+
629+
m_engine->setTargets({ stage });
630+
631+
ScriptBuilder builder1(m_extension.get(), m_engine, stage);
632+
builder1.addBlock("data_showlist");
633+
builder1.addEntityField("LIST", list1);
634+
635+
ScriptBuilder builder2(m_extension.get(), m_engine, stage);
636+
builder2.addBlock("data_showlist");
637+
builder2.addEntityField("LIST", list2);
638+
639+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
640+
641+
// Missing monitors should be created
642+
builder1.run();
643+
644+
Monitor *monitor1 = list1->monitor();
645+
ASSERT_TRUE(monitor1);
646+
ASSERT_TRUE(monitor1->visible());
647+
648+
builder2.run();
649+
650+
Monitor *monitor2 = list2->monitor();
651+
652+
ASSERT_TRUE(monitor2);
653+
ASSERT_TRUE(monitor1->visible());
654+
ASSERT_TRUE(monitor2->visible());
655+
}
656+
657+
TEST_F(ListBlocksTest, ShowList_Local_Existent)
658+
{
659+
auto stage = std::make_shared<Stage>();
660+
auto sprite = std::make_shared<Sprite>();
661+
662+
auto list1 = std::make_shared<List>("a", "list1");
663+
list1->append("item1");
664+
list1->append("item2");
665+
sprite->addList(list1);
666+
auto list2 = std::make_shared<List>("b", "list2");
667+
list2->append("Hello");
668+
list2->append("world");
669+
sprite->addList(list2);
670+
671+
auto monitor1 = std::make_shared<Monitor>("monitor", "data_listcontents");
672+
monitor1->block()->addField(std::make_shared<Field>("LIST", list1->name(), list1));
673+
monitor1->setVisible(true);
674+
675+
auto monitor2 = std::make_shared<Monitor>("monitor", "data_listcontents");
676+
monitor2->block()->addField(std::make_shared<Field>("LIST", list2->name(), list2));
677+
monitor2->setVisible(false);
678+
679+
m_engine->setMonitors({ monitor1, monitor2 });
680+
list1->setMonitor(monitor1.get());
681+
list2->setMonitor(monitor2.get());
682+
683+
m_engine->setTargets({ stage, sprite });
684+
685+
ScriptBuilder builder1(m_extension.get(), m_engine, sprite);
686+
builder1.addBlock("data_showlist");
687+
builder1.addEntityField("LIST", list1);
688+
689+
ScriptBuilder builder2(m_extension.get(), m_engine, sprite);
690+
builder2.addBlock("data_showlist");
691+
builder2.addEntityField("LIST", list2);
692+
693+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
694+
695+
builder1.run();
696+
ASSERT_TRUE(monitor1->visible());
697+
ASSERT_FALSE(monitor2->visible());
698+
699+
builder2.run();
700+
ASSERT_TRUE(monitor1->visible());
701+
ASSERT_TRUE(monitor2->visible());
702+
}
703+
704+
TEST_F(ListBlocksTest, ShowList_Local_Nonexistent)
705+
{
706+
auto stage = std::make_shared<Stage>();
707+
auto sprite = std::make_shared<Sprite>();
708+
709+
auto list1 = std::make_shared<List>("a", "list1");
710+
list1->append("item1");
711+
list1->append("item2");
712+
sprite->addList(list1);
713+
auto list2 = std::make_shared<List>("b", "list2");
714+
list2->append("Hello");
715+
list2->append("world");
716+
sprite->addList(list2);
717+
718+
m_engine->setTargets({ stage, sprite });
719+
720+
ScriptBuilder builder1(m_extension.get(), m_engine, sprite);
721+
builder1.addBlock("data_showlist");
722+
builder1.addEntityField("LIST", list1);
723+
724+
ScriptBuilder builder2(m_extension.get(), m_engine, sprite);
725+
builder2.addBlock("data_showlist");
726+
builder2.addEntityField("LIST", list2);
727+
728+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
729+
m_engine->run();
730+
731+
// Missing monitors should be created
732+
builder1.run();
733+
734+
Monitor *monitor1 = list1->monitor();
735+
ASSERT_TRUE(monitor1);
736+
ASSERT_TRUE(monitor1->visible());
737+
738+
builder2.run();
739+
740+
Monitor *monitor2 = list2->monitor();
741+
742+
ASSERT_TRUE(monitor2);
743+
ASSERT_TRUE(monitor1->visible());
744+
ASSERT_TRUE(monitor2->visible());
745+
}
746+
747+
TEST_F(ListBlocksTest, ShowList_Local_FromClone)
748+
{
749+
auto stage = std::make_shared<Stage>();
750+
auto sprite = std::make_shared<Sprite>();
751+
752+
auto list1 = std::make_shared<List>("a", "list1");
753+
list1->append("item1");
754+
list1->append("item2");
755+
sprite->addList(list1);
756+
auto list2 = std::make_shared<List>("b", "list2");
757+
list2->append("Hello");
758+
list2->append("world");
759+
sprite->addList(list2);
760+
761+
auto monitor1 = std::make_shared<Monitor>("monitor", "data_listcontents");
762+
monitor1->block()->addField(std::make_shared<Field>("LIST", list1->name(), list1));
763+
monitor1->setVisible(true);
764+
765+
auto monitor2 = std::make_shared<Monitor>("monitor", "data_listcontents");
766+
monitor2->block()->addField(std::make_shared<Field>("LIST", list2->name(), list2));
767+
monitor2->setVisible(false);
768+
769+
m_engine->setMonitors({ monitor1, monitor2 });
770+
list1->setMonitor(monitor1.get());
771+
list2->setMonitor(monitor2.get());
772+
773+
sprite->setEngine(m_engine);
774+
auto clone = sprite->clone();
775+
776+
m_engine->setTargets({ stage, sprite, clone });
777+
778+
ScriptBuilder builder1(m_extension.get(), m_engine, clone);
779+
builder1.addBlock("data_showlist");
780+
builder1.addEntityField("LIST", list1);
781+
Block *hat1 = builder1.currentBlock()->parent();
782+
783+
ScriptBuilder builder2(m_extension.get(), m_engine, clone);
784+
builder2.addBlock("data_showlist");
785+
builder2.addEntityField("LIST", list2);
786+
Block *hat2 = builder2.currentBlock()->parent();
787+
788+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
789+
790+
// The clone root lists should be used
791+
builder1.run();
792+
ASSERT_TRUE(monitor1->visible());
793+
ASSERT_FALSE(monitor2->visible());
794+
795+
builder2.run();
796+
ASSERT_TRUE(monitor1->visible());
797+
ASSERT_TRUE(monitor2->visible());
798+
799+
ASSERT_FALSE(clone->listAt(0)->monitor());
800+
ASSERT_FALSE(clone->listAt(1)->monitor());
801+
}

0 commit comments

Comments
 (0)