hokusai-zero 0.2.6.pre.pinephone2 → 0.2.6.pre.pinephone3
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/ast/src/core/input.c +0 -135
- data/ast/src/core/input.h +0 -33
- data/ast/test/hokusai.c +0 -2
- data/hokusai.gemspec +1 -1
- data/ui/examples/forum/file.rb +1 -1
- data/ui/examples/forum/post.rb +0 -1
- data/ui/examples/forum.rb +7 -7
- data/ui/examples/keyboard.rb +47 -0
- data/ui/examples/shader/test.rb +28 -18
- data/ui/examples/spreadsheet.rb +12 -11
- data/ui/lib/lib_hokusai.rb +19 -37
- data/ui/spec/hokusai/e2e/keyboard_spec.rb +52 -0
- data/ui/src/hokusai/assets/icons/outline/arrow-big-up.svg +19 -0
- data/ui/src/hokusai/assets/icons/outline/backspace.svg +20 -0
- data/ui/src/hokusai/backends/raylib.rb +7 -5
- data/ui/src/hokusai/backends/sdl2/config.rb +9 -6
- data/ui/src/hokusai/backends/sdl2/font.rb +3 -1
- data/ui/src/hokusai/backends/sdl2.rb +164 -65
- data/ui/src/hokusai/blocks/input.rb +1 -1
- data/ui/src/hokusai/blocks/keyboard.rb +234 -0
- data/ui/src/hokusai/blocks/slider.rb +139 -0
- data/ui/src/hokusai/commands/rect.rb +2 -2
- data/ui/src/hokusai/events/touch.rb +5 -11
- data/ui/src/hokusai/painter.rb +1 -1
- data/ui/src/hokusai/types.rb +152 -49
- data/ui/src/hokusai/util/wrap_stream.rb +197 -0
- data/ui/src/hokusai.rb +61 -30
- metadata +8 -6
- data/ast/test/input.c +0 -44
- data/ui/src/hokusai/backends/embedded/config.rb +0 -48
- data/ui/src/hokusai/backends/embedded/font.rb +0 -112
- data/ui/src/hokusai/backends/embedded/keys.rb +0 -124
- data/ui/src/hokusai/backends/embedded.rb +0 -540
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
module Hokusai::Blocks
|
3
|
+
class Slider < Hokusai::Block
|
4
|
+
template <<~EOF
|
5
|
+
[template]
|
6
|
+
empty {
|
7
|
+
@click="start_slider"
|
8
|
+
@mousemove="move_slider"
|
9
|
+
@mouseup="stop_slider"
|
10
|
+
}
|
11
|
+
EOF
|
12
|
+
|
13
|
+
uses(empty: Hokusai::Blocks::Empty)
|
14
|
+
|
15
|
+
computed :fill, default: [61,171,211], convert: Hokusai::Color
|
16
|
+
computed :initial, default: 0.0, convert: proc(&:to_f)
|
17
|
+
computed :size, default: 50.0, convert: proc(&:to_f)
|
18
|
+
computed :step, default: 20.0, convert: proc(&:to_f)
|
19
|
+
computed :min, default: 0.0, convert: proc(&:to_f)
|
20
|
+
computed :max, default: 100.0, convert: proc(&:to_f)
|
21
|
+
|
22
|
+
attr_reader :slider_width, :slider_start, :steps_x, :steps_val
|
23
|
+
attr_accessor :sliding, :slider_x, :last_index
|
24
|
+
|
25
|
+
def initialize(**args)
|
26
|
+
@sliding = false
|
27
|
+
@slider_width = 0.0
|
28
|
+
@slider_start = 0.0
|
29
|
+
@slider_x = 0.0
|
30
|
+
@last_index = 0
|
31
|
+
@configured = false
|
32
|
+
|
33
|
+
super
|
34
|
+
|
35
|
+
@last_max = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def start_slider(event)
|
39
|
+
self.sliding = true
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_resize(canvas)
|
43
|
+
# create our buckets for steps
|
44
|
+
@slider_start = canvas.x
|
45
|
+
@slider_width = canvas.width
|
46
|
+
|
47
|
+
@steps_val = [*(min..max).step(step).to_a, max]
|
48
|
+
steps_val.pop if steps_val[-1] == steps_val[-2]
|
49
|
+
|
50
|
+
step_x = (slider_width) / (steps_val.size - 1)
|
51
|
+
@steps_x = (slider_start..(slider_start + slider_width)).step(step_x).to_a
|
52
|
+
|
53
|
+
(steps_val.size - steps_x.size).times do |i|
|
54
|
+
steps_x << steps_x.last + step_x * i
|
55
|
+
end
|
56
|
+
|
57
|
+
steps_x[-1] = slider_start + slider_width if slider_start + slider_width != steps_x.last
|
58
|
+
end
|
59
|
+
|
60
|
+
def move_slider(event)
|
61
|
+
if sliding && event.left.down
|
62
|
+
pos = event.pos.x
|
63
|
+
index = steps_x.size - 1
|
64
|
+
|
65
|
+
(0...steps_x.size - 1).each do |i|
|
66
|
+
next if steps_x[i + 1] && pos - steps_x[i + 1] > step
|
67
|
+
|
68
|
+
if pos - steps_x[i] > pos - steps_x[i + 1]
|
69
|
+
index = i
|
70
|
+
break
|
71
|
+
else
|
72
|
+
index = i + 1
|
73
|
+
break
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
self.last_index = index
|
78
|
+
|
79
|
+
emit("updated", steps_val[index])
|
80
|
+
else
|
81
|
+
self.sliding = false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def stop_slider(event)
|
86
|
+
if event.left.up
|
87
|
+
self.sliding = false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def render(canvas)
|
92
|
+
if max != @last_max
|
93
|
+
on_resize(canvas)
|
94
|
+
|
95
|
+
@last_max = max
|
96
|
+
end
|
97
|
+
|
98
|
+
unless @setup || steps_val.nil? || initial.nil?
|
99
|
+
steps_val.each_with_index do |val, index|
|
100
|
+
if val == initial
|
101
|
+
self.last_index = index
|
102
|
+
|
103
|
+
break
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
@setup = true
|
108
|
+
end
|
109
|
+
|
110
|
+
slider_x = steps_x[last_index]
|
111
|
+
padding = Hokusai::Padding.new(0.0, 0.0, 0.0, 0.0)
|
112
|
+
percent = slider_x * 100.00
|
113
|
+
x = slider_x + padding.left
|
114
|
+
x = (canvas.x + canvas.width) - (size / 2) - 2 if x > (canvas.x + canvas.width) - (size / 2)
|
115
|
+
x = canvas.x if x < canvas.x
|
116
|
+
cursor = (x + size / 2)
|
117
|
+
|
118
|
+
draw do
|
119
|
+
# draw background
|
120
|
+
rect(canvas.x + padding.left, canvas.y + padding.top, canvas.width - padding.right - padding.left, size) do |command|
|
121
|
+
command.round = size / 2
|
122
|
+
command.color = Hokusai::Color.new(33, 33, 33)
|
123
|
+
command.padding = Hokusai::Padding.new(5.0, 20.0, 5.0, 20.0)
|
124
|
+
end
|
125
|
+
|
126
|
+
rect(canvas.x + padding.left + 1, canvas.y + padding.top + 1, x + (size / 2) - canvas.x - 2, size - 2) do |command|
|
127
|
+
command.round = size / 2
|
128
|
+
command.color = fill
|
129
|
+
end
|
130
|
+
|
131
|
+
circle(cursor + padding.left, canvas.y + (size / 2) + padding.top, size / 2) do |command|
|
132
|
+
command.color = Hokusai::Color.new(233, 233, 233)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
yield canvas
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -11,8 +11,8 @@ module Hokusai
|
|
11
11
|
@height = height.to_f
|
12
12
|
@outline = Outline.default
|
13
13
|
@rounding = 0.0
|
14
|
-
@color = Color.new(
|
15
|
-
@outline_color = Color.new(0, 0, 0,
|
14
|
+
@color = Color.new(255, 255, 255, 0)
|
15
|
+
@outline_color = Color.new(0, 0, 0, 0)
|
16
16
|
@padding = Padding.new(0.0, 0.0, 0.0, 0.0)
|
17
17
|
@gradient = nil
|
18
18
|
end
|
@@ -2,9 +2,9 @@ module Hokusai
|
|
2
2
|
class TouchEvent < Event
|
3
3
|
extend Forwardable
|
4
4
|
|
5
|
-
def_delegators :@touch, :tapped?, :swiped?, :longtapped?, :touching?,
|
6
|
-
:duration, :direction, :angle, :position, :last_position,
|
7
|
-
|
5
|
+
def_delegators :@touch, :tapped?, :swiped?, :longtapped?, :longtapping?, :touching?,
|
6
|
+
:duration, :direction, :distance, :angle, :position, :last_position,
|
7
|
+
:touch_len, :touch_count, :timer
|
8
8
|
|
9
9
|
attr_reader :input
|
10
10
|
|
@@ -32,14 +32,8 @@ module Hokusai
|
|
32
32
|
name "taphold"
|
33
33
|
|
34
34
|
def capture(block, canvas)
|
35
|
-
if longtapped? && hovered(canvas)
|
36
|
-
block
|
37
|
-
|
38
|
-
if matches(block)
|
39
|
-
captures << block
|
40
|
-
end
|
41
|
-
elsif touching?
|
42
|
-
block.node.meta.blur
|
35
|
+
if matches(block) && longtapped? && hovered(canvas)
|
36
|
+
captures << block
|
43
37
|
end
|
44
38
|
end
|
45
39
|
end
|
data/ui/src/hokusai/painter.rb
CHANGED
@@ -277,7 +277,7 @@ module Hokusai
|
|
277
277
|
end
|
278
278
|
events[:mousemove].capture(block, canvas)
|
279
279
|
|
280
|
-
if block_is_hovered || block.node.meta.focused
|
280
|
+
if block_is_hovered || block.node.meta.focused || input.keyboard_override
|
281
281
|
events[:keyup].capture(block, canvas)
|
282
282
|
events[:keypress].capture(block, canvas)
|
283
283
|
end
|
data/ui/src/hokusai/types.rb
CHANGED
@@ -96,6 +96,14 @@ module Hokusai
|
|
96
96
|
alias_method :r, :right
|
97
97
|
alias_method :b, :bottom
|
98
98
|
|
99
|
+
def width
|
100
|
+
right + left
|
101
|
+
end
|
102
|
+
|
103
|
+
def height
|
104
|
+
top + bottom
|
105
|
+
end
|
106
|
+
|
99
107
|
def self.convert(value)
|
100
108
|
case value
|
101
109
|
when String
|
@@ -241,90 +249,183 @@ module Hokusai
|
|
241
249
|
end
|
242
250
|
|
243
251
|
class Touch
|
244
|
-
|
252
|
+
attr_accessor :stack, :archive
|
253
|
+
def initialize
|
254
|
+
@stack = []
|
255
|
+
@archive = []
|
256
|
+
@tapped = false
|
257
|
+
@swiped = false
|
258
|
+
@pinched = false
|
259
|
+
# @file = File.open("touch.log", "w")
|
260
|
+
end
|
245
261
|
|
246
|
-
|
247
|
-
|
248
|
-
].each do |key|
|
249
|
-
define_method(key) do
|
250
|
-
raw[:touch][key]
|
251
|
-
end
|
262
|
+
def tapped?
|
263
|
+
@tapped
|
252
264
|
end
|
253
265
|
|
254
266
|
def swiped?
|
255
|
-
|
267
|
+
@swiped
|
256
268
|
end
|
257
269
|
|
270
|
+
def pinched?
|
271
|
+
@pinched
|
272
|
+
end
|
273
|
+
|
274
|
+
def longtapping?(stuff = "ok")
|
275
|
+
log("#{touching?} - #{elapsed(token)} - #{stuff}") if touching?
|
276
|
+
touching? && elapsed(token) > 5
|
277
|
+
end
|
278
|
+
|
258
279
|
def longtapped?
|
259
|
-
|
280
|
+
@longtapped
|
281
|
+
end
|
282
|
+
|
283
|
+
def touching?
|
284
|
+
type == :down || type == :move
|
260
285
|
end
|
261
286
|
|
262
|
-
# return [Intger] duration in milliseconds
|
263
287
|
def duration
|
264
|
-
|
288
|
+
if longtapping?
|
289
|
+
return elapsed(token)
|
290
|
+
end
|
291
|
+
|
292
|
+
first, last = archive[-2..-1]
|
293
|
+
|
294
|
+
last[:start] - first[:start]
|
265
295
|
end
|
266
296
|
|
267
|
-
def
|
268
|
-
|
297
|
+
def distance
|
298
|
+
raise Hokusai::Error.new("Archive is empty") if archive.empty?
|
299
|
+
first, last = archive[-2..-1]
|
300
|
+
|
301
|
+
x = last[:x] - first[:x]
|
302
|
+
y = last[:y] - first[:y]
|
303
|
+
|
304
|
+
[x, y]
|
269
305
|
end
|
270
306
|
|
271
|
-
def
|
272
|
-
|
307
|
+
def direction
|
308
|
+
raise Hokusai::Error.new("Archive is empty") if archive.empty?
|
309
|
+
|
310
|
+
first, last = archive[-2..-1]
|
311
|
+
|
312
|
+
x = last[:x] - first[:x]
|
313
|
+
y = last[:y] - first[:y]
|
314
|
+
|
315
|
+
if x.abs > y.abs
|
316
|
+
# swiping left/right
|
317
|
+
last[:x] > first[:x] ? :right : :left
|
318
|
+
else
|
319
|
+
# swiping up/down
|
320
|
+
last[:y] > first[:y] ? :down : :up
|
321
|
+
end
|
273
322
|
end
|
274
323
|
|
275
|
-
def
|
276
|
-
|
324
|
+
def angle
|
325
|
+
raise Hokusai::Error.new("Archive is empty") if archive.empty?
|
326
|
+
|
327
|
+
last, first = archive[-2..-1]
|
328
|
+
|
329
|
+
x = last[:x] - first[:x]
|
330
|
+
y = last[:y] - first[:y]
|
331
|
+
|
332
|
+
(Math.atan2(x, y) * (-180 / Math::PI)).round(0).to_i
|
277
333
|
end
|
278
334
|
|
279
|
-
def
|
280
|
-
|
335
|
+
def log(str)
|
336
|
+
# Thread.new do
|
337
|
+
# @file.write_nonblock("#{str}\n")
|
338
|
+
# end
|
281
339
|
end
|
282
340
|
|
283
|
-
def
|
284
|
-
|
341
|
+
def record(finger, x, y)
|
342
|
+
log("recording #{token}")
|
343
|
+
if type == :down
|
344
|
+
push(:move, finger, x, y)
|
345
|
+
log("state is move")
|
346
|
+
elsif type == :move
|
347
|
+
stack.last[:x] = x
|
348
|
+
stack.last[:y] = y
|
349
|
+
|
350
|
+
log("updated state move")
|
351
|
+
else
|
352
|
+
@longtapped = false
|
353
|
+
@swiped = false
|
354
|
+
@tapped = false
|
355
|
+
push(:down, finger, x, y)
|
356
|
+
log("state is down")
|
357
|
+
end
|
358
|
+
end
|
285
359
|
|
286
|
-
|
287
|
-
|
360
|
+
def clear
|
361
|
+
log("clearing")
|
362
|
+
if type == :move
|
363
|
+
log("elapsed: #{elapsed(token)}")
|
364
|
+
if elapsed(token) > 0.05 && within(10.0)
|
365
|
+
@longtapped = true
|
366
|
+
log('longtap')
|
367
|
+
else
|
368
|
+
@swiped = true
|
369
|
+
log('swipe')
|
370
|
+
end
|
371
|
+
elsif type == :down
|
372
|
+
@tapped = true
|
373
|
+
log('tap')
|
374
|
+
else
|
375
|
+
@longtapped = false
|
376
|
+
@swiped = false
|
377
|
+
@tapped = false
|
378
|
+
end
|
288
379
|
|
289
|
-
|
290
|
-
|
380
|
+
self.archive = stack.dup
|
381
|
+
stack.clear
|
382
|
+
end
|
291
383
|
|
292
|
-
|
384
|
+
def elapsed(token)
|
385
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - token[:start]
|
293
386
|
end
|
294
387
|
|
295
|
-
def
|
296
|
-
|
388
|
+
def within(threshold)
|
389
|
+
move = stack.last
|
390
|
+
down = stack[-2]
|
297
391
|
|
298
|
-
|
299
|
-
|
392
|
+
t1 = (move[:x] - down[:x]).abs
|
393
|
+
t2 = (move[:y] - down[:y]).abs
|
300
394
|
|
301
|
-
|
302
|
-
|
395
|
+
t1 < threshold && t2 < threshold
|
396
|
+
end
|
303
397
|
|
304
|
-
|
305
|
-
|
306
|
-
position.x > last_position.x ? :right : :left
|
307
|
-
else
|
308
|
-
# swiping up/down
|
309
|
-
position.y > last_position.y ? :up : :down
|
310
|
-
end
|
398
|
+
def pop
|
399
|
+
stack.pop
|
311
400
|
end
|
312
401
|
|
313
|
-
def
|
314
|
-
|
402
|
+
def push(type, finger, x, y)
|
403
|
+
log("push: #{type}")
|
404
|
+
stack << {
|
405
|
+
type: type,
|
406
|
+
i: finger,
|
407
|
+
x: x,
|
408
|
+
y: y,
|
409
|
+
start: Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
410
|
+
}
|
315
411
|
end
|
316
412
|
|
317
|
-
def
|
318
|
-
|
413
|
+
def index
|
414
|
+
token&.[](:finger)
|
319
415
|
end
|
320
416
|
|
321
|
-
def
|
322
|
-
|
417
|
+
def type
|
418
|
+
token&.[](:type)
|
419
|
+
end
|
420
|
+
|
421
|
+
def token
|
422
|
+
@stack.last
|
323
423
|
end
|
324
424
|
end
|
325
425
|
|
326
426
|
class Input
|
327
|
-
|
427
|
+
attr_accessor :keyboard_override
|
428
|
+
attr_reader :raw, :touch
|
328
429
|
|
329
430
|
def hash
|
330
431
|
[self.class, mouse.pos.x, mouse.pos.y, mouse.scroll, mouse.left.clicked, mouse.left.down, mouse.left.up].hash
|
@@ -332,12 +433,14 @@ module Hokusai
|
|
332
433
|
|
333
434
|
def initialize(raw)
|
334
435
|
@raw = raw
|
436
|
+
@touch = nil
|
437
|
+
@keyboard_override = false
|
335
438
|
end
|
336
439
|
|
337
|
-
def
|
338
|
-
|
440
|
+
def support_touch!
|
441
|
+
@touch ||= Touch.new
|
339
442
|
|
340
|
-
|
443
|
+
self
|
341
444
|
end
|
342
445
|
|
343
446
|
def keyboard
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require "rouge"
|
2
|
+
require "hokusai"
|
3
|
+
require_relative "./lib/hokusai_code/formatter"
|
4
|
+
|
5
|
+
module Hokusai::Util
|
6
|
+
class Wrapped
|
7
|
+
attr_accessor :text, :x, :y, :extra
|
8
|
+
|
9
|
+
def initialize(text, x, y, extra)
|
10
|
+
@text = text
|
11
|
+
@x = x
|
12
|
+
@y = y
|
13
|
+
@extra = extra
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class SelectionWrapper
|
18
|
+
attr_accessor :x, :y, :width, :height
|
19
|
+
|
20
|
+
def initialize(x, y, w, h)
|
21
|
+
@x = x
|
22
|
+
@y = y
|
23
|
+
@width = w
|
24
|
+
@height = h
|
25
|
+
end
|
26
|
+
|
27
|
+
def <<(w)
|
28
|
+
@width += w
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class WrapStream
|
33
|
+
attr_accessor :width, :current_width, :x, :y,
|
34
|
+
:origin_x, :origin_y, :buffer, :stack
|
35
|
+
attr_reader :on_text_cb, :on_text_selection_cb, :on_advancex_cb, :selector, :padding
|
36
|
+
|
37
|
+
def initialize(width, &measure)
|
38
|
+
@width = width
|
39
|
+
@current_width = 0.0
|
40
|
+
|
41
|
+
@origin_x = 0.0
|
42
|
+
@origin_y = 0.0
|
43
|
+
@x = @origin_x
|
44
|
+
@y = @origin_y
|
45
|
+
|
46
|
+
@stack = []
|
47
|
+
@buffer = ""
|
48
|
+
|
49
|
+
@measure_cb = measure
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_advancex(&block)
|
53
|
+
@on_advancex_cb = block
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_text_selection(selector, padding, &block)
|
57
|
+
@selector = selector
|
58
|
+
@padding = padding || Hokusai::Padding.new(0.0, 0.0, 0.0, 0.0)
|
59
|
+
@on_text_selection_cb = block
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_text(&block)
|
63
|
+
@on_text_cb = block
|
64
|
+
end
|
65
|
+
|
66
|
+
def measure(string, extra)
|
67
|
+
@measure_cb.call(string, extra)
|
68
|
+
end
|
69
|
+
|
70
|
+
def flush
|
71
|
+
sx = x + padding.left
|
72
|
+
wrapper = nil
|
73
|
+
stack.each do |(range, extra)|
|
74
|
+
str = buffer[range]
|
75
|
+
if selector
|
76
|
+
str.split("").each do |char|
|
77
|
+
nw, nh = measure(char, extra)
|
78
|
+
ox = on_advancex_cb.call(char.codepoints.first)
|
79
|
+
|
80
|
+
if selector.selected(sx, y + padding.top - selector.offset_y, nw, nh)
|
81
|
+
wrapper ||= SelectionWrapper.new(sx, y + padding.top, 0.0, nh)
|
82
|
+
wrapper << ox
|
83
|
+
end
|
84
|
+
|
85
|
+
sx += ox
|
86
|
+
end
|
87
|
+
|
88
|
+
on_text_selection_cb.call(wrapper) if wrapper
|
89
|
+
wrapper = nil
|
90
|
+
end
|
91
|
+
|
92
|
+
nw, _ = measure(str, extra)
|
93
|
+
|
94
|
+
# hard break on the buffer, split on character
|
95
|
+
wrap_and_call(str, extra)
|
96
|
+
|
97
|
+
self.x += nw
|
98
|
+
end
|
99
|
+
|
100
|
+
self.current_width = 0.0
|
101
|
+
self.buffer = ""
|
102
|
+
stack.clear
|
103
|
+
self.x = origin_x
|
104
|
+
end
|
105
|
+
|
106
|
+
def wrap(text, extra)
|
107
|
+
offset = 0
|
108
|
+
size = text.size
|
109
|
+
|
110
|
+
stack << [((buffer.size)..(text.size + buffer.size - 1)), extra]
|
111
|
+
|
112
|
+
while offset < size
|
113
|
+
char = text[offset]
|
114
|
+
w, h = measure(char, extra)
|
115
|
+
|
116
|
+
# if it's a newline we want to break
|
117
|
+
if char =~ /\n|\r\n/
|
118
|
+
flush
|
119
|
+
|
120
|
+
stack << [(0...(text.size - offset - 1)), extra]
|
121
|
+
self.y += h
|
122
|
+
self.x = origin_x
|
123
|
+
offset += 1
|
124
|
+
|
125
|
+
next
|
126
|
+
end
|
127
|
+
|
128
|
+
# if adding this char extends beyond the boundary
|
129
|
+
if current_width + w > width
|
130
|
+
# find the last space
|
131
|
+
idx = buffer.rindex(" ")
|
132
|
+
|
133
|
+
# if there is a break in this line split the buffer
|
134
|
+
# and render the current line
|
135
|
+
unless idx.nil? || idx < (buffer.size / 2)
|
136
|
+
cur = []
|
137
|
+
nex = []
|
138
|
+
found = false
|
139
|
+
|
140
|
+
# We need to split up both the buffer, and the ranges
|
141
|
+
while payload = stack.shift
|
142
|
+
range, xtra = payload
|
143
|
+
if range.include?(idx)
|
144
|
+
# putting the space on this line
|
145
|
+
cur << [(range.begin..idx), xtra]
|
146
|
+
# pp [range, idx]
|
147
|
+
nex << [(0..(range.end - idx - 1)), xtra] unless idx == range.end
|
148
|
+
|
149
|
+
found = true
|
150
|
+
elsif !found
|
151
|
+
cur << payload
|
152
|
+
else
|
153
|
+
nex << [((range.begin - idx - 1)..(range.end - idx - 1)), xtra]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
scur = buffer[0..idx]
|
158
|
+
snex = buffer[(idx + 1)..-1]
|
159
|
+
|
160
|
+
cur.each do |(range, xtra)|
|
161
|
+
str = scur[range]
|
162
|
+
|
163
|
+
nw, _ = measure(str, xtra)
|
164
|
+
wrap_and_call(str, xtra)
|
165
|
+
|
166
|
+
self.x += nw
|
167
|
+
end
|
168
|
+
|
169
|
+
self.buffer = snex + char
|
170
|
+
self.stack = nex
|
171
|
+
self.y += h
|
172
|
+
self.current_width = measure(buffer, extra).first
|
173
|
+
self.x = origin_x
|
174
|
+
else
|
175
|
+
# break on this word
|
176
|
+
flush
|
177
|
+
|
178
|
+
self.y += h
|
179
|
+
self.current_width = measure(char, extra).first
|
180
|
+
self.buffer = char
|
181
|
+
stack << [(0...(text.size - offset)), extra]
|
182
|
+
end
|
183
|
+
else
|
184
|
+
self.current_width += w
|
185
|
+
buffer << char
|
186
|
+
end
|
187
|
+
|
188
|
+
offset += 1
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
private def wrap_and_call(text, extra)
|
194
|
+
on_text_cb.call Wrapped.new(text, x, y, extra)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|