Skip to content

Commit a18d5d4

Browse files
authored
Merge pull request #184 from scratchcpp/dropdown_menu_value
Support dropdown menus in Compiler
2 parents 66f8b8e + 25534cc commit a18d5d4

File tree

7 files changed

+159
-10
lines changed

7 files changed

+159
-10
lines changed

include/scratchcpp/input.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class LIBSCRATCHCPP_EXPORT Input
4545
void setValueBlock(std::shared_ptr<Block> block);
4646
void setValueBlockId(const std::string &id);
4747

48+
bool pointsToDropdownMenu() const;
49+
std::string selectedMenuItem() const;
50+
4851
private:
4952
spimpl::unique_impl_ptr<InputPrivate> impl;
5053
};

src/engine/compiler.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,14 @@ const std::vector<InputValue *> &Compiler::constInputValues() const
9898
std::vector<Value> Compiler::constValues() const
9999
{
100100
std::vector<Value> ret;
101-
for (auto value : impl->constValues)
102-
ret.push_back(value->value());
101+
for (auto value : impl->constValues) {
102+
const auto &menuInfo = impl->constValueMenuInfo.at(value);
103+
104+
if (menuInfo.first)
105+
ret.push_back(menuInfo.second);
106+
else
107+
ret.push_back(value->value());
108+
}
103109
return ret;
104110
}
105111

@@ -139,7 +145,10 @@ void Compiler::addInput(Input *input)
139145
}
140146
switch (input->type()) {
141147
case Input::Type::Shadow:
142-
addInstruction(OP_CONST, { constIndex(input->primaryValue()) });
148+
if (input->pointsToDropdownMenu())
149+
addInstruction(OP_CONST, { impl->constIndex(input->primaryValue(), true, input->selectedMenuItem()) });
150+
else
151+
addInstruction(OP_CONST, { impl->constIndex(input->primaryValue()) });
143152
break;
144153

145154
case Input::Type::NoShadow: {
@@ -275,11 +284,7 @@ unsigned int Compiler::listIndex(std::shared_ptr<Entity> listEntity)
275284
/*! Returns the index of the given constant input value. */
276285
unsigned int Compiler::constIndex(InputValue *value)
277286
{
278-
auto it = std::find(impl->constValues.begin(), impl->constValues.end(), value);
279-
if (it != impl->constValues.end())
280-
return it - impl->constValues.begin();
281-
impl->constValues.push_back(value);
282-
return impl->constValues.size() - 1;
287+
return impl->constIndex(value);
283288
}
284289

285290
/*! Returns the index of the procedure code of the given block. */

src/engine/compiler_p.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ void CompilerPrivate::addInstruction(vm::Opcode opcode, std::initializer_list<un
2020
bytecode.push_back(arg);
2121
}
2222

23+
unsigned int CompilerPrivate::constIndex(InputValue *value, bool pointsToDropdownMenu, const std::string &selectedMenuItem)
24+
{
25+
auto it = std::find(constValues.begin(), constValues.end(), value);
26+
if (it != constValues.end())
27+
return it - constValues.begin();
28+
constValues.push_back(value);
29+
constValueMenuInfo[value] = { pointsToDropdownMenu, selectedMenuItem };
30+
return constValues.size() - 1;
31+
}
32+
2333
void CompilerPrivate::substackEnd()
2434
{
2535
auto parent = substackTree.back();

src/engine/compiler_p.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ struct CompilerPrivate
1313

1414
void addInstruction(vm::Opcode opcode, std::initializer_list<unsigned int> args = {});
1515

16+
unsigned int constIndex(InputValue *value, bool pointsToDropdownMenu = false, const std::string &selectedMenuItem = "");
17+
1618
void substackEnd();
1719

1820
IEngine *engine;
@@ -23,6 +25,7 @@ struct CompilerPrivate
2325

2426
std::vector<unsigned int> bytecode;
2527
std::vector<InputValue *> constValues;
28+
std::unordered_map<InputValue *, std::pair<bool, std::string>> constValueMenuInfo; // input value, <whether the input points to a dropdown menu, selected menu item>
2629
std::vector<Variable *> variables;
2730
std::vector<List *> lists;
2831
std::vector<std::string> procedures;

src/scratch/input.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <iostream>
44
#include <scratchcpp/input.h>
5+
#include <scratchcpp/field.h>
56
#include <scratchcpp/variable.h>
67
#include <scratchcpp/block.h>
78

@@ -94,3 +95,35 @@ void Input::setValueBlockId(const std::string &id)
9495
impl->primaryValue.setValueBlock(nullptr);
9596
impl->primaryValue.setValueBlockId(id);
9697
}
98+
99+
/*!
100+
* Returns true if the input points to a dropdown menu.\n
101+
* (if type() == Type::Shadow and valueBlock() points to a block with a single field which does not point to an entity)
102+
*/
103+
bool Input::pointsToDropdownMenu() const
104+
{
105+
auto block = valueBlock();
106+
107+
if ((impl->type != Type::Shadow) || !block)
108+
return false;
109+
110+
const auto &fields = block->fields();
111+
112+
if (fields.size() != 1)
113+
return false;
114+
115+
auto field = fields[0];
116+
return (field && !field->valuePtr() && (field->valueId() == ""));
117+
}
118+
119+
/*!
120+
* Returns the selected item in the dropdown menu.\n
121+
* Works only pointsToDropdownMenu() is true.
122+
*/
123+
std::string Input::selectedMenuItem() const
124+
{
125+
if (!pointsToDropdownMenu())
126+
return "";
127+
128+
return valueBlock()->fieldAt(0)->value().toString();
129+
}

test/compiler/compiler_test.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,32 @@ TEST_F(CompilerTest, ResolveInput)
329329
engine.registerSection(m_section);
330330

331331
auto block = std::make_shared<Block>("a", "test_block1");
332-
auto reporter = std::make_shared<Block>("a", "test_reporter");
333332
auto input = std::make_shared<Input>("INPUT1", Input::Type::Shadow);
334333
input->setPrimaryValue("test");
335334
input->setInputId(TestBlockSection::INPUT1);
336-
input->setValueBlock(reporter);
335+
block->addInput(input);
336+
block->updateInputMap();
337+
block->setCompileFunction(&TestBlockSection::compileTestBlock1);
338+
339+
compiler.compile(block);
340+
341+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_PRINT, vm::OP_HALT }));
342+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ "test" }));
343+
}
344+
345+
TEST_F(CompilerTest, ResolveDropdownMenuInput)
346+
{
347+
INIT_COMPILER(engine, compiler);
348+
349+
engine.registerSection(m_section);
350+
351+
auto block = std::make_shared<Block>("a", "test_block1");
352+
auto input = std::make_shared<Input>("INPUT1", Input::Type::Shadow);
353+
auto menu = std::make_shared<Block>("a", "test_menu");
354+
auto optionField = std::make_shared<Field>("OPTION", "test");
355+
input->setInputId(TestBlockSection::INPUT1);
356+
input->setValueBlock(menu);
357+
menu->addField(optionField);
337358
block->addInput(input);
338359
block->updateInputMap();
339360
block->setCompileFunction(&TestBlockSection::compileTestBlock1);

test/scratch_classes/input_test.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include <scratchcpp/input.h>
22
#include <scratchcpp/inputvalue.h>
3+
#include <scratchcpp/field.h>
34
#include <scratchcpp/block.h>
5+
#include <scratchcpp/variable.h>
46

57
#include "../common.h"
68

@@ -64,3 +66,75 @@ TEST(InputTest, ValueBlock)
6466
ASSERT_EQ(input.valueBlock(), nullptr);
6567
ASSERT_EQ(input.valueBlockId(), "");
6668
}
69+
70+
TEST(InputTest, SelectedMenuItem)
71+
{
72+
// Test with Shadow type
73+
Input input1("", Input::Type::Shadow);
74+
ASSERT_FALSE(input1.pointsToDropdownMenu());
75+
ASSERT_TRUE(input1.selectedMenuItem().empty());
76+
77+
auto block1 = std::make_shared<Block>("abc", "");
78+
input1.setValueBlock(block1);
79+
ASSERT_FALSE(input1.pointsToDropdownMenu());
80+
ASSERT_TRUE(input1.selectedMenuItem().empty());
81+
82+
auto field1 = std::make_shared<Field>("OPTION1", "");
83+
block1->addField(field1);
84+
ASSERT_TRUE(input1.pointsToDropdownMenu());
85+
ASSERT_TRUE(input1.selectedMenuItem().empty());
86+
87+
auto field2 = std::make_shared<Field>("OPTION2", "something");
88+
block1->addField(field2);
89+
ASSERT_FALSE(input1.pointsToDropdownMenu());
90+
ASSERT_TRUE(input1.selectedMenuItem().empty());
91+
92+
auto block2 = std::make_shared<Block>("def", "");
93+
input1.setValueBlock(block2);
94+
95+
field1 = std::make_shared<Field>("OPTION1", "something");
96+
block2->addField(field1);
97+
ASSERT_TRUE(input1.pointsToDropdownMenu());
98+
ASSERT_EQ(input1.selectedMenuItem(), "something");
99+
100+
auto variable = std::make_shared<Variable>("", "");
101+
field1->setValuePtr(variable);
102+
ASSERT_FALSE(input1.pointsToDropdownMenu());
103+
ASSERT_TRUE(input1.selectedMenuItem().empty());
104+
105+
field1->setValuePtr(nullptr);
106+
ASSERT_TRUE(input1.pointsToDropdownMenu());
107+
ASSERT_EQ(input1.selectedMenuItem(), "something");
108+
109+
field2 = std::make_shared<Field>("OPTION2", "hello");
110+
block2->addField(field2);
111+
ASSERT_FALSE(input1.pointsToDropdownMenu());
112+
ASSERT_TRUE(input1.selectedMenuItem().empty());
113+
114+
// Test with NoShadow type
115+
Input input2("", Input::Type::NoShadow);
116+
ASSERT_FALSE(input2.pointsToDropdownMenu());
117+
ASSERT_TRUE(input2.selectedMenuItem().empty());
118+
119+
auto block3 = std::make_shared<Block>("ghi", "");
120+
input2.setValueBlock(block3);
121+
ASSERT_FALSE(input2.pointsToDropdownMenu());
122+
ASSERT_TRUE(input2.selectedMenuItem().empty());
123+
124+
block3->addField(field1);
125+
ASSERT_FALSE(input2.pointsToDropdownMenu());
126+
ASSERT_TRUE(input2.selectedMenuItem().empty());
127+
128+
// Test with ObscuredShadow type
129+
Input input3("", Input::Type::ObscuredShadow);
130+
ASSERT_FALSE(input3.pointsToDropdownMenu());
131+
ASSERT_TRUE(input3.selectedMenuItem().empty());
132+
133+
input3.setValueBlock(block3);
134+
ASSERT_FALSE(input3.pointsToDropdownMenu());
135+
ASSERT_TRUE(input3.selectedMenuItem().empty());
136+
137+
block3->addField(field1);
138+
ASSERT_FALSE(input3.pointsToDropdownMenu());
139+
ASSERT_TRUE(input3.selectedMenuItem().empty());
140+
}

0 commit comments

Comments
 (0)