/
example.gui_script
429 lines (357 loc) · 19.3 KB
/
example.gui_script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
local richtext = require "richtext.richtext"
local color = require "richtext.color"
local tags = require "richtext.tags"
local sherlock = [[At three o'clock precisely I was at Baker Street, but Holmes had not yet returned. The landlady informed me that he had left the house shortly after eight o'clock in the morning. I sat down beside the fire, however, with the intention of awaiting him, however long he might be. I was already deeply interested in his inquiry, for, though it was surrounded by none of the grim and strange features which were associated with the two crimes which I have already recorded, still, the nature of the case and the exalted station of his client gave it a character of its own. Indeed, apart from the nature of the investigation which my friend had on hand, there was something in his masterly grasp of a situation, and his keen, incisive reasoning, which made it a pleasure to me to study his system of work, and to follow the quick, subtle methods by which he disentangled the most inextricable mysteries. So accustomed was I to his invariable success that the very possibility of his failing had ceased to enter into my head. It was close upon four before the door opened, and a drunken-looking groom, ill-kempt and side-whiskered, with an inflamed face and disreputable clothes, walked into the room. Accustomed as I was to my friend's amazing powers in the use of disguises, I had to look three times before I was certain that it was indeed he. With a nod he vanished into the bedroom, whence he emerged in five minutes tweed-suited and respectable, as of old. Putting his hands into his pockets, he stretched out his legs in front of the fire and laughed heartily for some minutes. 'Well, really!' he cried, and then he choked and laughed again until he was obliged to lie back, limp and helpless, in the chair. 'What is it?' 'It's quite too funny. I am sure you could never guess how I employed my morning, or what I ended by doing.' 'I can't imagine. I suppose that you have been watching the habits, and perhaps the house, of Miss Irene Adler.' 'Quite so; but the sequel was rather unusual. I will tell you, however. I left the house a little after eight o'clock this morning in the character of a groom out of work. There is a wonderful sympathy and freemasonry among horsey men. Be one of them, and you will know all that there is to know. I soon found Briony Lodge. It is a bijou villa, with a garden at the back, but built out in front right up to the road, two stories. Chubb lock to the door. Large sitting-room on the right side, well furnished, with long windows almost to the floor, and those preposterous English window fasteners which a child could open. Behind there was nothing remarkable, save that the passage window could be reached from the top of the coach-house. I walked round it and examined it closely from every point of view, but without noting anything else of interest. 'I then lounged down the street and found, as I expected, that there was a mews in a lane which runs down by one wall of the garden. I lent the ostlers a hand in rubbing down their horses, and received in exchange twopence, a glass of half and half, two fills of shag tobacco, and as much information as I could desire about Miss Adler, to say nothing of half a dozen other people in the neighbourhood in whom I was not in the least interested, but whose biographies I was compelled to listen to.']]
local ROBOTO = {
regular = hash("Roboto-Regular"),
italic = hash("Roboto-Italic"),
bold = hash("Roboto-Bold"),
bold_italic = hash("Roboto-BoldItalic"),
}
local NANUM = {
regular = hash("Nanum-Regular"),
}
local function merge(...)
local out = {}
for _,words in pairs({...}) do
for _,word in pairs(words) do
table.insert(out, word)
end
end
return out
end
local function create_long_text_example()
local settings = { position = vmath.vector3(10, 1075, 0), width = 600, combine_words = true }
return richtext.create(sherlock, "Roboto-Regular", settings)
end
local function create_paragraph_example()
local settings = { position = vmath.vector3(10, 1075, 0), width = 600 }
local text = "<p>This is some rather long text that wraps around and has 0.5 lines of space after its last line.</p>\n<p=2.5>This one has 2.5 lines of space.</p>\nThis is just regular text."
return richtext.create(text, "Roboto-Regular", settings)
end
local function create_complex_example()
local settings = {
fonts = {
Roboto = ROBOTO,
Nanum = NANUM,
},
layers = {
fonts = {
[hash("Roboto-Regular")] = hash("roboto-regular"),
[hash("Roboto-Italic")] = hash("roboto-italic"),
[hash("Roboto-Bold")] = hash("roboto-bold"),
[hash("Roboto-BoldItalic")] = hash("roboto-bold_italic"),
[hash("Nanum-Regular")] = hash("nanum-regular"),
},
images = {
[hash("smileys")] = hash("image-smileys"),
},
spinemodels = {
[hash("spineboy")] = hash("spine-spineboy"),
},
},
width = 480,
position = vmath.vector3(0, 0, 0),
parent = gui.get_node("bg"),
color = vmath.vector4(0.95, 0.95, 1.0, 1.0),
align = richtext.ALIGN_LEFT,
line_spacing = 0.85,
}
local text = "<size=3><outline=green>>RichText<</outline></size><shadow=red>Lorem</shadow> <color=0,0.5,0,1>ipsum </color><img=smileys:zombie/> dolor <color=red>sit </color><color=#ff00ffff>amet, </color><size=1.15><font=Nanum>consectetur </font></size>adipiscing elit.<br/><b>Nunc </b>tincidunt <b><i>mattis</i> libero</b> <i>non viverra</i>.\n\nNullam ornare <img=smileys:hungry></img>accumsan rhoncus.\n\n<size=0.5>Nunc placerat nibh a purus auctor, id scelerisque massa</size> <size=2>rutrum.</size>"
local words, metrics = richtext.create(text, "Roboto", settings)
print("The text consists of " .. tostring(metrics.char_count) .. " characters")
print("The plain-text is " .. richtext.plaintext(words))
-- adjust background to cover text
gui.set_size(settings.parent, vmath.vector3(metrics.width, metrics.height, 0))
return words
end
local function create_align_example()
local settings_valign_bottom = { position = vmath.vector3(10, 870, 0), valign = richtext.VALIGN_BOTTOM, width = 700 }
local valign_bottom = richtext.create("Align <size=2>EVERYTHING</size> to\nthe <size=0.5>BOTTOM</size>of each line", "Roboto-Regular", settings_valign_bottom)
local settings_valign_top = { position = vmath.vector3(10, 720, 0), valign = richtext.VALIGN_TOP, width = 700 }
local valign_top = richtext.create("Align <size=2>EVERYTHING</size> to\nthe <size=0.5>TOP</size>of each line", "Roboto-Regular", settings_valign_top)
local settings_valign_middle = { position = vmath.vector3(10, 570, 0), valign = richtext.VALIGN_MIDDLE, width = 700 }
local valign_middle = richtext.create("Align <size=2>EVERYTHING</size> to\nthe <size=0.5>MIDDLE</size>of each line", "Roboto-Regular", settings_valign_middle)
local settings_align_justify = { position = vmath.vector3(10, 420, 0), align = richtext.ALIGN_JUSTIFY, width = 300 }
local justify = richtext.create("Justify this text\nDo it for both lines", "Roboto-Regular", settings_align_justify)
local settings_align_left = { position = vmath.vector3(10, 310, 0), align = richtext.ALIGN_LEFT }
local left = richtext.create("Left align this text\nDo it for both lines", "Roboto-Regular", settings_align_left)
local settings_align_right = { position = vmath.vector3(640, 200, 0), align = richtext.ALIGN_RIGHT }
local right = richtext.create("Right align this text\nDo it for both lines", "Roboto-Regular", settings_align_right)
local settings_align_center = { position = vmath.vector3(320, 90, 0), align = richtext.ALIGN_CENTER }
local center = richtext.create("Center words around the specified position\nAnd these words as well", "Roboto-Regular", settings_align_center)
return merge(justify, left, right, center, valign_top, valign_middle, valign_bottom)
end
local function create_clickable_words_example(self)
local settings = { position = vmath.vector3(10, 130, 0) }
return richtext.create("Click <color=red><a=click_text>here</a></color> or <color=green><a=click_text>here</a></color> to generate a message", "Roboto-Regular", settings)
end
local function create_truncate_example()
-- add a "cursor" that should follow at the end of the text as it is revealed
local cursor = gui.get_node("cursor")
local settings = { position = vmath.vector3(0, 240, 0) }
local words, metrics = richtext.create("This text should be shown one <img=smileys:cyclops/> at a time...&zwsp;&zwsp;&zwsp;&zwsp;", "Roboto-Regular", settings)
local length = 0
local max_length = #words
local options = { words = true }
richtext.truncate(words, length, options)
timer.delay(0.1, true, function(self, handle, time_elapsed)
if #words == 0 then
timer.cancel(handle)
return
end
length = length + 1
if length > max_length then
-- alternate between truncating full words and per character
if options.words then
options.words = false
max_length = metrics.char_count
else
options.words = true
max_length = #words
end
length = 0
end
local last_word = richtext.truncate(words, length, options)
local pos = vmath.vector3(settings.position)
if last_word then
pos = gui.get_position(last_word.node)
pos.x = pos.x + last_word.metrics.width + 5
end
gui.set_position(cursor, pos)
gui.set_enabled(cursor, not gui.is_enabled(cursor))
end)
return words
end
local function wave_words(words)
local waves = richtext.tagged(words, "wave")
for _,wave in pairs(waves) do
for i,word in ipairs(words) do
if word == wave then
table.remove(words, i)
break
end
end
gui.delete_node(wave.node)
local chars = richtext.characters(wave)
for i,char in ipairs(chars) do
local pos = gui.get_position(char.node)
local pos_2 = gui.get_position(char.node)
pos_2.y = pos_2.y - 3
pos_2.x = pos_2.x + 2
gui.set_position(char.node, pos_2)
local amplitude = tonumber(wave.tags.wave) or 3
gui.animate(char.node, gui.PROP_POSITION, pos + vmath.vector3(0, amplitude, 0), gui.EASING_INOUTSINE, 0.6, i * 0.12112, nil, gui.PLAYBACK_LOOP_PINGPONG)
gui.animate(char.node, "color.x", 0.9, gui.EASING_INOUTSINE, 0.2, i * 0.12112, nil, gui.PLAYBACK_LOOP_PINGPONG)
gui.animate(char.node, "color.y", 0.9, gui.EASING_INOUTSINE, 0.6, i * 0.12112, nil, gui.PLAYBACK_LOOP_PINGPONG)
gui.animate(char.node, "color.z", 0.9, gui.EASING_INOUTSINE, 10, i * 0.12112, nil, gui.PLAYBACK_LOOP_PINGPONG)
gui.animate(char.node, "scale", 1.2, gui.EASING_INOUTSINE, 1.5, i * 0.12112, nil, gui.PLAYBACK_LOOP_PINGPONG)
table.insert(words, char)
end
end
end
local function create_characters_example()
local settings = { position = vmath.vector3(0, 290, 0) }
local words, metrics = richtext.create("Our <wave><color=#ff69b4>princess</color></wave> is in another <wave=25><color=red>castle</color></wave>", "Roboto-Regular", settings)
wave_words(words)
return words
end
local function create_spine_example()
local settings = { position = vmath.vector3(320, 450, 0), align = richtext.ALIGN_CENTER }
return richtext.create("A running <size=0.05><spine=spineboy:run/></size> in my text", "Roboto-Regular", settings)
end
local function create_language_example()
local settings_latin = { position = vmath.vector3(20, 410, 0), align = richtext.ALIGN_LEFT }
local latin = richtext.create("The <wave>quick</wave> brown fox", "Roboto-Regular", settings_latin)
wave_words(latin)
local settings_russian = { position = vmath.vector3(20, 380, 0), align = richtext.ALIGN_LEFT }
local russian = richtext.create("Быстрая <wave>коричневая</wave> лиса", "Roboto-Regular", settings_russian)
wave_words(russian)
local settings_korean = { position = vmath.vector3(20, 350, 0), align = richtext.ALIGN_LEFT }
local korean = richtext.create("빠른 <wave>갈색</wave> 여우", "UnYetgul-Regular", settings_korean)
wave_words(korean)
return merge(latin, russian, korean)
end
local function create_linebreak_example()
local settings_nobr = { position = vmath.vector3(0, 600, 0), align = richtext.ALIGN_LEFT, width = 245 }
local words1 = richtext.create("The image at the end should end up on a new line <nobr><img=smileys:cyclops/></nobr>", "Roboto-Regular", settings_nobr)
local settings_right = { position = vmath.vector3(320, 600, 0), align = richtext.ALIGN_LEFT, width = 245 }
local words2 = richtext.create("The image at the end should end up on a new line <img=smileys:cyclops/>", "Roboto-Regular", settings_right)
local settings_nobr = { position = vmath.vector3(0, 900, 0), align = richtext.ALIGN_LEFT, width = 245 }
local longword = richtext.create("THIS IS AVERYLONGWORDWITHOUTANYLINEBREAKS", "Roboto-Regular", settings_nobr)
return merge(words1, words2, longword)
end
local function create_overlapping_tags_example()
local settings_overlap = {
fonts = {
Roboto = ROBOTO
},
position = vmath.vector3(0, 650, 0),
}
local text = "<size=0.5>You can even <i>overlap tags<size=0.65> like italic <b>bold italic : </i>bold regular</b> back to normal,\n" ..
"<color=red>or <color=#98e9f8>nest</color> tags with the <wave=1>same</size> name. Red text, <color=yellow>nested <b>" ..
"yellow,</color> back to</b> red,</color> no</wave>rmal."
local words = richtext.create(text, "Roboto", settings_overlap)
wave_words(words)
return words
end
local function create_html_entities_example()
local settings_ltgt = { position = vmath.vector3(20, 900, 0), align = richtext.ALIGN_LEFT, width = 640 }
local ltgt = richtext.create("5 > 2 and 2 < 5", "Roboto-Regular", settings_ltgt)
local settings_nbsp = { position = vmath.vector3(20, 700, 0), align = richtext.ALIGN_LEFT, width = 280 }
local nbsp = richtext.create("Some Padding 10 km\nSome Padding 10 km", "Roboto-Regular", settings_nbsp)
local settings_zwsp = { position = vmath.vector3(20, 500, 0), align = richtext.ALIGN_LEFT, width = 640 }
local zwsp = richtext.create("There is space&zwsp;&zwsp;&zwsp;&zwsp;here but you barely see it\nThere is no spacehere", "Roboto-Regular", settings_zwsp)
return merge(ltgt, nbsp, zwsp)
end
local function delay(seconds)
local co = coroutine.running()
assert(co, "You must call delay() from a coroutine")
timer.delay(seconds, false, function()
coroutine.resume(co)
end)
coroutine.yield()
end
local function fade_in(node, easing, duration, delay)
local color = gui.get_color(node)
local to = vmath.vector4(color)
color.w = 0
gui.set_color(node, color)
gui.animate(node, gui.PROP_COLOR, to, easing, duration, delay)
end
local function create_fade_in_example()
local settings = { position = vmath.vector3(20, 900, 0), align = richtext.ALIGN_LEFT, width = 640 }
local words = richtext.create("This is a text that should fade in character by character", "Roboto-Regular", settings)
local fade_duration = 0.5
local fade_delay = 0.1
coroutine.wrap(function()
-- disable all words
for i=1,#words do
gui.set_enabled(words[i].node, false)
end
-- iterate over words and fade them in character by character
for i=1,#words do
local word = words[i]
-- split into individual characters and fade in
local characters = richtext.characters(word)
for i,char in ipairs(characters) do
fade_in(char.node, go.EASING_LINEAR, fade_duration, (i - 1) * fade_delay)
end
-- start a timer to delete the individual characters and re-enable original word
local max_delay = (#characters - 1) * fade_delay
timer.delay(fade_duration + max_delay, false, function()
for _,char in ipairs(characters) do
gui.delete_node(char.node)
end
gui.set_enabled(word.node, true)
end)
-- wait before dealing with the next word
delay(max_delay)
end
end)()
return words
end
local function create_colors_example()
local settings = { position = vmath.vector3(320, 450, 0), align = richtext.ALIGN_CENTER }
return richtext.create("Hold my <color=asparagus>asparagus</color>! I have <color=blood>blood</color> on my <color=denim>jeans</color>", "Roboto-Regular", settings)
end
local function create_repeat_example()
local settings = { position = vmath.vector3(320, 450, 0), align = richtext.ALIGN_CENTER, fonts = { Roboto = ROBOTO } }
return richtext.create("Now repeat<repeat=7>\n<b>after <color=red><i>me</i></color></b></repeat>\nWell done!", "Roboto", settings)
end
local function create_custom_tag_example()
local settings = { position = vmath.vector3(320, 450, 0), align = richtext.ALIGN_CENTER, fonts = { Roboto = ROBOTO } }
tags.register("boldred", function(params, settings)
tags.apply("color", "red", settings)
tags.apply("b", nil, settings)
end)
return richtext.create("I am <boldred>bold and red</boldred>!", "Roboto", settings)
end
function init(self)
msg.post(".", "acquire_input_focus")
-- add some custom colors
color.add("blood", "#8A0303")
color.add("asparagus", "#87a96b")
color.add("denim", "#1560bd")
local EXAMPLES = {
{ name = "COMPLEX", fn = create_complex_example },
{ name = "LONG TEXT", fn = create_long_text_example },
{ name = "ALIGN", fn = create_align_example },
{ name = "TRUNCATE", fn = create_truncate_example },
{ name = "CHARACTERS", fn = create_characters_example },
--{ name = "SPINE", fn = create_spine_example },
{ name = "LANGUAGE", fn = create_language_example },
{ name = "LINEBREAK", fn = create_linebreak_example },
{ name = "CLICKABLE", fn = create_clickable_words_example },
{ name = "OVERLAPPING", fn = create_overlapping_tags_example },
{ name = "HTML ENTITIES", fn = create_html_entities_example },
{ name = "FADE IN", fn = create_fade_in_example },
{ name = "PARAGRAPHS", fn = create_paragraph_example },
{ name = "COLORS", fn = create_colors_example },
{ name = "REPEAT", fn = create_repeat_example },
{ name = "CUSTOM TAGS", fn = create_custom_tag_example },
}
self.back = gui.get_node("back/bg")
self.buttons = {}
for i,example in ipairs(EXAMPLES) do
local button = gui.clone_tree(self.back)
local text = button[hash("back/text")]
local bg = button[hash("back/bg")]
local position = vmath.vector3(110 + ((i - 1) % 3) * 210, 1100 - math.floor((i - 1) / 3) * 50, 0)
gui.set_position(bg, position)
gui.set_text(text, example.name)
self.buttons[i] = {
node = bg,
name = example.name,
example = example.fn,
}
end
gui.set_enabled(self.back, false)
end
function on_message(self, message_id, message, sender)
print(message_id)
if message_id == hash("click_text") then
gui.animate(gui.get_node("flower"), gui.PROP_POSITION, gui.get_position(gui.get_node(message.node_id)), gui.EASING_INOUTQUAD, 1)
end
end
local function enable_menu(self, enabled)
gui.set_enabled(self.back, not enabled)
for _,button in ipairs(self.buttons) do
gui.set_enabled(button.node, enabled)
end
end
local function delete_words(words)
if not words then return end
local word = table.remove(words)
while word do
if word.node then
gui.delete_node(word.node)
end
word = table.remove(words)
end
end
local function pick_node(node, x, y)
return gui.is_enabled(node) and gui.pick_node(node, x, y)
end
function on_input(self, action_id, action)
if not action_id == hash("touch") or not action.released then
return
end
if pick_node(self.back, action.x, action.y) then
enable_menu(self, true)
delete_words(self.words)
gui.set_position(gui.get_node("flower"), vmath.vector3(0))
gui.set_position(gui.get_node("cursor"), vmath.vector3(0))
gui.set_size(gui.get_node("bg"), vmath.vector3(0))
else
-- click on menu button?
for _,button in ipairs(self.buttons) do
if pick_node(button.node, action.x, action.y) then
self.words = button.example(self)
enable_menu(self, false)
return
end
end
return richtext.on_click(self.words or {}, action)
end
end