Skip to content

Commit 5709034

Browse files
committed
Create missing monitors in Engine
1 parent 2ce9880 commit 5709034

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

src/engine/internal/engine.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <scratchcpp/costume.h>
1818
#include <scratchcpp/keyevent.h>
1919
#include <scratchcpp/sound.h>
20+
#include <scratchcpp/monitor.h>
21+
#include <scratchcpp/rect.h>
2022
#include <cassert>
2123
#include <iostream>
2224

@@ -25,6 +27,8 @@
2527
#include "timer.h"
2628
#include "clock.h"
2729
#include "blocks/standardblocks.h"
30+
#include "blocks/variableblocks.h"
31+
#include "blocks/listblocks.h"
2832

2933
using namespace libscratchcpp;
3034

@@ -838,6 +842,9 @@ void Engine::setTargets(const std::vector<std::shared_ptr<Target>> &newTargets)
838842

839843
// Sort the executable targets by layer order
840844
std::sort(m_executableTargets.begin(), m_executableTargets.end(), [](Target *t1, Target *t2) { return t1->layerOrder() < t2->layerOrder(); });
845+
846+
// Create missing monitors
847+
createMissingMonitors();
841848
}
842849

843850
Target *Engine::targetAt(int index) const
@@ -973,6 +980,9 @@ const std::vector<std::shared_ptr<Monitor>> &Engine::monitors() const
973980
void Engine::setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors)
974981
{
975982
m_monitors = newMonitors;
983+
984+
// Create missing monitors
985+
createMissingMonitors();
976986
}
977987

978988
const std::vector<std::string> &Engine::extensions() const
@@ -1250,6 +1260,76 @@ void Engine::removeExecutableClones()
12501260
m_executableTargets.erase(std::remove(m_executableTargets.begin(), m_executableTargets.end(), clone.get()), m_executableTargets.end());
12511261
}
12521262

1263+
void Engine::createMissingMonitors()
1264+
{
1265+
// This is called when setting targets and monitors because we never know in which order they're set
1266+
// If there aren't any targets yet, quit
1267+
if (m_targets.empty())
1268+
return;
1269+
1270+
for (auto target : m_targets) {
1271+
// Read all variables
1272+
const auto &variables = target->variables();
1273+
1274+
for (auto variable : variables) {
1275+
// Find the monitor for this variable
1276+
auto it = std::find_if(m_monitors.begin(), m_monitors.end(), [variable](std::shared_ptr<Monitor> monitor) {
1277+
// TODO: Move the opcode out of Engine
1278+
return monitor->opcode() == "data_variable" && monitor->id() == variable->id();
1279+
});
1280+
1281+
// If it doesn't exist, create it
1282+
if (it == m_monitors.end()) {
1283+
auto monitor = std::make_shared<Monitor>(variable->id(), "data_variable");
1284+
// TODO: Move field information out of Engine
1285+
auto field = std::make_shared<Field>("VARIABLE", variable->name(), variable);
1286+
field->setFieldId(VariableBlocks::VARIABLE);
1287+
monitor->block()->addField(field);
1288+
1289+
addVarOrListMonitor(monitor, target.get());
1290+
}
1291+
}
1292+
1293+
// Read all lists
1294+
const auto &lists = target->lists();
1295+
1296+
for (auto list : lists) {
1297+
// Find the monitor for this list
1298+
auto it = std::find_if(m_monitors.begin(), m_monitors.end(), [list](std::shared_ptr<Monitor> monitor) {
1299+
// TODO: Move the opcode out of Engine
1300+
return monitor->opcode() == "data_listcontents" && monitor->id() == list->id();
1301+
});
1302+
1303+
// If it doesn't exist, create it
1304+
if (it == m_monitors.end()) {
1305+
auto monitor = std::make_shared<Monitor>(list->id(), "data_listcontents");
1306+
// TODO: Move field information out of Engine
1307+
auto field = std::make_shared<Field>("LIST", list->name(), list);
1308+
field->setFieldId(ListBlocks::LIST);
1309+
monitor->block()->addField(field);
1310+
1311+
addVarOrListMonitor(monitor, target.get());
1312+
}
1313+
}
1314+
}
1315+
}
1316+
1317+
void Engine::addVarOrListMonitor(std::shared_ptr<Monitor> monitor, Target *target)
1318+
{
1319+
if (!target->isStage())
1320+
monitor->setSprite(dynamic_cast<Sprite *>(target));
1321+
1322+
monitor->setVisible(false);
1323+
1324+
// Auto-position the monitor
1325+
// TODO: Get width and height from renderer
1326+
Rect rect = Monitor::getInitialPosition(m_monitors, monitor->width(), monitor->height());
1327+
monitor->setX(rect.left());
1328+
monitor->setY(rect.top());
1329+
1330+
m_monitors.push_back(monitor);
1331+
}
1332+
12531333
void Engine::updateFrameDuration()
12541334
{
12551335
m_frameDuration = std::chrono::milliseconds(static_cast<long>(1000 / m_fps));

src/engine/internal/engine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class Engine : public IEngine
167167
void finalize();
168168
void deleteClones();
169169
void removeExecutableClones();
170+
void createMissingMonitors();
171+
void addVarOrListMonitor(std::shared_ptr<Monitor> monitor, Target *target);
170172
std::shared_ptr<Block> getBlock(const std::string &id);
171173
std::shared_ptr<Variable> getVariable(const std::string &id);
172174
std::shared_ptr<List> getList(const std::string &id);

test/engine/engine_test.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <scratchcpp/list.h>
88
#include <scratchcpp/keyevent.h>
99
#include <scratchcpp/monitor.h>
10+
#include <scratchcpp/field.h>
1011
#include <scratch/sound_p.h>
1112
#include <timermock.h>
1213
#include <clockmock.h>
@@ -19,6 +20,10 @@
1920
#include "engine/internal/engine.h"
2021
#include "engine/internal/clock.h"
2122

23+
// TODO: Remove this
24+
#include "blocks/variableblocks.h"
25+
#include "blocks/listblocks.h"
26+
2227
using namespace libscratchcpp;
2328

2429
using ::testing::Return;
@@ -1141,6 +1146,104 @@ TEST(EngineTest, Monitors)
11411146
ASSERT_EQ(engine.monitors(), std::vector<std::shared_ptr<Monitor>>({ m1, m2, m3 }));
11421147
}
11431148

1149+
TEST(EngineTest, CreateMissingMonitors)
1150+
{
1151+
auto var1 = std::make_shared<Variable>("a", "var1");
1152+
auto var2 = std::make_shared<Variable>("b", "var2");
1153+
auto var3 = std::make_shared<Variable>("c", "var3");
1154+
auto var4 = std::make_shared<Variable>("d", "var4");
1155+
auto var5 = std::make_shared<Variable>("e", "var5");
1156+
auto list1 = std::make_shared<List>("f", "list1");
1157+
auto list2 = std::make_shared<List>("g", "list2");
1158+
auto target1 = std::make_shared<Stage>();
1159+
auto target2 = std::make_shared<Sprite>();
1160+
target1->addVariable(var1);
1161+
target1->addVariable(var2);
1162+
target1->addList(list1);
1163+
target2->addVariable(var3);
1164+
target2->addVariable(var4);
1165+
target2->addVariable(var5);
1166+
target2->addList(list2);
1167+
1168+
auto m1 = std::make_shared<Monitor>(var1->id(), "data_variable");
1169+
auto m2 = std::make_shared<Monitor>(var3->id(), "data_variable");
1170+
auto m3 = std::make_shared<Monitor>(list2->id(), "data_listcontents");
1171+
1172+
auto checkVariableMonitor = [](std::shared_ptr<Monitor> monitor, std::shared_ptr<Variable> var) {
1173+
auto block = monitor->block();
1174+
ASSERT_EQ(monitor->id(), var->id());
1175+
ASSERT_EQ(monitor->opcode(), "data_variable");
1176+
ASSERT_EQ(monitor->mode(), Monitor::Mode::Default);
1177+
ASSERT_FALSE(monitor->visible());
1178+
ASSERT_EQ(block->fields().size(), 1);
1179+
1180+
auto field = block->fieldAt(0);
1181+
ASSERT_EQ(field->name(), "VARIABLE");
1182+
ASSERT_EQ(field->fieldId(), VariableBlocks::VARIABLE);
1183+
ASSERT_EQ(field->value(), var->name());
1184+
ASSERT_EQ(field->valuePtr(), var);
1185+
1186+
if (var->target()->isStage())
1187+
ASSERT_EQ(monitor->sprite(), nullptr);
1188+
else
1189+
ASSERT_EQ(monitor->sprite(), dynamic_cast<Sprite *>(var->target()));
1190+
};
1191+
1192+
auto checkListMonitor = [](std::shared_ptr<Monitor> monitor, std::shared_ptr<List> list) {
1193+
auto block = monitor->block();
1194+
ASSERT_EQ(monitor->id(), list->id());
1195+
ASSERT_EQ(monitor->opcode(), "data_listcontents");
1196+
ASSERT_EQ(monitor->mode(), Monitor::Mode::Default);
1197+
ASSERT_FALSE(monitor->visible());
1198+
ASSERT_EQ(block->fields().size(), 1);
1199+
1200+
auto field = block->fieldAt(0);
1201+
ASSERT_EQ(field->name(), "LIST");
1202+
ASSERT_EQ(field->fieldId(), ListBlocks::LIST);
1203+
ASSERT_EQ(field->value(), list->name());
1204+
ASSERT_EQ(field->valuePtr(), list);
1205+
1206+
if (list->target()->isStage())
1207+
ASSERT_EQ(monitor->sprite(), nullptr);
1208+
else
1209+
ASSERT_EQ(monitor->sprite(), dynamic_cast<Sprite *>(list->target()));
1210+
};
1211+
1212+
// Set monitors after setting targets
1213+
{
1214+
Engine engine;
1215+
engine.setTargets({ target1, target2 });
1216+
engine.setMonitors({ m1, m2, m3 });
1217+
1218+
const auto &monitors = engine.monitors();
1219+
ASSERT_EQ(monitors.size(), 7);
1220+
ASSERT_EQ(monitors[0], m1);
1221+
ASSERT_EQ(monitors[1], m2);
1222+
ASSERT_EQ(monitors[2], m3);
1223+
checkVariableMonitor(monitors[3], var2);
1224+
checkListMonitor(monitors[4], list1);
1225+
checkVariableMonitor(monitors[5], var4);
1226+
checkVariableMonitor(monitors[6], var5);
1227+
}
1228+
1229+
// Set monitors before setting targets
1230+
{
1231+
Engine engine;
1232+
engine.setMonitors({ m1, m2, m3 });
1233+
engine.setTargets({ target1, target2 });
1234+
1235+
const auto &monitors = engine.monitors();
1236+
ASSERT_EQ(monitors.size(), 7);
1237+
ASSERT_EQ(monitors[0], m1);
1238+
ASSERT_EQ(monitors[1], m2);
1239+
ASSERT_EQ(monitors[2], m3);
1240+
checkVariableMonitor(monitors[3], var2);
1241+
checkListMonitor(monitors[4], list1);
1242+
checkVariableMonitor(monitors[5], var4);
1243+
checkVariableMonitor(monitors[6], var5);
1244+
}
1245+
}
1246+
11441247
TEST(EngineTest, Clones)
11451248
{
11461249
Project p("clones.sb3");

0 commit comments

Comments
 (0)