Skip to content

Commit 2c01b5d

Browse files
authored
Merge pull request #579 from scratchcpp/show_missing_monitor
Create missing variable and list monitors before setting visibility
2 parents e98718e + 9da9b7f commit 2c01b5d

File tree

10 files changed

+161
-156
lines changed

10 files changed

+161
-156
lines changed

include/scratchcpp/iengine.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,12 @@ class LIBSCRATCHCPP_EXPORT IEngine
360360
/*! Sets the list of monitors. */
361361
virtual void setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors) = 0;
362362

363+
/*! Creates a monitor for the given variable if it's missing and returns it. */
364+
virtual Monitor *createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) = 0;
365+
366+
/*! Creates a monitor for the given list if it's missing and returns it. */
367+
virtual Monitor *createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) = 0;
368+
363369
/*! Emits when a monitor is added. */
364370
virtual sigslot::signal<Monitor *> &monitorAdded() = 0;
365371

src/blocks/listblocks.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,23 @@ void ListBlocks::compileHideList(Compiler *compiler)
142142
compiler->addFunctionCall(&hideList);
143143
}
144144

145-
void ListBlocks::setListVisible(std::shared_ptr<List> list, bool visible)
145+
void ListBlocks::setListVisible(std::shared_ptr<List> list, bool visible, IEngine *engine)
146146
{
147147
if (list) {
148-
assert(list->monitor());
149-
list->monitor()->setVisible(visible);
148+
Monitor *monitor = list->monitor();
149+
150+
if (!monitor)
151+
monitor = engine->createListMonitor(list, "data_listcontents", "LIST", LIST, &compileListContents);
152+
153+
monitor->setVisible(visible);
150154
}
151155
}
152156

153157
unsigned int ListBlocks::showGlobalList(VirtualMachine *vm)
154158
{
155159
if (Stage *target = vm->engine()->stage()) {
156160
int index = target->findListById(vm->getInput(0, 1)->toString());
157-
setListVisible(target->listAt(index), true);
161+
setListVisible(target->listAt(index), true, vm->engine());
158162
}
159163

160164
return 1;
@@ -166,10 +170,10 @@ unsigned int ListBlocks::showList(VirtualMachine *vm)
166170
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
167171
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root list
168172
int index = sprite->findListById(vm->getInput(0, 1)->toString());
169-
setListVisible(sprite->listAt(index), true);
173+
setListVisible(sprite->listAt(index), true, vm->engine());
170174
} else {
171175
int index = target->findListById(vm->getInput(0, 1)->toString());
172-
setListVisible(target->listAt(index), true);
176+
setListVisible(target->listAt(index), true, vm->engine());
173177
}
174178
}
175179

@@ -180,7 +184,7 @@ unsigned int ListBlocks::hideGlobalList(VirtualMachine *vm)
180184
{
181185
if (Stage *target = vm->engine()->stage()) {
182186
int index = target->findListById(vm->getInput(0, 1)->toString());
183-
setListVisible(target->listAt(index), false);
187+
setListVisible(target->listAt(index), false, vm->engine());
184188
}
185189

186190
return 1;
@@ -192,10 +196,10 @@ unsigned int ListBlocks::hideList(VirtualMachine *vm)
192196
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
193197
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root list
194198
int index = sprite->findListById(vm->getInput(0, 1)->toString());
195-
setListVisible(sprite->listAt(index), false);
199+
setListVisible(sprite->listAt(index), false, vm->engine());
196200
} else {
197201
int index = target->findListById(vm->getInput(0, 1)->toString());
198-
setListVisible(target->listAt(index), false);
202+
setListVisible(target->listAt(index), false, vm->engine());
199203
}
200204
}
201205

src/blocks/listblocks.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class ListBlocks : public IBlockSection
4343
static void compileShowList(Compiler *compiler);
4444
static void compileHideList(Compiler *compiler);
4545

46-
static void setListVisible(std::shared_ptr<List> list, bool visible);
46+
static void setListVisible(std::shared_ptr<List> list, bool visible, IEngine *engine);
4747

4848
static unsigned int showGlobalList(VirtualMachine *vm);
4949
static unsigned int showList(VirtualMachine *vm);

src/blocks/variableblocks.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,23 @@ void VariableBlocks::compileHideVariable(Compiler *compiler)
8888
compiler->addFunctionCall(&hideVariable);
8989
}
9090

91-
void VariableBlocks::setVarVisible(std::shared_ptr<Variable> var, bool visible)
91+
void VariableBlocks::setVarVisible(std::shared_ptr<Variable> var, bool visible, IEngine *engine)
9292
{
9393
if (var) {
94-
assert(var->monitor());
95-
var->monitor()->setVisible(visible);
94+
Monitor *monitor = var->monitor();
95+
96+
if (!monitor)
97+
monitor = engine->createVariableMonitor(var, "data_variable", "VARIABLE", VARIABLE, &compileVariable);
98+
99+
monitor->setVisible(visible);
96100
}
97101
}
98102

99103
unsigned int VariableBlocks::showGlobalVariable(VirtualMachine *vm)
100104
{
101105
if (Stage *target = vm->engine()->stage()) {
102106
int index = target->findVariableById(vm->getInput(0, 1)->toString());
103-
setVarVisible(target->variableAt(index), true);
107+
setVarVisible(target->variableAt(index), true, vm->engine());
104108
}
105109

106110
return 1;
@@ -112,10 +116,10 @@ unsigned int VariableBlocks::showVariable(VirtualMachine *vm)
112116
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
113117
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root variable
114118
int index = sprite->findVariableById(vm->getInput(0, 1)->toString());
115-
setVarVisible(sprite->variableAt(index), true);
119+
setVarVisible(sprite->variableAt(index), true, vm->engine());
116120
} else {
117121
int index = target->findVariableById(vm->getInput(0, 1)->toString());
118-
setVarVisible(target->variableAt(index), true);
122+
setVarVisible(target->variableAt(index), true, vm->engine());
119123
}
120124
}
121125

@@ -126,7 +130,7 @@ unsigned int VariableBlocks::hideGlobalVariable(VirtualMachine *vm)
126130
{
127131
if (Stage *target = vm->engine()->stage()) {
128132
int index = target->findVariableById(vm->getInput(0, 1)->toString());
129-
setVarVisible(target->variableAt(index), false);
133+
setVarVisible(target->variableAt(index), false, vm->engine());
130134
}
131135

132136
return 1;
@@ -138,10 +142,10 @@ unsigned int VariableBlocks::hideVariable(VirtualMachine *vm)
138142
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
139143
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root variable
140144
int index = sprite->findVariableById(vm->getInput(0, 1)->toString());
141-
setVarVisible(sprite->variableAt(index), false);
145+
setVarVisible(sprite->variableAt(index), false, vm->engine());
142146
} else {
143147
int index = target->findVariableById(vm->getInput(0, 1)->toString());
144-
setVarVisible(target->variableAt(index), false);
148+
setVarVisible(target->variableAt(index), false, vm->engine());
145149
}
146150
}
147151

src/blocks/variableblocks.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class VariableBlocks : public IBlockSection
3434
static void compileShowVariable(Compiler *compiler);
3535
static void compileHideVariable(Compiler *compiler);
3636

37-
static void setVarVisible(std::shared_ptr<Variable> var, bool visible);
37+
static void setVarVisible(std::shared_ptr<Variable> var, bool visible, IEngine *engine);
3838

3939
static unsigned int showGlobalVariable(VirtualMachine *vm);
4040
static unsigned int showVariable(VirtualMachine *vm);

src/engine/internal/engine.cpp

Lines changed: 89 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
#include "clock.h"
3030
#include "audio/iaudioengine.h"
3131
#include "blocks/standardblocks.h"
32-
#include "blocks/variableblocks.h"
33-
#include "blocks/listblocks.h"
3432
#include "scratch/monitor_p.h"
3533

3634
using namespace libscratchcpp;
@@ -314,56 +312,8 @@ void Engine::compile()
314312
// Compile monitor blocks to bytecode
315313
std::cout << "Compiling stage monitors..." << std::endl;
316314

317-
for (auto monitor : m_monitors) {
318-
Target *target = monitor->sprite() ? dynamic_cast<Target *>(monitor->sprite()) : stage();
319-
Compiler compiler(this, target);
320-
auto block = monitor->block();
321-
auto section = blockSection(block->opcode());
322-
auto container = blockSectionContainer(block->opcode());
323-
324-
if (container) {
325-
MonitorNameFunc nameFunc = container->resolveMonitorNameFunc(block->opcode());
326-
327-
if (nameFunc)
328-
monitor->setName(nameFunc(block.get()));
329-
330-
MonitorChangeFunc changeFunc = container->resolveMonitorChangeFunc(block->opcode());
331-
monitor->setValueChangeFunction(changeFunc);
332-
}
333-
334-
if (section) {
335-
auto script = std::make_shared<Script>(target, block, this);
336-
monitor->setScript(script);
337-
compiler.init();
338-
compiler.setBlock(block);
339-
340-
if (block->compileFunction())
341-
block->compile(&compiler);
342-
else
343-
std::cout << "warning: monitor block doesn't have a compile function: " << block->opcode() << std::endl;
344-
345-
// Workaround for register leak warning spam: pause the script after getting the monitor value
346-
compiler.addFunctionCall([](VirtualMachine *vm) -> unsigned int {
347-
vm->stop(false, false, false);
348-
return 0;
349-
});
350-
351-
compiler.end();
352-
353-
script->setBytecode(compiler.bytecode());
354-
script->setConstValues(compiler.constValues());
355-
script->setVariables(compiler.variables());
356-
script->setLists(compiler.lists());
357-
} else {
358-
std::cout << "warning: unsupported monitor block: " << block->opcode() << std::endl;
359-
m_unsupportedBlocks.insert(block->opcode());
360-
}
361-
362-
const auto &unsupportedBlocks = compiler.unsupportedBlocks();
363-
364-
for (const std::string &opcode : unsupportedBlocks)
365-
m_unsupportedBlocks.insert(opcode);
366-
}
315+
for (auto monitor : m_monitors)
316+
compileMonitor(monitor);
367317
}
368318

369319
void Engine::start()
@@ -1231,9 +1181,6 @@ void Engine::setTargets(const std::vector<std::shared_ptr<Target>> &newTargets)
12311181

12321182
// Sort the executable targets by layer order
12331183
std::sort(m_executableTargets.begin(), m_executableTargets.end(), [](Target *t1, Target *t2) { return t1->layerOrder() < t2->layerOrder(); });
1234-
1235-
// Create missing monitors
1236-
createMissingMonitors();
12371184
}
12381185

12391186
Target *Engine::targetAt(int index) const
@@ -1401,9 +1348,42 @@ void Engine::setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitor
14011348
m_monitors.push_back(monitor);
14021349
m_monitorAdded(monitor.get());
14031350
}
1351+
}
1352+
1353+
Monitor *Engine::createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction)
1354+
{
1355+
if (var->monitor())
1356+
return var->monitor();
1357+
else {
1358+
auto monitor = std::make_shared<Monitor>(var->id(), opcode);
1359+
auto field = std::make_shared<Field>(varFieldName, var->name(), var);
1360+
field->setFieldId(varFieldId);
1361+
monitor->block()->addField(field);
1362+
monitor->block()->setCompileFunction(compileFunction);
1363+
1364+
addVarOrListMonitor(monitor, var->target());
1365+
var->setMonitor(monitor.get());
1366+
compileMonitor(monitor);
1367+
return monitor.get();
1368+
}
1369+
}
14041370

1405-
// Create missing monitors
1406-
createMissingMonitors();
1371+
Monitor *Engine::createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction)
1372+
{
1373+
if (list->monitor())
1374+
return list->monitor();
1375+
else {
1376+
auto monitor = std::make_shared<Monitor>(list->id(), opcode);
1377+
auto field = std::make_shared<Field>(listFieldName, list->name(), list);
1378+
field->setFieldId(listFieldId);
1379+
monitor->block()->addField(field);
1380+
monitor->block()->setCompileFunction(compileFunction);
1381+
1382+
addVarOrListMonitor(monitor, list->target());
1383+
list->setMonitor(monitor.get());
1384+
compileMonitor(monitor);
1385+
return monitor.get();
1386+
}
14071387
}
14081388

14091389
sigslot::signal<Monitor *> &Engine::monitorAdded()
@@ -1759,6 +1739,58 @@ const std::unordered_set<std::string> &Engine::unsupportedBlocks() const
17591739
return m_unsupportedBlocks;
17601740
}
17611741

1742+
void Engine::compileMonitor(std::shared_ptr<Monitor> monitor)
1743+
{
1744+
Target *target = monitor->sprite() ? dynamic_cast<Target *>(monitor->sprite()) : stage();
1745+
Compiler compiler(this, target);
1746+
auto block = monitor->block();
1747+
auto section = blockSection(block->opcode());
1748+
auto container = blockSectionContainer(block->opcode());
1749+
1750+
if (container) {
1751+
MonitorNameFunc nameFunc = container->resolveMonitorNameFunc(block->opcode());
1752+
1753+
if (nameFunc)
1754+
monitor->setName(nameFunc(block.get()));
1755+
1756+
MonitorChangeFunc changeFunc = container->resolveMonitorChangeFunc(block->opcode());
1757+
monitor->setValueChangeFunction(changeFunc);
1758+
}
1759+
1760+
if (section) {
1761+
auto script = std::make_shared<Script>(target, block, this);
1762+
monitor->setScript(script);
1763+
compiler.init();
1764+
compiler.setBlock(block);
1765+
1766+
if (block->compileFunction())
1767+
block->compile(&compiler);
1768+
else
1769+
std::cout << "warning: monitor block doesn't have a compile function: " << block->opcode() << std::endl;
1770+
1771+
// Workaround for register leak warning spam: pause the script after getting the monitor value
1772+
compiler.addFunctionCall([](VirtualMachine *vm) -> unsigned int {
1773+
vm->stop(false, false, false);
1774+
return 0;
1775+
});
1776+
1777+
compiler.end();
1778+
1779+
script->setBytecode(compiler.bytecode());
1780+
script->setConstValues(compiler.constValues());
1781+
script->setVariables(compiler.variables());
1782+
script->setLists(compiler.lists());
1783+
} else {
1784+
std::cout << "warning: unsupported monitor block: " << block->opcode() << std::endl;
1785+
m_unsupportedBlocks.insert(block->opcode());
1786+
}
1787+
1788+
const auto &unsupportedBlocks = compiler.unsupportedBlocks();
1789+
1790+
for (const std::string &opcode : unsupportedBlocks)
1791+
m_unsupportedBlocks.insert(opcode);
1792+
}
1793+
17621794
void Engine::finalize()
17631795
{
17641796
m_eventLoopMutex.lock();
@@ -1797,60 +1829,6 @@ void Engine::removeExecutableClones()
17971829
m_executableTargets.erase(std::remove(m_executableTargets.begin(), m_executableTargets.end(), clone.get()), m_executableTargets.end());
17981830
}
17991831

1800-
void Engine::createMissingMonitors()
1801-
{
1802-
// This is called when setting targets and monitors because we never know in which order they're set
1803-
// If there aren't any targets yet, quit
1804-
if (m_targets.empty())
1805-
return;
1806-
1807-
for (auto target : m_targets) {
1808-
// Read all variables
1809-
const auto &variables = target->variables();
1810-
1811-
for (auto variable : variables) {
1812-
// Find the monitor for this variable
1813-
auto it = std::find_if(m_monitors.begin(), m_monitors.end(), [variable](std::shared_ptr<Monitor> monitor) {
1814-
// TODO: Move the opcode out of Engine
1815-
return monitor->opcode() == "data_variable" && monitor->id() == variable->id();
1816-
});
1817-
1818-
// If it doesn't exist, create it
1819-
if (it == m_monitors.end()) {
1820-
auto monitor = std::make_shared<Monitor>(variable->id(), "data_variable");
1821-
// TODO: Move field information out of Engine
1822-
auto field = std::make_shared<Field>("VARIABLE", variable->name(), variable);
1823-
field->setFieldId(VariableBlocks::VARIABLE);
1824-
monitor->block()->addField(field);
1825-
1826-
addVarOrListMonitor(monitor, target.get());
1827-
}
1828-
}
1829-
1830-
// Read all lists
1831-
const auto &lists = target->lists();
1832-
1833-
for (auto list : lists) {
1834-
// Find the monitor for this list
1835-
auto it = std::find_if(m_monitors.begin(), m_monitors.end(), [list](std::shared_ptr<Monitor> monitor) {
1836-
// TODO: Move the opcode out of Engine
1837-
return monitor->opcode() == "data_listcontents" && monitor->id() == list->id();
1838-
});
1839-
1840-
// If it doesn't exist, create it
1841-
if (it == m_monitors.end()) {
1842-
auto monitor = std::make_shared<Monitor>(list->id(), "data_listcontents");
1843-
// TODO: Move field information out of Engine
1844-
auto field = std::make_shared<Field>("LIST", list->name(), list);
1845-
field->setFieldId(ListBlocks::LIST);
1846-
monitor->block()->addField(field);
1847-
1848-
addVarOrListMonitor(monitor, target.get());
1849-
}
1850-
}
1851-
}
1852-
}
1853-
18541832
void Engine::addVarOrListMonitor(std::shared_ptr<Monitor> monitor, Target *target)
18551833
{
18561834
if (!target->isStage())

0 commit comments

Comments
 (0)