gosu 0.13.3 → 0.14.0.pre2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gosu/Audio.hpp +15 -11
- data/Gosu/Font.hpp +24 -20
- data/Gosu/Fwd.hpp +1 -1
- data/Gosu/Graphics.hpp +8 -9
- data/Gosu/ImageData.hpp +1 -1
- data/Gosu/Input.hpp +1 -1
- data/Gosu/Math.hpp +0 -18
- data/Gosu/Text.hpp +22 -30
- data/Gosu/TextInput.hpp +13 -0
- data/Gosu/Utility.hpp +2 -0
- data/Gosu/Window.hpp +3 -3
- data/README.md +3 -4
- data/ext/gosu/extconf.rb +7 -9
- data/lib/gosu/swig_patches.rb +1 -4
- data/rdoc/gosu.rb +34 -9
- data/src/Audio.cpp +6 -6
- data/src/AudioImpl.cpp +2 -2
- data/src/Bitmap.cpp +1 -2
- data/src/BitmapIO.cpp +21 -2
- data/src/BlockAllocator.cpp +1 -1
- data/src/Channel.cpp +7 -1
- data/src/ClipRectStack.hpp +4 -1
- data/src/Color.cpp +2 -1
- data/src/DirectoriesWin.cpp +1 -1
- data/src/DrawOp.hpp +8 -4
- data/src/DrawOpQueue.hpp +13 -24
- data/src/FileUnix.cpp +3 -1
- data/src/Font.cpp +92 -96
- data/src/GosuGLView.cpp +59 -31
- data/src/GosuGLView.hpp +14 -0
- data/src/GosuViewController.cpp +21 -21
- data/src/{GosuViewController.h → GosuViewController.hpp} +2 -4
- data/src/Graphics.cpp +71 -38
- data/src/GraphicsImpl.hpp +12 -29
- data/src/Image.cpp +5 -7
- data/src/Input.cpp +7 -5
- data/src/InputUIKit.cpp +19 -37
- data/src/Macro.cpp +10 -2
- data/src/MarkupParser.cpp +241 -0
- data/src/MarkupParser.hpp +61 -0
- data/src/Math.cpp +1 -1
- data/src/OffScreenTarget.cpp +99 -0
- data/src/OffScreenTarget.hpp +23 -0
- data/src/OggFile.hpp +10 -0
- data/src/RenderState.hpp +0 -2
- data/src/Resolution.cpp +2 -2
- data/src/RubyGosu.cxx +457 -244
- data/src/TexChunk.cpp +8 -6
- data/src/Text.cpp +58 -345
- data/src/TextBuilder.cpp +138 -0
- data/src/TextBuilder.hpp +55 -0
- data/src/TextInput.cpp +27 -10
- data/src/Texture.cpp +22 -17
- data/src/Texture.hpp +19 -20
- data/src/TimingApple.cpp +5 -7
- data/src/TimingUnix.cpp +1 -4
- data/src/TimingWin.cpp +4 -1
- data/src/TrueTypeFont.cpp +282 -0
- data/src/TrueTypeFont.hpp +66 -0
- data/src/TrueTypeFontApple.cpp +65 -0
- data/src/TrueTypeFontUnix.cpp +91 -0
- data/src/TrueTypeFontWin.cpp +82 -0
- data/src/Utility.cpp +40 -0
- data/src/Window.cpp +7 -6
- data/src/WindowUIKit.cpp +9 -4
- data/src/stb_truetype.h +4589 -0
- data/src/utf8proc.c +755 -0
- data/src/utf8proc.h +699 -0
- data/src/utf8proc_data.h +14386 -0
- metadata +23 -16
- data/src/FormattedString.cpp +0 -237
- data/src/FormattedString.hpp +0 -47
- data/src/GosuAppDelegate.cpp +0 -30
- data/src/GosuAppDelegate.h +0 -8
- data/src/GosuGLView.h +0 -8
- data/src/TextApple.cpp +0 -212
- data/src/TextTTFWin.cpp +0 -197
- data/src/TextUnix.cpp +0 -280
- data/src/TextWin.cpp +0 -191
data/src/GraphicsImpl.hpp
CHANGED
@@ -4,21 +4,11 @@
|
|
4
4
|
#include <Gosu/Graphics.hpp>
|
5
5
|
#include <Gosu/Platform.hpp>
|
6
6
|
|
7
|
-
#if defined(
|
8
|
-
#ifndef NOMINMAX
|
9
|
-
#define NOMINMAX
|
10
|
-
#endif
|
11
|
-
#include <windows.h>
|
12
|
-
#include <GL/gl.h>
|
13
|
-
#elif defined(GOSU_IS_IPHONE)
|
7
|
+
#if defined(GOSU_IS_IPHONE) || defined(GOSU_IS_OPENGLES)
|
14
8
|
#include <OpenGLES/ES1/gl.h>
|
15
9
|
#include <OpenGLES/ES1/glext.h>
|
16
|
-
#elif defined(GOSU_IS_MAC)
|
17
|
-
#include <OpenGL/gl.h>
|
18
|
-
#elif defined GOSU_IS_OPENGLES
|
19
|
-
#include <GLES/gl.h>
|
20
10
|
#else
|
21
|
-
#include <
|
11
|
+
#include <SDL_opengl.h>
|
22
12
|
#endif
|
23
13
|
|
24
14
|
#include <algorithm>
|
@@ -51,6 +41,13 @@ namespace Gosu
|
|
51
41
|
|
52
42
|
namespace Gosu
|
53
43
|
{
|
44
|
+
enum QueueMode
|
45
|
+
{
|
46
|
+
QM_RENDER_TO_SCREEN,
|
47
|
+
QM_RENDER_TO_TEXTURE,
|
48
|
+
QM_RECORD_MACRO,
|
49
|
+
};
|
50
|
+
|
54
51
|
class Texture;
|
55
52
|
class TexChunk;
|
56
53
|
class ClipRectStack;
|
@@ -99,25 +96,11 @@ namespace Gosu
|
|
99
96
|
y = out[1] / out[3];
|
100
97
|
}
|
101
98
|
|
102
|
-
|
103
|
-
{
|
104
|
-
for (int y = 0; y < bmp.height(); ++y) {
|
105
|
-
for (int x = 0; x < bmp.width(); ++x) {
|
106
|
-
Color c = bmp.get_pixel(x, y);
|
107
|
-
c.set_alpha(c.alpha() * alpha / 255);
|
108
|
-
bmp.set_pixel(x, y, c);
|
109
|
-
}
|
110
|
-
}
|
111
|
-
}
|
112
|
-
|
113
|
-
#ifdef GOSU_IS_IPHONE
|
99
|
+
#ifdef GOSU_IS_IPHONE
|
114
100
|
int clip_rect_base_factor();
|
115
|
-
|
101
|
+
#else
|
116
102
|
inline int clip_rect_base_factor() { return 1; }
|
117
|
-
|
118
|
-
|
119
|
-
bool is_entity(const std::string& name);
|
120
|
-
const Bitmap& entity_bitmap(const std::string& name);
|
103
|
+
#endif
|
121
104
|
|
122
105
|
void ensure_current_context();
|
123
106
|
}
|
data/src/Image.cpp
CHANGED
@@ -21,8 +21,8 @@ Gosu::Image::Image(const string& filename, unsigned flags)
|
|
21
21
|
Image(bmp, flags).data_.swap(data_);
|
22
22
|
}
|
23
23
|
|
24
|
-
Gosu::Image::Image(const string& filename, unsigned src_x, unsigned src_y,
|
25
|
-
|
24
|
+
Gosu::Image::Image(const string& filename, unsigned src_x, unsigned src_y,
|
25
|
+
unsigned src_width, unsigned src_height, unsigned flags)
|
26
26
|
{
|
27
27
|
// Forward.
|
28
28
|
Bitmap bmp;
|
@@ -36,8 +36,8 @@ Gosu::Image::Image(const Bitmap& source, unsigned flags)
|
|
36
36
|
Image(source, 0, 0, source.width(), source.height(), flags).data_.swap(data_);
|
37
37
|
}
|
38
38
|
|
39
|
-
Gosu::Image::Image(const Bitmap& source, unsigned src_x, unsigned src_y,
|
40
|
-
|
39
|
+
Gosu::Image::Image(const Bitmap& source, unsigned src_x, unsigned src_y,
|
40
|
+
unsigned src_width, unsigned src_height, unsigned flags)
|
41
41
|
: data_(Graphics::create_image(source, src_x, src_y, src_width, src_height, flags))
|
42
42
|
{
|
43
43
|
}
|
@@ -45,9 +45,7 @@ Gosu::Image::Image(const Bitmap& source, unsigned src_x, unsigned src_y, unsigne
|
|
45
45
|
Gosu::Image::Image(unique_ptr<ImageData>&& data)
|
46
46
|
: data_(data.release())
|
47
47
|
{
|
48
|
-
if (
|
49
|
-
throw invalid_argument("Gosu::Image cannot be initialized with nullptr");
|
50
|
-
}
|
48
|
+
if (!data_) throw invalid_argument("Gosu::Image cannot be initialized with nullptr");
|
51
49
|
}
|
52
50
|
|
53
51
|
unsigned Gosu::Image::width() const
|
data/src/Input.cpp
CHANGED
@@ -79,19 +79,21 @@ struct Gosu::Input::Impl
|
|
79
79
|
int x, y, window_x, window_y;
|
80
80
|
SDL_GetWindowPosition(window, &window_x, &window_y);
|
81
81
|
SDL_GetGlobalMouseState(&x, &y);
|
82
|
-
mouse_x = x - window_x
|
82
|
+
mouse_x = x - window_x;
|
83
|
+
mouse_y = y - window_y;
|
83
84
|
#else
|
84
85
|
int x, y;
|
85
86
|
SDL_GetMouseState(&x, &y);
|
86
|
-
mouse_x = x
|
87
|
+
mouse_x = x;
|
88
|
+
mouse_y = y;
|
87
89
|
#endif
|
88
90
|
}
|
89
91
|
|
90
92
|
void set_mouse_position(double x, double y)
|
91
93
|
{
|
92
94
|
SDL_WarpMouseInWindow(window,
|
93
|
-
(x - mouse_offset_x) / mouse_scale_x,
|
94
|
-
(y - mouse_offset_y) / mouse_scale_y);
|
95
|
+
static_cast<int>((x - mouse_offset_x) / mouse_scale_x),
|
96
|
+
static_cast<int>((y - mouse_offset_y) / mouse_scale_y));
|
95
97
|
|
96
98
|
#if SDL_VERSION_ATLEAST(2, 0, 4) && !defined(GOSU_IS_X)
|
97
99
|
// On systems where we have a working GetGlobalMouseState, we can warp the mouse and
|
@@ -329,7 +331,7 @@ string Gosu::Input::id_to_char(Button btn)
|
|
329
331
|
// Convert to lower case to be consistent with previous versions of Gosu.
|
330
332
|
// German umlauts are already reported in lower-case by SDL, anyway.
|
331
333
|
// (This should handle Turkish i/I just fine because it uses the current locale.)
|
332
|
-
wname[0] = (wchar_t) towlower((
|
334
|
+
wname[0] = (wchar_t) towlower((wint_t) wname[0]);
|
333
335
|
return wstring_to_utf8(wname);
|
334
336
|
}
|
335
337
|
|
data/src/InputUIKit.cpp
CHANGED
@@ -7,19 +7,10 @@
|
|
7
7
|
#import <UIKit/UIKit.h>
|
8
8
|
using namespace std;
|
9
9
|
|
10
|
-
struct Gosu::TextInput::Impl {};
|
11
|
-
Gosu::TextInput::TextInput() {}
|
12
|
-
Gosu::TextInput::~TextInput() {}
|
13
|
-
string Gosu::TextInput::text() const { return ""; }
|
14
|
-
void Gosu::TextInput::set_text(const string& text) {}
|
15
|
-
unsigned Gosu::TextInput::caret_pos() const { return 0; }
|
16
|
-
void Gosu::TextInput::set_caret_pos(unsigned) {}
|
17
|
-
unsigned Gosu::TextInput::selection_start() const { return 0; }
|
18
|
-
void Gosu::TextInput::set_selection_start(unsigned) {}
|
19
|
-
|
20
10
|
struct Gosu::Input::Impl
|
21
11
|
{
|
22
|
-
UIView* view;
|
12
|
+
UIView* view = nil;
|
13
|
+
TextInput* text_input = nullptr;
|
23
14
|
float mouse_x, mouse_y;
|
24
15
|
float scale_x, scale_y;
|
25
16
|
float update_interval;
|
@@ -32,7 +23,7 @@ struct Gosu::Input::Impl
|
|
32
23
|
CGPoint point = [ui_touch locationInView:view];
|
33
24
|
|
34
25
|
return (Touch) {
|
35
|
-
.id = (__bridge void*)
|
26
|
+
.id = (__bridge void*)ui_touch,
|
36
27
|
.x = (float)point.x * scale_x,
|
37
28
|
.y = (float)point.y * scale_y,
|
38
29
|
};
|
@@ -53,33 +44,22 @@ Gosu::Input::~Input()
|
|
53
44
|
{
|
54
45
|
}
|
55
46
|
|
56
|
-
void Gosu::Input::feed_touch_event(
|
47
|
+
void Gosu::Input::feed_touch_event(function<void (Touch)>& callback, void* touches)
|
57
48
|
{
|
58
49
|
NSSet* ui_touches = (__bridge NSSet*) touches;
|
59
50
|
|
60
51
|
pimpl->current_touches_vector.reset();
|
61
52
|
|
62
|
-
|
63
|
-
|
64
|
-
if (type == 0) {
|
53
|
+
if (&callback == &on_touch_began) {
|
65
54
|
[pimpl->current_touches_set unionSet:ui_touches];
|
66
|
-
callback = &on_touch_began;
|
67
|
-
}
|
68
|
-
else if (type == 1) {
|
69
|
-
callback = &on_touch_moved;
|
70
|
-
}
|
71
|
-
else if (type == 2) {
|
72
|
-
[pimpl->current_touches_set minusSet:ui_touches];
|
73
|
-
callback = &on_touch_ended;
|
74
55
|
}
|
75
|
-
else if (
|
56
|
+
else if (&callback == &on_touch_ended || &callback == &on_touch_cancelled) {
|
76
57
|
[pimpl->current_touches_set minusSet:ui_touches];
|
77
|
-
callback = &on_touch_cancelled;
|
78
58
|
}
|
79
59
|
|
80
|
-
if (callback
|
60
|
+
if (callback) {
|
81
61
|
for (UITouch* ui_touch in ui_touches) {
|
82
|
-
|
62
|
+
callback(pimpl->translate_touch(ui_touch));
|
83
63
|
}
|
84
64
|
}
|
85
65
|
}
|
@@ -152,9 +132,7 @@ double Gosu::Input::accelerometer_z() const
|
|
152
132
|
|
153
133
|
void Gosu::Input::update()
|
154
134
|
{
|
155
|
-
// Check for dead touches and remove from vector if
|
156
|
-
// necessary
|
157
|
-
|
135
|
+
// Check for dead touches and remove from vector if necessary.
|
158
136
|
NSMutableSet* dead_touches = nil;
|
159
137
|
|
160
138
|
for (UITouch* touch in pimpl->current_touches_set) {
|
@@ -166,9 +144,7 @@ void Gosu::Input::update()
|
|
166
144
|
}
|
167
145
|
|
168
146
|
// Something was deleted, we will need the set.
|
169
|
-
if (!dead_touches)
|
170
|
-
dead_touches = [NSMutableSet new];
|
171
|
-
}
|
147
|
+
if (!dead_touches) dead_touches = [NSMutableSet new];
|
172
148
|
[dead_touches addObject:touch];
|
173
149
|
}
|
174
150
|
|
@@ -187,12 +163,18 @@ void Gosu::Input::update()
|
|
187
163
|
|
188
164
|
Gosu::TextInput* Gosu::Input::text_input() const
|
189
165
|
{
|
190
|
-
return
|
166
|
+
return pimpl->text_input;
|
191
167
|
}
|
192
168
|
|
193
|
-
void Gosu::Input::set_text_input(TextInput*
|
169
|
+
void Gosu::Input::set_text_input(TextInput* text_input)
|
194
170
|
{
|
195
|
-
|
171
|
+
if (text_input) {
|
172
|
+
pimpl->text_input = text_input;
|
173
|
+
[pimpl->view becomeFirstResponder];
|
174
|
+
} else {
|
175
|
+
[pimpl->view resignFirstResponder];
|
176
|
+
pimpl->text_input = nullptr;
|
177
|
+
}
|
196
178
|
}
|
197
179
|
|
198
180
|
#endif
|
data/src/Macro.cpp
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "Macro.hpp"
|
2
2
|
#include "DrawOpQueue.hpp"
|
3
|
+
#include <Gosu/Image.hpp>
|
3
4
|
#include <cmath>
|
4
5
|
#include <algorithm>
|
5
6
|
#include <functional>
|
@@ -58,7 +59,8 @@ struct Gosu::Macro::Impl
|
|
58
59
|
Float c[8];
|
59
60
|
|
60
61
|
// Rows 1, 2
|
61
|
-
c[2] = x1
|
62
|
+
c[2] = x1;
|
63
|
+
c[5] = y1;
|
62
64
|
|
63
65
|
// The logic below assumes x2 != x4, i.e. row7 can be used to eliminate
|
64
66
|
// the leftmost value in row 8 and afterwards the values in rows 3 & 4.
|
@@ -221,7 +223,13 @@ const Gosu::GLTexInfo* Gosu::Macro::gl_tex_info() const
|
|
221
223
|
|
222
224
|
Gosu::Bitmap Gosu::Macro::to_bitmap() const
|
223
225
|
{
|
224
|
-
|
226
|
+
return Gosu::Graphics::render(pimpl->width, pimpl->height, [this] {
|
227
|
+
draw(0, 0, Color::WHITE,
|
228
|
+
pimpl->width, 0, Color::WHITE,
|
229
|
+
0, pimpl->height, Color::WHITE,
|
230
|
+
pimpl->width, pimpl->height, Color::WHITE,
|
231
|
+
0, AM_DEFAULT);
|
232
|
+
}).data().to_bitmap();
|
225
233
|
}
|
226
234
|
|
227
235
|
unique_ptr<Gosu::ImageData> Gosu::Macro::subimage(int x, int y, int width, int height) const
|
@@ -0,0 +1,241 @@
|
|
1
|
+
#include "MarkupParser.hpp"
|
2
|
+
#include <Gosu/Utility.hpp>
|
3
|
+
#include "utf8proc.h"
|
4
|
+
|
5
|
+
#include <cstring>
|
6
|
+
using namespace std;
|
7
|
+
|
8
|
+
// Helper method for allowing CJK text to have line breaks in the absence of whitespace.
|
9
|
+
static bool should_allow_break_after_codepoint(utf8proc_int32_t cp)
|
10
|
+
{
|
11
|
+
return (cp >= 0x3040 && cp <= 0x3096) || // Hiragana
|
12
|
+
(cp >= 0x30a0 && cp <= 0x30fa) || // Katakana
|
13
|
+
(cp >= 0x4e00 && cp <= 0x9fff) || // CJK Unified Ideographs
|
14
|
+
(cp >= 0x3400 && cp <= 0x4db5) || // CJK Unified Ideographs Extension A
|
15
|
+
(cp >= 0xac00 && cp <= 0xd7af); // Precomposed Hangul syllables
|
16
|
+
}
|
17
|
+
|
18
|
+
unsigned Gosu::MarkupParser::flags() const
|
19
|
+
{
|
20
|
+
unsigned flags = 0;
|
21
|
+
if (b > 0) flags |= Gosu::FF_BOLD;
|
22
|
+
if (i > 0) flags |= Gosu::FF_ITALIC;
|
23
|
+
if (u > 0) flags |= Gosu::FF_UNDERLINE;
|
24
|
+
return flags;
|
25
|
+
}
|
26
|
+
|
27
|
+
bool Gosu::MarkupParser::match_and_skip(const char* chars, size_t length)
|
28
|
+
{
|
29
|
+
if (strncmp(markup, chars, length) != 0) return false;
|
30
|
+
|
31
|
+
// Finish building the current substring (if any) before matching.
|
32
|
+
add_current_substring();
|
33
|
+
// Skip chars.
|
34
|
+
markup += length;
|
35
|
+
return true;
|
36
|
+
}
|
37
|
+
|
38
|
+
bool Gosu::MarkupParser::parse_markup()
|
39
|
+
{
|
40
|
+
// Open and close bold text spans.
|
41
|
+
if (match_and_skip("<b>")) {
|
42
|
+
b += 1;
|
43
|
+
return true;
|
44
|
+
}
|
45
|
+
if (match_and_skip("</b>")) {
|
46
|
+
b -= 1;
|
47
|
+
return true;
|
48
|
+
}
|
49
|
+
|
50
|
+
// Open and close underlined text spans.
|
51
|
+
if (match_and_skip("<u>")) {
|
52
|
+
u += 1;
|
53
|
+
return true;
|
54
|
+
}
|
55
|
+
if (match_and_skip("</u>")) {
|
56
|
+
u -= 1;
|
57
|
+
return true;
|
58
|
+
}
|
59
|
+
|
60
|
+
// Open and close italic text spans.
|
61
|
+
if (match_and_skip("<i>")) {
|
62
|
+
i += 1;
|
63
|
+
return true;
|
64
|
+
}
|
65
|
+
if (match_and_skip("</i>")) {
|
66
|
+
i -= 1;
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
|
70
|
+
// Reset to previous color.
|
71
|
+
if (match_and_skip("</c>")) {
|
72
|
+
if (c.size() > 1) {
|
73
|
+
c.pop_back();
|
74
|
+
}
|
75
|
+
else {
|
76
|
+
// If the user pops the color stack empty, go back to the default color.
|
77
|
+
c[0] = Color::WHITE;
|
78
|
+
}
|
79
|
+
return true;
|
80
|
+
}
|
81
|
+
|
82
|
+
// Leave the trickiest case for last - changing the current text color.
|
83
|
+
if (strncmp(markup, "<c=", 3) == 0) {
|
84
|
+
// Count hex chars following the <c= string.
|
85
|
+
const char* hex = markup + 3;
|
86
|
+
const char* valid_hex_chars = "0123456789ABCDEFabcdef";
|
87
|
+
size_t hex_chars = strspn(hex, valid_hex_chars);
|
88
|
+
|
89
|
+
if (*(hex + hex_chars) != '>' || (hex_chars != 3 && hex_chars != 6 && hex_chars != 8)) {
|
90
|
+
// Does not match <c=[0-9A-Fa-f]{3,6,8}> -> not considered valid markup.
|
91
|
+
return false;
|
92
|
+
}
|
93
|
+
|
94
|
+
add_current_substring();
|
95
|
+
|
96
|
+
auto argb = strtoul(hex, nullptr, 16);
|
97
|
+
|
98
|
+
if (hex_chars == 3) {
|
99
|
+
// Expand 0xrgb to 0xFFrrggbb:
|
100
|
+
auto r = argb >> 8 & 0x7;
|
101
|
+
auto g = argb >> 4 & 0x7;
|
102
|
+
auto b = argb >> 0 & 0x7;
|
103
|
+
argb = 0xff000000 | r << 20 | r << 16 | g << 12 | g << 8 | b << 4 | b << 0;
|
104
|
+
}
|
105
|
+
else if (hex_chars == 6) {
|
106
|
+
// Expand 0xrrggbb to 0xFFrrggbb:
|
107
|
+
argb = 0xff000000 | argb;
|
108
|
+
}
|
109
|
+
|
110
|
+
c.emplace_back(argb);
|
111
|
+
|
112
|
+
markup += (4 + hex_chars);
|
113
|
+
return true;
|
114
|
+
}
|
115
|
+
|
116
|
+
// Everything else is not considered markup.
|
117
|
+
return false;
|
118
|
+
}
|
119
|
+
|
120
|
+
bool Gosu::MarkupParser::parse_escape_entity()
|
121
|
+
{
|
122
|
+
// These are not entities (images) but escapes for markup characters.
|
123
|
+
if (match_and_skip("<")) {
|
124
|
+
add_composed_substring(u32string(1, '<'));
|
125
|
+
return true;
|
126
|
+
}
|
127
|
+
if (match_and_skip(">")) {
|
128
|
+
add_composed_substring(u32string(1, '>'));
|
129
|
+
return true;
|
130
|
+
}
|
131
|
+
if (match_and_skip("&")) {
|
132
|
+
add_composed_substring(u32string(1, '&'));
|
133
|
+
return true;
|
134
|
+
}
|
135
|
+
|
136
|
+
// These are the only recognized entities - disregard anything else.
|
137
|
+
return false;
|
138
|
+
}
|
139
|
+
|
140
|
+
void Gosu::MarkupParser::add_current_substring()
|
141
|
+
{
|
142
|
+
if (!substring.empty()) {
|
143
|
+
add_composed_substring(utf8_to_composed_utc4(substring));
|
144
|
+
substring.clear();
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
void Gosu::MarkupParser::add_composed_substring(u32string&& substring)
|
149
|
+
{
|
150
|
+
FormattedString fstr;
|
151
|
+
fstr.text = substring;
|
152
|
+
fstr.flags = flags();
|
153
|
+
fstr.color = c.back();
|
154
|
+
|
155
|
+
if (! substrings.empty() && substrings.back().can_be_merged_with(fstr)) {
|
156
|
+
substrings.back().text.append(move(fstr.text));
|
157
|
+
}
|
158
|
+
else {
|
159
|
+
substrings.emplace_back(move(fstr));
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
void Gosu::MarkupParser::flush_to_consumer()
|
164
|
+
{
|
165
|
+
if (! substrings.empty()) {
|
166
|
+
consumer(move(substrings));
|
167
|
+
substrings.clear();
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
Gosu::MarkupParser::MarkupParser(const char* markup, unsigned base_flags, bool split_words,
|
172
|
+
function<void (vector<FormattedString>)> consumer)
|
173
|
+
: markup(markup), consumer(move(consumer))
|
174
|
+
{
|
175
|
+
word_state = (split_words ? ADDING_WORD : IGNORE_WORDS);
|
176
|
+
|
177
|
+
b = (base_flags & FF_BOLD) ? 1 : 0;
|
178
|
+
i = (base_flags & FF_ITALIC) ? 1 : 0;
|
179
|
+
u = (base_flags & FF_UNDERLINE) ? 1 : 0;
|
180
|
+
}
|
181
|
+
|
182
|
+
void Gosu::MarkupParser::parse()
|
183
|
+
{
|
184
|
+
auto end_of_markup = markup + strlen(markup);
|
185
|
+
|
186
|
+
while (markup < end_of_markup) {
|
187
|
+
if (*markup == '<' && parse_markup()) {
|
188
|
+
continue;
|
189
|
+
}
|
190
|
+
if (*markup == '&' && parse_escape_entity()) {
|
191
|
+
continue;
|
192
|
+
}
|
193
|
+
|
194
|
+
// The newline character always terminates the current line, regardless of whether this
|
195
|
+
// parser is trying to split words.
|
196
|
+
if (*markup == '\n') {
|
197
|
+
// Explicitly add the trailing \n to the current substring so that the consumer can
|
198
|
+
// distinguish between line breaks and word breaks in split_words mode.
|
199
|
+
++markup;
|
200
|
+
add_current_substring();
|
201
|
+
flush_to_consumer();
|
202
|
+
// Avoid incrementing ++markup again.
|
203
|
+
continue;
|
204
|
+
}
|
205
|
+
|
206
|
+
utf8proc_int32_t codepoint;
|
207
|
+
auto len = utf8proc_iterate((utf8proc_uint8_t*) markup, end_of_markup - markup, &codepoint);
|
208
|
+
// Cancel parsing when invalid UTF-8 is encountered.
|
209
|
+
if (len < 1) break;
|
210
|
+
|
211
|
+
auto* properties = utf8proc_get_property(codepoint);
|
212
|
+
// Also check the BiDi class to filter out non-breaking spaces.
|
213
|
+
bool whitespace_except_newline = properties->category == UTF8PROC_CATEGORY_ZS &&
|
214
|
+
properties->bidi_class == UTF8PROC_BIDI_CLASS_WS;
|
215
|
+
|
216
|
+
if (whitespace_except_newline && word_state == ADDING_WORD) {
|
217
|
+
// We are in word-parsing mode, and this is was the end of a word.
|
218
|
+
add_current_substring();
|
219
|
+
flush_to_consumer();
|
220
|
+
word_state = ADDING_WHITESPACE;
|
221
|
+
} else if (!whitespace_except_newline && word_state == ADDING_WHITESPACE) {
|
222
|
+
// We are in word-parsing mode, and this is was the start of a word.
|
223
|
+
add_current_substring();
|
224
|
+
flush_to_consumer();
|
225
|
+
word_state = ADDING_WORD;
|
226
|
+
}
|
227
|
+
|
228
|
+
substring.append(markup, len);
|
229
|
+
markup += len;
|
230
|
+
|
231
|
+
if (word_state != IGNORE_WORDS && should_allow_break_after_codepoint(codepoint)) {
|
232
|
+
// Flush each individual CJK character out as a word so that the TextBuilder can insert
|
233
|
+
// line breaks as needed.
|
234
|
+
add_current_substring();
|
235
|
+
flush_to_consumer();
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
add_current_substring();
|
240
|
+
flush_to_consumer();
|
241
|
+
}
|