Skip to content

Commit 2f627e6

Browse files
committed
Implement pen_setPenColorToColor block
1 parent 9c8640d commit 2f627e6

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed

src/blocks/penblocks.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ void PenBlocks::registerBlocks(IEngine *engine)
3636
engine->addCompileFunction(this, "pen_stamp", &compileStamp);
3737
engine->addCompileFunction(this, "pen_penDown", &compilePenDown);
3838
engine->addCompileFunction(this, "pen_penUp", &compilePenUp);
39+
engine->addCompileFunction(this, "pen_setPenColorToColor", &compileSetPenColorToColor);
40+
}
41+
42+
CompilerValue *PenBlocks::compileSetPenColorToColor(Compiler *compiler)
43+
{
44+
CompilerValue *color = compiler->addInput("COLOR");
45+
compiler->addTargetFunctionCall("pen_setPenColorToColor", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { color });
46+
return nullptr;
3947
}
4048

4149
CompilerValue *PenBlocks::compileClear(Compiler *compiler)
@@ -111,3 +119,45 @@ BLOCK_EXPORT void pen_set_pen_down(Target *target, bool down)
111119
{
112120
getTargetModel(target)->setPenDown(down);
113121
}
122+
123+
BLOCK_EXPORT void pen_setPenColorToColor(Target *target, const ValueData *color)
124+
{
125+
TargetModel *model = getTargetModel(target);
126+
127+
std::string stringValue;
128+
PenState &penState = model->penState();
129+
QColor newColor;
130+
131+
if (value_isString(color))
132+
value_toString(color, &stringValue);
133+
134+
if (!stringValue.empty() && stringValue[0] == '#') {
135+
bool valid = false;
136+
137+
if (stringValue.size() <= 7) // #RRGGBB
138+
{
139+
newColor = QColor::fromString(stringValue);
140+
valid = newColor.isValid();
141+
}
142+
143+
if (!valid)
144+
newColor = Qt::black;
145+
146+
} else
147+
newColor = QColor::fromRgba(static_cast<QRgb>(value_toLong(color)));
148+
149+
QColor hsv = newColor.toHsv();
150+
penState.color = (hsv.hue() / 360.0) * 100;
151+
penState.saturation = hsv.saturationF() * 100;
152+
penState.brightness = hsv.valueF() * 100;
153+
154+
if (newColor.alpha() > 0)
155+
penState.transparency = 100 * (1 - newColor.alpha() / 255.0);
156+
else
157+
penState.transparency = 0;
158+
159+
penState.updateColor();
160+
161+
// Set the legacy "shade" value the same way Scratch 2 did.
162+
penState.shade = penState.brightness / 2;
163+
}

src/blocks/penblocks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class PenBlocks : public libscratchcpp::IExtension
1717
static libscratchcpp::CompilerValue *compileStamp(libscratchcpp::Compiler *compiler);
1818
static libscratchcpp::CompilerValue *compilePenDown(libscratchcpp::Compiler *compiler);
1919
static libscratchcpp::CompilerValue *compilePenUp(libscratchcpp::Compiler *compiler);
20+
static libscratchcpp::CompilerValue *compileSetPenColorToColor(libscratchcpp::Compiler *compiler);
2021
};
2122

2223
} // namespace scratchcpprender

test/blocks/pen_blocks_test.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,188 @@ TEST_F(PenBlocksTest, PenUp_Stage)
220220
thread->run();
221221
ASSERT_FALSE(model.penDown());
222222
}
223+
224+
TEST_F(PenBlocksTest, SetPenColorToColor_ValidHex)
225+
{
226+
auto sprite = std::make_shared<Sprite>();
227+
sprite->setEngine(&m_engineMock);
228+
229+
RenderedTarget renderedTarget;
230+
SpriteModel model;
231+
model.init(sprite.get());
232+
model.setRenderedTarget(&renderedTarget);
233+
sprite->setInterface(&model);
234+
235+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
236+
builder.addBlock("pen_setPenColorToColor");
237+
builder.addValueInput("COLOR", "#FFGFFF");
238+
239+
auto thread = buildScript(builder, sprite.get());
240+
241+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
242+
thread->run();
243+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(359, 0, 0)));
244+
}
245+
246+
TEST_F(PenBlocksTest, SetPenColorToColor_CaseInsensitiveHex)
247+
{
248+
auto sprite = std::make_shared<Sprite>();
249+
sprite->setEngine(&m_engineMock);
250+
251+
RenderedTarget renderedTarget;
252+
SpriteModel model;
253+
model.init(sprite.get());
254+
model.setRenderedTarget(&renderedTarget);
255+
sprite->setInterface(&model);
256+
257+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
258+
builder.addBlock("pen_setPenColorToColor");
259+
builder.addValueInput("COLOR", "#AABbCC");
260+
261+
auto thread = buildScript(builder, sprite.get());
262+
263+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
264+
thread->run();
265+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(210, 42, 204)));
266+
}
267+
268+
TEST_F(PenBlocksTest, SetPenColorToColor_3DigitHex)
269+
{
270+
auto sprite = std::make_shared<Sprite>();
271+
sprite->setEngine(&m_engineMock);
272+
273+
RenderedTarget renderedTarget;
274+
SpriteModel model;
275+
model.init(sprite.get());
276+
model.setRenderedTarget(&renderedTarget);
277+
sprite->setInterface(&model);
278+
279+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
280+
builder.addBlock("pen_setPenColorToColor");
281+
builder.addValueInput("COLOR", "#03F");
282+
283+
auto thread = buildScript(builder, sprite.get());
284+
285+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
286+
thread->run();
287+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(228, 255, 255)));
288+
}
289+
290+
TEST_F(PenBlocksTest, SetPenColorToColor_InvalidHex)
291+
{
292+
auto sprite = std::make_shared<Sprite>();
293+
sprite->setEngine(&m_engineMock);
294+
295+
RenderedTarget renderedTarget;
296+
SpriteModel model;
297+
model.init(sprite.get());
298+
model.setRenderedTarget(&renderedTarget);
299+
sprite->setInterface(&model);
300+
301+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
302+
builder.addBlock("pen_setPenColorToColor");
303+
builder.addValueInput("COLOR", "#AABBCCDD");
304+
305+
model.penAttributes().color = QNanoColor::fromQColor(QColor::fromHsv(228, 255, 255));
306+
307+
auto thread = buildScript(builder, sprite.get());
308+
309+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
310+
thread->run();
311+
312+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor(0, 0, 0)));
313+
}
314+
315+
TEST_F(PenBlocksTest, SetPenColorToColor_InvalidString)
316+
{
317+
auto sprite = std::make_shared<Sprite>();
318+
sprite->setEngine(&m_engineMock);
319+
320+
RenderedTarget renderedTarget;
321+
SpriteModel model;
322+
model.init(sprite.get());
323+
model.setRenderedTarget(&renderedTarget);
324+
sprite->setInterface(&model);
325+
326+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
327+
builder.addBlock("pen_setPenColorToColor");
328+
builder.addValueInput("COLOR", "FFFFFF");
329+
330+
model.penAttributes().color = QNanoColor::fromQColor(QColor::fromHsv(228, 255, 255));
331+
332+
auto thread = buildScript(builder, sprite.get());
333+
334+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
335+
thread->run();
336+
337+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor(0, 0, 0)));
338+
}
339+
340+
TEST_F(PenBlocksTest, SetPenColorToColor_NumberWithoutAlphaChannel)
341+
{
342+
auto sprite = std::make_shared<Sprite>();
343+
sprite->setEngine(&m_engineMock);
344+
345+
RenderedTarget renderedTarget;
346+
SpriteModel model;
347+
model.init(sprite.get());
348+
model.setRenderedTarget(&renderedTarget);
349+
sprite->setInterface(&model);
350+
351+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
352+
builder.addBlock("pen_setPenColorToColor");
353+
builder.addValueInput("COLOR", 255);
354+
355+
auto thread = buildScript(builder, sprite.get());
356+
357+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
358+
thread->run();
359+
360+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(239, 255, 255)));
361+
}
362+
363+
TEST_F(PenBlocksTest, SetPenColorToColor_NumberWithAlphaChannel)
364+
{
365+
auto sprite = std::make_shared<Sprite>();
366+
sprite->setEngine(&m_engineMock);
367+
368+
RenderedTarget renderedTarget;
369+
SpriteModel model;
370+
model.init(sprite.get());
371+
model.setRenderedTarget(&renderedTarget);
372+
sprite->setInterface(&model);
373+
374+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
375+
builder.addBlock("pen_setPenColorToColor");
376+
builder.addValueInput("COLOR", 1228097602);
377+
378+
auto thread = buildScript(builder, sprite.get());
379+
380+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
381+
thread->run();
382+
383+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(162, 74, 72, 73)));
384+
}
385+
386+
TEST_F(PenBlocksTest, SetPenColorToColor_Stage)
387+
{
388+
auto stage = std::make_shared<Stage>();
389+
stage->setEngine(&m_engineMock);
390+
391+
RenderedTarget renderedTarget;
392+
StageModel model;
393+
model.init(stage.get());
394+
model.setRenderedTarget(&renderedTarget);
395+
stage->setInterface(&model);
396+
397+
ScriptBuilder builder(m_extension.get(), m_engine, stage);
398+
builder.addBlock("pen_setPenColorToColor");
399+
builder.addValueInput("COLOR", rgb(128, 255, 26));
400+
401+
auto thread = buildScript(builder, stage.get());
402+
403+
EXPECT_CALL(m_engineMock, requestRedraw).Times(0);
404+
thread->run();
405+
406+
ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(93, 229, 255)));
407+
}

0 commit comments

Comments
 (0)