Skip to content

Commit 2431956

Browse files
committed
Implement data_showvariable block
1 parent 4b46a89 commit 2431956

File tree

3 files changed

+250
-1
lines changed

3 files changed

+250
-1
lines changed

src/blocks/variableblocks.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <scratchcpp/input.h>
88
#include <scratchcpp/field.h>
99
#include <scratchcpp/variable.h>
10+
#include <scratchcpp/target.h>
11+
#include <scratchcpp/monitor.h>
1012

1113
#include "variableblocks.h"
1214

@@ -33,6 +35,7 @@ void VariableBlocks::registerBlocks(IEngine *engine)
3335
engine->addCompileFunction(this, "data_variable", &compileVariable);
3436
engine->addCompileFunction(this, "data_setvariableto", &compileSetVariableTo);
3537
engine->addCompileFunction(this, "data_changevariableby", &compileChangeVariableBy);
38+
engine->addCompileFunction(this, "data_showvariable", &compileShowVariable);
3639

3740
// Monitor names
3841
engine->addMonitorNameFunction(this, "data_variable", &variableMonitorName);
@@ -79,6 +82,19 @@ CompilerValue *VariableBlocks::compileChangeVariableBy(Compiler *compiler)
7982
return nullptr;
8083
}
8184

85+
CompilerValue *VariableBlocks::compileShowVariable(Compiler *compiler)
86+
{
87+
Field *field = compiler->field("VARIABLE");
88+
assert(field);
89+
Variable *var = static_cast<Variable *>(field->valuePtr().get());
90+
assert(var);
91+
92+
CompilerConstant *varPtr = compiler->addConstValue(var);
93+
compiler->addTargetFunctionCall("data_showvariable", Compiler::StaticType::Void, { Compiler::StaticType::Pointer }, { varPtr });
94+
95+
return nullptr;
96+
}
97+
8298
const std::string &VariableBlocks::variableMonitorName(Block *block)
8399
{
84100
static const std::string empty = "";
@@ -108,3 +124,20 @@ void VariableBlocks::changeVariableMonitorValue(Block *block, const Value &newVa
108124
if (var)
109125
var->setValue(newValue);
110126
}
127+
128+
extern "C" void data_showvariable(Target *target, Variable *variable)
129+
{
130+
Monitor *monitor = variable->monitor();
131+
132+
if (!monitor) {
133+
/*
134+
* We need shared_ptr.
135+
* Since this case doesn't occur frequently,
136+
* we can look up the variable by ID.
137+
*/
138+
auto index = target->findVariableById(variable->id());
139+
monitor = target->engine()->createVariableMonitor(target->variableAt(index), "data_variable", "VARIABLE", -1, &VariableBlocks::compileVariable);
140+
}
141+
142+
monitor->setVisible(true);
143+
}

src/blocks/variableblocks.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ class VariableBlocks : public IExtension
1616

1717
void registerBlocks(IEngine *engine) override;
1818

19-
private:
19+
// NOTE: Must be public for show/hide monitor blocks
2020
static CompilerValue *compileVariable(Compiler *compiler);
21+
22+
private:
2123
static CompilerValue *compileSetVariableTo(Compiler *compiler);
2224
static CompilerValue *compileChangeVariableBy(Compiler *compiler);
25+
static CompilerValue *compileShowVariable(Compiler *compiler);
2326

2427
static const std::string &variableMonitorName(Block *block);
2528
static void changeVariableMonitorValue(Block *block, const Value &newValue);

test/blocks/variable_blocks_test.cpp

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#include <scratchcpp/project.h>
22
#include <scratchcpp/sprite.h>
3+
#include <scratchcpp/stage.h>
34
#include <scratchcpp/variable.h>
45
#include <scratchcpp/list.h>
56
#include <scratchcpp/compiler.h>
67
#include <scratchcpp/monitor.h>
78
#include <scratchcpp/block.h>
9+
#include <scratchcpp/field.h>
810
#include <scratchcpp/test/scriptbuilder.h>
911
#include <enginemock.h>
1012

@@ -146,3 +148,214 @@ TEST_F(VariableBlocksTest, ChangeVariableBy)
146148
ASSERT_EQ(var1->value(), 850.57);
147149
ASSERT_EQ(var2->value(), -7.5);
148150
}
151+
152+
TEST_F(VariableBlocksTest, ShowVariable_Global_Existent)
153+
{
154+
auto stage = std::make_shared<Stage>();
155+
auto var1 = std::make_shared<Variable>("a", "var1", 835.21);
156+
stage->addVariable(var1);
157+
auto var2 = std::make_shared<Variable>("b", "var2", "Hello world");
158+
stage->addVariable(var2);
159+
160+
auto monitor1 = std::make_shared<Monitor>("monitor", "data_variable");
161+
monitor1->block()->addField(std::make_shared<Field>("VARIABLE", var1->name(), var1));
162+
monitor1->setVisible(true);
163+
164+
auto monitor2 = std::make_shared<Monitor>("monitor", "data_variable");
165+
monitor2->block()->addField(std::make_shared<Field>("VARIABLE", var2->name(), var2));
166+
monitor2->setVisible(false);
167+
168+
m_engine->setMonitors({ monitor1, monitor2 });
169+
var1->setMonitor(monitor1.get());
170+
var2->setMonitor(monitor2.get());
171+
172+
m_engine->setTargets({ stage });
173+
174+
ScriptBuilder builder1(m_extension.get(), m_engine, stage);
175+
builder1.addBlock("data_showvariable");
176+
builder1.addEntityField("VARIABLE", var1);
177+
178+
ScriptBuilder builder2(m_extension.get(), m_engine, stage);
179+
builder2.addBlock("data_showvariable");
180+
builder2.addEntityField("VARIABLE", var2);
181+
182+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
183+
184+
builder1.run();
185+
ASSERT_TRUE(monitor1->visible());
186+
ASSERT_FALSE(monitor2->visible());
187+
188+
builder2.run();
189+
ASSERT_TRUE(monitor1->visible());
190+
ASSERT_TRUE(monitor2->visible());
191+
}
192+
193+
TEST_F(VariableBlocksTest, ShowVariable_Global_Nonexistent)
194+
{
195+
auto stage = std::make_shared<Stage>();
196+
auto var1 = std::make_shared<Variable>("a", "var1", 835.21);
197+
stage->addVariable(var1);
198+
auto var2 = std::make_shared<Variable>("b", "var2", "Hello world");
199+
stage->addVariable(var2);
200+
201+
m_engine->setTargets({ stage });
202+
203+
ScriptBuilder builder1(m_extension.get(), m_engine, stage);
204+
builder1.addBlock("data_showvariable");
205+
builder1.addEntityField("VARIABLE", var1);
206+
207+
ScriptBuilder builder2(m_extension.get(), m_engine, stage);
208+
builder2.addBlock("data_showvariable");
209+
builder2.addEntityField("VARIABLE", var2);
210+
211+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
212+
213+
// Missing monitors should be created
214+
builder1.run();
215+
216+
Monitor *monitor1 = var1->monitor();
217+
ASSERT_TRUE(monitor1);
218+
ASSERT_TRUE(monitor1->visible());
219+
220+
builder2.run();
221+
222+
Monitor *monitor2 = var2->monitor();
223+
224+
ASSERT_TRUE(monitor2);
225+
ASSERT_TRUE(monitor1->visible());
226+
ASSERT_TRUE(monitor2->visible());
227+
}
228+
229+
TEST_F(VariableBlocksTest, ShowVariable_Local_Existent)
230+
{
231+
auto stage = std::make_shared<Stage>();
232+
auto sprite = std::make_shared<Sprite>();
233+
234+
auto var1 = std::make_shared<Variable>("a", "var1", 835.21);
235+
sprite->addVariable(var1);
236+
auto var2 = std::make_shared<Variable>("b", "var2", "Hello world");
237+
sprite->addVariable(var2);
238+
239+
auto monitor1 = std::make_shared<Monitor>("monitor", "data_variable");
240+
monitor1->block()->addField(std::make_shared<Field>("VARIABLE", var1->name(), var1));
241+
monitor1->setVisible(true);
242+
243+
auto monitor2 = std::make_shared<Monitor>("monitor", "data_variable");
244+
monitor2->block()->addField(std::make_shared<Field>("VARIABLE", var2->name(), var2));
245+
monitor2->setVisible(false);
246+
247+
m_engine->setMonitors({ monitor1, monitor2 });
248+
var1->setMonitor(monitor1.get());
249+
var2->setMonitor(monitor2.get());
250+
251+
m_engine->setTargets({ stage, sprite });
252+
253+
ScriptBuilder builder1(m_extension.get(), m_engine, sprite);
254+
builder1.addBlock("data_showvariable");
255+
builder1.addEntityField("VARIABLE", var1);
256+
257+
ScriptBuilder builder2(m_extension.get(), m_engine, sprite);
258+
builder2.addBlock("data_showvariable");
259+
builder2.addEntityField("VARIABLE", var2);
260+
261+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
262+
263+
builder1.run();
264+
ASSERT_TRUE(monitor1->visible());
265+
ASSERT_FALSE(monitor2->visible());
266+
267+
builder2.run();
268+
ASSERT_TRUE(monitor1->visible());
269+
ASSERT_TRUE(monitor2->visible());
270+
}
271+
272+
TEST_F(VariableBlocksTest, ShowVariable_Local_Nonexistent)
273+
{
274+
auto stage = std::make_shared<Stage>();
275+
auto sprite = std::make_shared<Sprite>();
276+
277+
auto var1 = std::make_shared<Variable>("a", "var1", 835.21);
278+
sprite->addVariable(var1);
279+
auto var2 = std::make_shared<Variable>("b", "var2", "Hello world");
280+
sprite->addVariable(var2);
281+
282+
m_engine->setTargets({ stage, sprite });
283+
284+
ScriptBuilder builder1(m_extension.get(), m_engine, sprite);
285+
builder1.addBlock("data_showvariable");
286+
builder1.addEntityField("VARIABLE", var1);
287+
288+
ScriptBuilder builder2(m_extension.get(), m_engine, sprite);
289+
builder2.addBlock("data_showvariable");
290+
builder2.addEntityField("VARIABLE", var2);
291+
292+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
293+
m_engine->run();
294+
295+
// Missing monitors should be created
296+
builder1.run();
297+
298+
Monitor *monitor1 = var1->monitor();
299+
ASSERT_TRUE(monitor1);
300+
ASSERT_TRUE(monitor1->visible());
301+
302+
builder2.run();
303+
304+
Monitor *monitor2 = var2->monitor();
305+
306+
ASSERT_TRUE(monitor2);
307+
ASSERT_TRUE(monitor1->visible());
308+
ASSERT_TRUE(monitor2->visible());
309+
}
310+
311+
TEST_F(VariableBlocksTest, ShowVariable_Local_FromClone)
312+
{
313+
auto stage = std::make_shared<Stage>();
314+
auto sprite = std::make_shared<Sprite>();
315+
316+
auto var1 = std::make_shared<Variable>("a", "var1", 835.21);
317+
sprite->addVariable(var1);
318+
auto var2 = std::make_shared<Variable>("b", "var2", "Hello world");
319+
sprite->addVariable(var2);
320+
321+
auto monitor1 = std::make_shared<Monitor>("monitor", "data_variable");
322+
monitor1->block()->addField(std::make_shared<Field>("VARIABLE", var1->name(), var1));
323+
monitor1->setVisible(true);
324+
325+
auto monitor2 = std::make_shared<Monitor>("monitor", "data_variable");
326+
monitor2->block()->addField(std::make_shared<Field>("VARIABLE", var2->name(), var2));
327+
monitor2->setVisible(false);
328+
329+
m_engine->setMonitors({ monitor1, monitor2 });
330+
var1->setMonitor(monitor1.get());
331+
var2->setMonitor(monitor2.get());
332+
333+
sprite->setEngine(m_engine);
334+
auto clone = sprite->clone();
335+
336+
m_engine->setTargets({ stage, sprite, clone });
337+
338+
ScriptBuilder builder1(m_extension.get(), m_engine, clone);
339+
builder1.addBlock("data_showvariable");
340+
builder1.addEntityField("VARIABLE", var1);
341+
Block *hat1 = builder1.currentBlock()->parent();
342+
343+
ScriptBuilder builder2(m_extension.get(), m_engine, clone);
344+
builder2.addBlock("data_showvariable");
345+
builder2.addEntityField("VARIABLE", var2);
346+
Block *hat2 = builder2.currentBlock()->parent();
347+
348+
ScriptBuilder::buildMultiple({ &builder1, &builder2 });
349+
350+
// The clone root variables should be used
351+
builder1.run();
352+
ASSERT_TRUE(monitor1->visible());
353+
ASSERT_FALSE(monitor2->visible());
354+
355+
builder2.run();
356+
ASSERT_TRUE(monitor1->visible());
357+
ASSERT_TRUE(monitor2->visible());
358+
359+
ASSERT_FALSE(clone->variableAt(0)->monitor());
360+
ASSERT_FALSE(clone->variableAt(1)->monitor());
361+
}

0 commit comments

Comments
 (0)