Skip to content

Commit d68cc9e

Browse files
committed
Add TextBubble class
1 parent 00abb3a commit d68cc9e

File tree

8 files changed

+232
-0
lines changed

8 files changed

+232
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ target_sources(scratchcpp
5555
include/scratchcpp/target.h
5656
include/scratchcpp/stage.h
5757
include/scratchcpp/sprite.h
58+
include/scratchcpp/textbubble.h
5859
include/scratchcpp/itimer.h
5960
include/scratchcpp/keyevent.h
6061
include/scratchcpp/rect.h

include/scratchcpp/textbubble.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "drawable.h"
6+
#include "signal.h"
7+
8+
namespace libscratchcpp
9+
{
10+
11+
class TextBubblePrivate;
12+
13+
/*! \brief The TextBubble class represents a text bubble created using say or think block. */
14+
class TextBubble : public Drawable
15+
{
16+
public:
17+
enum class Type
18+
{
19+
Say,
20+
Think
21+
};
22+
23+
TextBubble();
24+
TextBubble(const TextBubble &) = delete;
25+
26+
Type type() const;
27+
virtual void setType(Type type);
28+
sigslot::signal<Type> &typeChanged() const;
29+
30+
const std::string &text() const;
31+
virtual void setText(const std::string &text);
32+
sigslot::signal<const std::string &> &textChanged() const;
33+
34+
private:
35+
spimpl::unique_impl_ptr<TextBubblePrivate> impl;
36+
};
37+
38+
} // namespace libscratchcpp

src/scratch/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ target_sources(scratchcpp
4444
sprite.cpp
4545
sprite_p.cpp
4646
sprite_p.h
47+
textbubble.cpp
48+
textbubble_p.cpp
49+
textbubble_p.h
4750
broadcast.cpp
4851
broadcast_p.cpp
4952
broadcast_p.h

src/scratch/textbubble.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include <scratchcpp/textbubble.h>
4+
#include <scratchcpp/value.h>
5+
6+
#include "textbubble_p.h"
7+
8+
using namespace libscratchcpp;
9+
10+
TextBubble::TextBubble() :
11+
Drawable(),
12+
impl(spimpl::make_unique_impl<TextBubblePrivate>())
13+
{
14+
}
15+
16+
/*! Returns the type of the TextBubble (say or think). */
17+
TextBubble::Type TextBubble::type() const
18+
{
19+
return impl->type;
20+
}
21+
22+
/*! Sets the type of the TextBubble (say or think). */
23+
void TextBubble::setType(Type type)
24+
{
25+
impl->type = type;
26+
impl->typeChanged(type);
27+
}
28+
29+
/*! Emits when the type changes. */
30+
sigslot::signal<TextBubble::Type> &TextBubble::typeChanged() const
31+
{
32+
return impl->typeChanged;
33+
}
34+
35+
/*!
36+
* Returns the text of the TextBubble.
37+
* \note If the text is an empty string, the TextBubble is supposed to be hidden.
38+
*/
39+
const std::string &TextBubble::text() const
40+
{
41+
return impl->text;
42+
}
43+
44+
/*!
45+
* Sets the text of the TextBubble.
46+
* \note If the text is an empty string, the TextBubble is supposed to be hidden.
47+
*/
48+
void TextBubble::setText(const std::string &text)
49+
{
50+
// https://github.com/scratchfoundation/scratch-vm/blob/7313ce5199f8a3da7850085d0f7f6a3ca2c89bf6/src/blocks/scratch3_looks.js#L251-L257
51+
const Value v(text);
52+
std::string converted = text;
53+
54+
// Non-integers should be rounded to 2 decimal places (no more, no less), unless they're small enough that rounding would display them as 0.00.
55+
if (v.isValidNumber()) {
56+
const double num = v.toDouble();
57+
58+
if (std::abs(num) >= 0.01 && (v % 1).toDouble() != 0)
59+
converted = Value(std::round(num * 100) / 100).toString();
60+
}
61+
62+
// Limit the length of the string
63+
size_t limit = 330;
64+
impl->text = converted.substr(0, limit);
65+
66+
impl->textChanged(impl->text);
67+
}
68+
69+
/*! Emits when the text changes. */
70+
sigslot::signal<const std::string &> &TextBubble::textChanged() const
71+
{
72+
return impl->textChanged;
73+
}

src/scratch/textbubble_p.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "textbubble_p.h"
4+
5+
using namespace libscratchcpp;

src/scratch/textbubble_p.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <scratchcpp/textbubble.h>
6+
7+
namespace libscratchcpp
8+
{
9+
10+
struct TextBubblePrivate
11+
{
12+
TextBubble::Type type = TextBubble::Type::Say;
13+
std::string text;
14+
mutable sigslot::signal<TextBubble::Type> typeChanged;
15+
mutable sigslot::signal<const std::string &> textChanged;
16+
};
17+
18+
} // namespace libscratchcpp

test/scratch_classes/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,20 @@ target_link_libraries(
8888

8989
gtest_discover_tests(target_test)
9090

91+
# textbubble_test
92+
add_executable(
93+
textbubble_test
94+
textbubble_test.cpp
95+
)
96+
97+
target_link_libraries(
98+
textbubble_test
99+
GTest::gtest_main
100+
scratchcpp
101+
)
102+
103+
gtest_discover_tests(textbubble_test)
104+
91105
# value_test
92106
add_executable(
93107
value_test
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <scratchcpp/textbubble.h>
2+
3+
#include "../common.h"
4+
5+
using namespace libscratchcpp;
6+
7+
TEST(TextBubbleTest, BubbleType)
8+
{
9+
TextBubble bubble;
10+
ASSERT_EQ(bubble.type(), TextBubble::Type::Say);
11+
12+
bubble.setType(TextBubble::Type::Think);
13+
ASSERT_EQ(bubble.type(), TextBubble::Type::Think);
14+
15+
bubble.setType(TextBubble::Type::Say);
16+
ASSERT_EQ(bubble.type(), TextBubble::Type::Say);
17+
}
18+
19+
TEST(TextBubbleTest, BubbleText)
20+
{
21+
TextBubble bubble;
22+
ASSERT_TRUE(bubble.text().empty());
23+
24+
bubble.setText("hello");
25+
ASSERT_EQ(bubble.text(), "hello");
26+
27+
bubble.setText("world");
28+
ASSERT_EQ(bubble.text(), "world");
29+
30+
// longstr.length = 384, should be limited to 330 in bubble text
31+
std::string longstr =
32+
"EY8OUNzAqwgh7NRGk5TzCP3dkAhJy9TX"
33+
"Y9mqKElPjdQpKddYqjyCwUk2hx6YgVZV"
34+
"6BOdmZGxDMs8Hjv8W9G6j4gTxAWdOkzs"
35+
"8Ih80xzEDbvLilWsDwoB6FxH2kVVI4xs"
36+
"IXOETNQ6QMsCKLWc5XjHk2BS9nYvDGpJ"
37+
"uEmp9zIzFGT1kRSrOlU3ZwnN1YtvqFx"
38+
"3hkWVNtJ71dQ0PJHhOVQPUy19V01SPu3"
39+
"KIIS2wdSUVAc4RYMzepSveghzWbdcizy"
40+
"Tm1KKAj4svu9YoL8b9vsolG8gKunvKO7"
41+
"MurRKSeUbECELnJEKV6683xCq7RvmjAu"
42+
"2djZ54apiQc1lTixWns5GoG0SVNuFzHl"
43+
"q97qUiqiMecjVFM51YVif7c1Stip52Hl";
44+
45+
bubble.setText(longstr);
46+
ASSERT_EQ(bubble.text().length(), 330);
47+
ASSERT_EQ(bubble.text(), longstr.substr(0, 330));
48+
49+
// Integers should be left unchanged
50+
bubble.setText("8");
51+
ASSERT_EQ(bubble.text(), "8");
52+
53+
bubble.setText("-52");
54+
ASSERT_EQ(bubble.text(), "-52");
55+
56+
bubble.setText("0");
57+
ASSERT_EQ(bubble.text(), "0");
58+
59+
// Non-integers should be rounded to 2 decimal places (no more, no less), unless they're small enough that rounding would display them as 0.00 (#478)
60+
bubble.setText("8.324");
61+
ASSERT_EQ(bubble.text(), "8.32");
62+
63+
bubble.setText("-52.576");
64+
ASSERT_EQ(bubble.text(), "-52.58");
65+
66+
bubble.setText("3.5");
67+
ASSERT_EQ(bubble.text(), "3.5");
68+
69+
bubble.setText("0.015");
70+
ASSERT_EQ(bubble.text(), "0.02");
71+
72+
bubble.setText("-0.015");
73+
ASSERT_EQ(bubble.text(), "-0.02");
74+
75+
bubble.setText("0.005");
76+
ASSERT_EQ(bubble.text(), "0.005");
77+
78+
bubble.setText("-0.005");
79+
ASSERT_EQ(bubble.text(), "-0.005");
80+
}

0 commit comments

Comments
 (0)