hokusai-zero 0.2.7 → 0.2.8
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/hml.c +2 -0
- data/hokusai.gemspec +1 -1
- data/ui/spec/hokusai/e2e/client_spec.rb +0 -1
- data/ui/src/hokusai/backends/raylib/font.rb +31 -1
- data/ui/src/hokusai/backends/raylib.rb +32 -0
- data/ui/src/hokusai/blocks/dynamic.rb +2 -0
- data/ui/src/hokusai/blocks/panel.rb +2 -0
- data/ui/src/hokusai/blocks/scrollbar.rb +7 -0
- data/ui/src/hokusai/blocks/selectable.rb +1 -0
- data/ui/src/hokusai/blocks/text_stream.rb +130 -0
- data/ui/src/hokusai/blocks/translation.rb +91 -0
- data/ui/src/hokusai/commands/rotation.rb +21 -0
- data/ui/src/hokusai/commands/scale.rb +20 -0
- data/ui/src/hokusai/commands/translation.rb +20 -0
- data/ui/src/hokusai/commands.rb +27 -3
- data/ui/src/hokusai/meta.rb +4 -2
- data/ui/src/hokusai/mounting/loop_entry.rb +3 -3
- data/ui/src/hokusai/mounting/update_entry.rb +0 -2
- data/ui/src/hokusai/types/display.rb +5 -1
- data/ui/src/hokusai/util/selection.rb +28 -7
- data/ui/src/hokusai/util/wrap_stream.rb +224 -149
- data/ui/src/hokusai.rb +12 -1
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3430bbda86d7deae31c2e789646f7347e378bb8e0349bb7f37545010417b5d1f
|
4
|
+
data.tar.gz: 72457b126d79909ef0989cb67fd278993377b7aa18b9e759b6822e2c05380e8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 407646a8c7bb287f18b1cc864d72150f4f93f12e29dbcb833befb9968ee5bbea48e0447fda35ca9b40513e4a371db499bc3cd4457e66f8f1d70a6b1d6c0692bb
|
7
|
+
data.tar.gz: ea2e824fb8df8fb0c0a28fbf2ffeb6768f6c2a66d900bf0f2146e04da68dad0a9d93e30eb36d654a60deec1fa4d0625321d9d21a47156a3162bd428f10049b8d
|
data/ast/src/core/hml.c
CHANGED
data/hokusai.gemspec
CHANGED
@@ -26,7 +26,7 @@ module Hokusai
|
|
26
26
|
class Font < Hokusai::Font
|
27
27
|
attr_reader :raw, :sdf
|
28
28
|
|
29
|
-
DEFAULT = "–—‘’“”…\r\n\
|
29
|
+
DEFAULT = "–—‘’“”…\r\n\t\s0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%%^&*(),.?/\"\\[]-_=+|~`{}<>;:'\0"
|
30
30
|
|
31
31
|
def self.default
|
32
32
|
font = Raylib.GetFontDefault
|
@@ -83,6 +83,26 @@ module Hokusai
|
|
83
83
|
@raw = raw
|
84
84
|
@spacing = spacing
|
85
85
|
@sdf = sdf
|
86
|
+
@measure_map = {}
|
87
|
+
|
88
|
+
DEFAULT.split("").each do |char|
|
89
|
+
@measure_map[char] ||= begin
|
90
|
+
bcount = FFI::MemoryPointer.new(:int)
|
91
|
+
letter = Raylib.GetCodepoint(char, bcount)
|
92
|
+
bcount.free
|
93
|
+
info = Raylib.GetGlyphInfo(raw, letter)
|
94
|
+
if info.advanceX > 0
|
95
|
+
w = info.advanceX
|
96
|
+
else
|
97
|
+
w = info.image.width + info.offsetX
|
98
|
+
end
|
99
|
+
|
100
|
+
->(size) do
|
101
|
+
width = (size.to_f / raw.baseSize.to_f) * w + (spacing)
|
102
|
+
width
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
86
106
|
end
|
87
107
|
|
88
108
|
# returns the spacing for this font
|
@@ -94,6 +114,16 @@ module Hokusai
|
|
94
114
|
(ssize / (raw.baseSize.zero? ? 1 : raw.baseSize)).to_f
|
95
115
|
end
|
96
116
|
|
117
|
+
def measure_char(char, size)
|
118
|
+
w = @measure_map[char]&.call(size)
|
119
|
+
|
120
|
+
if w.nil?
|
121
|
+
measure(char, size).first
|
122
|
+
else
|
123
|
+
w
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
97
127
|
def measure(string, size)
|
98
128
|
vec = Raylib.MeasureTextEx(raw, string, size, spacing)
|
99
129
|
|
@@ -380,6 +380,10 @@ module Hokusai::Backends
|
|
380
380
|
end
|
381
381
|
end
|
382
382
|
|
383
|
+
Hokusai.on_copy do |text|
|
384
|
+
Raylib.SetClipboardText(text)
|
385
|
+
end
|
386
|
+
|
383
387
|
Hokusai.on_set_mouse_position do |mouse|
|
384
388
|
Raylib.SetMousePosition(mouse.pos.x, mouse.pos.y)
|
385
389
|
end
|
@@ -410,6 +414,34 @@ module Hokusai::Backends
|
|
410
414
|
inside_scissor(canvas.x, canvas.y, canvas.height)
|
411
415
|
end
|
412
416
|
|
417
|
+
Hokusai::Commands::TranslationBegin.on_draw do |command|
|
418
|
+
Raylib.rlPushMatrix
|
419
|
+
Raylib.rlTranslatef(command.x, command.y, 0)
|
420
|
+
end
|
421
|
+
|
422
|
+
Hokusai::Commands::TranslationEnd.on_draw do |command|
|
423
|
+
Raylib.rlPopMatrix
|
424
|
+
end
|
425
|
+
|
426
|
+
Hokusai::Commands::RotationBegin.on_draw do |command|
|
427
|
+
Raylib.rlPushMatrix
|
428
|
+
Raylib.rlTranslatef(command.x, command.y, 0)
|
429
|
+
Raylib.rlRotatef(command.degrees, 0, 0, 1);
|
430
|
+
end
|
431
|
+
|
432
|
+
Hokusai::Commands::RotationEnd.on_draw do
|
433
|
+
Raylib.rlPopMatrix
|
434
|
+
end
|
435
|
+
|
436
|
+
Hokusai::Commands::ScaleBegin.on_draw do |command|
|
437
|
+
Raylib.rlPushMatrix
|
438
|
+
Raylib.rlScalef(command.x, command.y, 0)
|
439
|
+
end
|
440
|
+
|
441
|
+
Hokusai::Commands::ScaleEnd.on_draw do
|
442
|
+
Raylib.rlPopMatrix
|
443
|
+
end
|
444
|
+
|
413
445
|
Hokusai::Commands::ShaderBegin.on_draw do |command|
|
414
446
|
self.class.shaders[command.hash] ||= Raylib.LoadShaderFromMemory(command.vertex_shader, command.fragment_shader)
|
415
447
|
|
@@ -39,6 +39,8 @@ class Hokusai::Blocks::Panel < Hokusai::Block
|
|
39
39
|
provide :panel_height, :panel_height
|
40
40
|
provide :panel_top, :panel_top
|
41
41
|
|
42
|
+
inject :selection
|
43
|
+
|
42
44
|
attr_accessor :top, :panel_height, :scroll_y, :scroll_percent,
|
43
45
|
:scroll_goto_y, :clipped_offset, :clipped_content_height
|
44
46
|
|
@@ -1,7 +1,14 @@
|
|
1
1
|
class Hokusai::Blocks::Scrollbar < Hokusai::Block
|
2
|
+
style <<~EOF
|
3
|
+
[style]
|
4
|
+
scrollbar {
|
5
|
+
cursor: "pointer";
|
6
|
+
}
|
7
|
+
EOF
|
2
8
|
template <<~EOF
|
3
9
|
[template]
|
4
10
|
vblock.scrollbar {
|
11
|
+
...scrollbar
|
5
12
|
@mousedown="scroll_start"
|
6
13
|
@mousemove="scroll_handle"
|
7
14
|
:background="background"
|
@@ -0,0 +1,130 @@
|
|
1
|
+
class Hokusai::Blocks::TextStream < Hokusai::Block
|
2
|
+
template <<~EOF
|
3
|
+
[template]
|
4
|
+
empty {
|
5
|
+
cursor="ibeam"
|
6
|
+
}
|
7
|
+
EOF
|
8
|
+
|
9
|
+
uses(empty: Hokusai::Blocks::Empty)
|
10
|
+
|
11
|
+
computed! :content
|
12
|
+
computed :font, default: nil
|
13
|
+
computed :size, default: 16, convert: proc(&:to_i)
|
14
|
+
computed :color, default: Hokusai::Color.new(33, 33, 33), convert: Hokusai::Color
|
15
|
+
computed :selection_color, default: Hokusai::Color.new(233,233,233), convert: Hokusai::Color
|
16
|
+
computed :padding, default: Hokusai::Padding.new(5.0, 5.0, 5.0, 5.0), convert: Hokusai::Padding
|
17
|
+
computed :cursor_offset, default: nil
|
18
|
+
|
19
|
+
inject :selection
|
20
|
+
inject :panel_top
|
21
|
+
inject :panel_height
|
22
|
+
inject :panel_offset
|
23
|
+
|
24
|
+
attr_accessor :last_commands, :last_content, :last_coords, :last_height,
|
25
|
+
:last_width, :last_selector, :last_panel_offset, :copying, :buffer
|
26
|
+
|
27
|
+
attr_reader :stream
|
28
|
+
|
29
|
+
def initialize(**args)
|
30
|
+
@last_commands = []
|
31
|
+
@last_coords = nil
|
32
|
+
@last_height = 0.0
|
33
|
+
@last_width = nil
|
34
|
+
@last_selector = nil
|
35
|
+
@last_content = nil
|
36
|
+
@last_panel_offset = nil
|
37
|
+
@last_canvas = nil
|
38
|
+
@copying = false
|
39
|
+
@reset = false
|
40
|
+
@buffer = ""
|
41
|
+
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def after_updated
|
46
|
+
@stream&.reset(last_width)
|
47
|
+
end
|
48
|
+
|
49
|
+
def render(canvas)
|
50
|
+
w = canvas.width - padding.left - padding.right
|
51
|
+
poff = panel_offset || 0.0 - panel_top || 0.0
|
52
|
+
selection&.offset_y = poff
|
53
|
+
|
54
|
+
lfont = Hokusai.fonts.active_font_name
|
55
|
+
Hokusai.fonts.activate font unless font.nil?
|
56
|
+
|
57
|
+
# if Hokusai.can_render(canvas)
|
58
|
+
# if (last_content != content ||
|
59
|
+
# last_width != w ||
|
60
|
+
# panel_offset != last_panel_offset ||
|
61
|
+
# last_coords != selection&.coords ||
|
62
|
+
# copying ||
|
63
|
+
# last_selector != selection&.type)
|
64
|
+
|
65
|
+
last_commands.clear
|
66
|
+
self.last_panel_offset = panel_offset
|
67
|
+
self.last_content = content
|
68
|
+
self.last_width = w
|
69
|
+
self.last_selector = selection&.type
|
70
|
+
self.last_coords = selection&.coords
|
71
|
+
|
72
|
+
@stream ||= WrapStream.new(w) do |string, extra|
|
73
|
+
[Hokusai.fonts.active.measure_char(string, size), size]
|
74
|
+
end
|
75
|
+
|
76
|
+
stream.origin_x = canvas.x + padding.left
|
77
|
+
stream.origin_y = canvas.y + padding.top
|
78
|
+
stream.offset_y = 0.0
|
79
|
+
stream.reset(w)
|
80
|
+
|
81
|
+
draw do
|
82
|
+
stream.on_text_selection(selection, nil) do |wrapped|
|
83
|
+
self.buffer << wrapped.buffer if copying
|
84
|
+
rect(wrapped.x, wrapped.y, wrapped.width, wrapped.height) do |command|
|
85
|
+
command.color = selection_color
|
86
|
+
|
87
|
+
last_commands << command
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
stream.on_text do |wrapped|
|
92
|
+
text(wrapped.text, wrapped.x, wrapped.y) do |command|
|
93
|
+
command.color = color
|
94
|
+
command.size = size
|
95
|
+
command.font = font
|
96
|
+
|
97
|
+
last_commands << command
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
stream.wrap(content, nil)
|
102
|
+
stream.flush
|
103
|
+
stream.y += size
|
104
|
+
end
|
105
|
+
|
106
|
+
self.last_height = stream.y - canvas.y + padding.top + padding.bottom
|
107
|
+
emit("height_updated", last_height)
|
108
|
+
node.meta.set_prop(:height, last_height)
|
109
|
+
|
110
|
+
|
111
|
+
# else
|
112
|
+
# draw do
|
113
|
+
# queue.concat last_commands
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
|
117
|
+
if copying
|
118
|
+
Hokusai.copy(buffer)
|
119
|
+
self.buffer = ""
|
120
|
+
self.copying = false
|
121
|
+
end
|
122
|
+
|
123
|
+
# node.portal&.meta&.set_prop(:height, last_height + padding.top + padding.bottom)
|
124
|
+
|
125
|
+
yield canvas
|
126
|
+
# end
|
127
|
+
|
128
|
+
Hokusai.fonts.activate lfont
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Hokusai::Blocks::TranslationBlock < Hokusai::Block
|
2
|
+
template <<~EOF
|
3
|
+
[template]
|
4
|
+
dynamic { @size_updated="set_size" }
|
5
|
+
slot
|
6
|
+
EOF
|
7
|
+
|
8
|
+
uses(dynamic: Hokusai::Blocks::Dynamic)
|
9
|
+
|
10
|
+
attr_accessor :content_width, :content_height
|
11
|
+
|
12
|
+
def set_size(width, height)
|
13
|
+
self.content_width = width
|
14
|
+
self.content_height = height
|
15
|
+
node.meta.set_prop(:width, width)
|
16
|
+
node.meta.set_prop(:height, height)
|
17
|
+
end
|
18
|
+
|
19
|
+
computed :duration, default: 500.0, convert: proc(&:to_f)
|
20
|
+
computed :from, default: :top, convert: proc(&:to_sym)
|
21
|
+
|
22
|
+
def on_mounted
|
23
|
+
@start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
24
|
+
end
|
25
|
+
|
26
|
+
def circular_in(t)
|
27
|
+
return 1.0 - Math.sqrt(1.0 - t * t);
|
28
|
+
end
|
29
|
+
|
30
|
+
def bounce_out(x)
|
31
|
+
n1 = 7.5625;
|
32
|
+
d1 = 2.75;
|
33
|
+
if (x < 1 / d1)
|
34
|
+
return n1 * x * x;
|
35
|
+
elsif (x < 2 / d1)
|
36
|
+
return n1 * (x -= 1.5 / d1) * x + 0.75;
|
37
|
+
elsif (x < 2.5 / d1)
|
38
|
+
return n1 * (x -= 2.25 / d1) * x + 0.9375;
|
39
|
+
else
|
40
|
+
return n1 * (x -= 2.625 / d1) * x + 0.984375;
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def bounce_in(t)
|
45
|
+
return 1.0 - bounce_out(1.0 - t);
|
46
|
+
end
|
47
|
+
|
48
|
+
def ease(x)
|
49
|
+
return 1 - Math.cos((x * Math::PI) / 2);
|
50
|
+
end
|
51
|
+
|
52
|
+
def render(canvas)
|
53
|
+
@canvas ||= canvas
|
54
|
+
time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - @start
|
55
|
+
|
56
|
+
if time > duration
|
57
|
+
yield canvas
|
58
|
+
|
59
|
+
return
|
60
|
+
else
|
61
|
+
case from
|
62
|
+
when :top
|
63
|
+
@startx ||= canvas.x
|
64
|
+
@starty ||= canvas.y - canvas.height
|
65
|
+
when :left
|
66
|
+
@startx ||= canvas.x - canvas.width
|
67
|
+
@starty ||= canvas.y
|
68
|
+
when :right
|
69
|
+
@startx ||= canvas.x + canvas.width
|
70
|
+
@starty ||= canvas.y
|
71
|
+
when :bottom
|
72
|
+
@startx ||= canvas.x
|
73
|
+
@starty ||= canvas.y + canvas.height
|
74
|
+
end
|
75
|
+
|
76
|
+
@targetx ||= canvas.x
|
77
|
+
@targety ||= canvas.y
|
78
|
+
|
79
|
+
progress = bounce_in(time.to_f / duration)
|
80
|
+
|
81
|
+
if progress >= 1
|
82
|
+
progress = 1.0
|
83
|
+
end
|
84
|
+
|
85
|
+
canvas.x = (@startx + (-@startx * progress)) + (@targetx * progress)
|
86
|
+
canvas.y = (@starty + (-@starty * progress)) + (@targety * progress)
|
87
|
+
|
88
|
+
yield canvas
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hokusai
|
2
|
+
class Commands::RotationBegin < Commands::Base
|
3
|
+
attr_reader :x, :y, :degrees
|
4
|
+
|
5
|
+
def initialize(x, y, deg)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
@degrees = deg
|
9
|
+
end
|
10
|
+
|
11
|
+
def hash
|
12
|
+
[self.class, x, y, degrees].hash
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Commands::RotationEnd < Commands::Base;
|
17
|
+
def hash
|
18
|
+
[self.class].hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Hokusai
|
2
|
+
class Commands::ScaleBegin < Commands::Base
|
3
|
+
attr_reader :x, :y
|
4
|
+
|
5
|
+
def initialize(x, y = x)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash
|
11
|
+
[self.class, x, y].hash
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Commands::ScaleEnd < Commands::Base;
|
16
|
+
def hash
|
17
|
+
[self.class].hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Hokusai
|
2
|
+
class Commands::TranslationBegin < Commands::Base
|
3
|
+
attr_reader :x, :y
|
4
|
+
|
5
|
+
def initialize(x, y = x)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash
|
11
|
+
[self.class, x, y].hash
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Commands::TranslationEnd < Commands::Base;
|
16
|
+
def hash
|
17
|
+
[self.class].hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/ui/src/hokusai/commands.rb
CHANGED
@@ -6,6 +6,9 @@ require_relative "./commands/scissor"
|
|
6
6
|
require_relative "./commands/text"
|
7
7
|
require_relative "./commands/shader"
|
8
8
|
require_relative "./commands/texture"
|
9
|
+
require_relative "./commands/rotation"
|
10
|
+
require_relative "./commands/scale"
|
11
|
+
require_relative "./commands/translation"
|
9
12
|
|
10
13
|
module Hokusai
|
11
14
|
# A proxy class for invoking various UI commands
|
@@ -33,7 +36,6 @@ module Hokusai
|
|
33
36
|
yield(command)
|
34
37
|
|
35
38
|
queue << command
|
36
|
-
# command.draw
|
37
39
|
end
|
38
40
|
|
39
41
|
# Draw a circle
|
@@ -46,9 +48,7 @@ module Hokusai
|
|
46
48
|
|
47
49
|
yield(command)
|
48
50
|
|
49
|
-
|
50
51
|
queue << command
|
51
|
-
# command.draw
|
52
52
|
end
|
53
53
|
|
54
54
|
# Draws an SVG
|
@@ -95,6 +95,30 @@ module Hokusai
|
|
95
95
|
queue << Commands::ShaderEnd.new
|
96
96
|
end
|
97
97
|
|
98
|
+
def rotation_begin(x, y, deg)
|
99
|
+
queue << Commands::RotationBegin.new(x, y, deg)
|
100
|
+
end
|
101
|
+
|
102
|
+
def rotation_end
|
103
|
+
queue << Commands::RotationEnd.new
|
104
|
+
end
|
105
|
+
|
106
|
+
def scale_begin(*args)
|
107
|
+
queue << Commands::ScaleBegin.new(*args)
|
108
|
+
end
|
109
|
+
|
110
|
+
def scale_end
|
111
|
+
queue << Commands::ScaleEnd.new
|
112
|
+
end
|
113
|
+
|
114
|
+
def translation_Begin(x, y)
|
115
|
+
queue << Commands::TranslationBegin.new
|
116
|
+
end
|
117
|
+
|
118
|
+
def translation_end
|
119
|
+
queue << Commands::TranslationEnd.new
|
120
|
+
end
|
121
|
+
|
98
122
|
def texture(x, y, w, h)
|
99
123
|
command = Commands::Texture.new(x, y, w, h)
|
100
124
|
|
data/ui/src/hokusai/meta.rb
CHANGED
@@ -107,16 +107,18 @@ module Hokusai
|
|
107
107
|
def update(block)
|
108
108
|
if target_block = target
|
109
109
|
if updater_block = updater
|
110
|
-
block.
|
110
|
+
block.before_updated if block.respond_to?(:before_updated)
|
111
111
|
|
112
112
|
updater_block.call(block, target_block, target_block)
|
113
|
-
block.
|
113
|
+
block.after_updated if block.respond_to?(:after_updated)
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
117
|
children?&.each do |child|
|
118
118
|
child.update
|
119
119
|
end
|
120
|
+
|
121
|
+
block.after_children_updated if block.respond_to?(:after_children_updated)
|
120
122
|
end
|
121
123
|
def has_ast?(ast, index, elsy = false)
|
122
124
|
if elsy
|
@@ -154,7 +154,7 @@ module Hokusai
|
|
154
154
|
if patch.delete
|
155
155
|
from = children[patch.from]
|
156
156
|
children[patch.to] = from
|
157
|
-
children[patch.from].public_send(:
|
157
|
+
children[patch.from].public_send(:before_destroy) if children[patch.from].respond_to? :before_destroy
|
158
158
|
children[patch.from].node.destroy
|
159
159
|
children[patch.from] = nil
|
160
160
|
else
|
@@ -189,7 +189,7 @@ module Hokusai
|
|
189
189
|
if !condition && ast.has_else_condition?
|
190
190
|
target_ast = ast.else_ast
|
191
191
|
elsif !condition
|
192
|
-
children[patch.target].public_send(:
|
192
|
+
children[patch.target].public_send(:before_destroy) if children[patch.target].respond_to? :before_destroy
|
193
193
|
children[patch.target].node.destroy
|
194
194
|
children[patch.target] = nil
|
195
195
|
next
|
@@ -210,7 +210,7 @@ module Hokusai
|
|
210
210
|
children.insert(patch.target, child_block)
|
211
211
|
end
|
212
212
|
when DeletePatch
|
213
|
-
children[patch.target].public_send(:
|
213
|
+
children[patch.target].public_send(:before_destroy) if children[patch.target].respond_to? :before_destroy
|
214
214
|
children[patch.target].node.destroy
|
215
215
|
children[patch.target] = nil
|
216
216
|
# TODO: update rest of block props
|
@@ -52,9 +52,7 @@ module Hokusai
|
|
52
52
|
child_block = NodeMounter.new(node, child_block_klass, [stack], previous_providers: providers).mount(context: context, providers: providers)
|
53
53
|
|
54
54
|
UpdateEntry.new(child_block, block, target).register(context: context, providers: providers)
|
55
|
-
pp [meta.has_ast?(child, index, false), "shouldn't be here?"]
|
56
55
|
meta.children!.insert(index, child_block)
|
57
|
-
pp ["children", meta.children!.size, meta.children!.map(&:class)]
|
58
56
|
|
59
57
|
child_block.public_send(:before_updated) if child_block.respond_to?(:before_updated)
|
60
58
|
child_block.update
|
@@ -117,7 +117,7 @@ module Hokusai
|
|
117
117
|
|
118
118
|
# Color = Struct.new(:red, :green, :blue, :alpha) do
|
119
119
|
class Color
|
120
|
-
|
120
|
+
attr_accessor :red, :green, :blue, :alpha
|
121
121
|
def initialize(red, green, blue, alpha = 255)
|
122
122
|
@red = red.freeze
|
123
123
|
@green = green.freeze
|
@@ -144,6 +144,10 @@ module Hokusai
|
|
144
144
|
new(value[0], value[1], value[2], value[3] || 255)
|
145
145
|
end
|
146
146
|
|
147
|
+
def to_shader_value
|
148
|
+
[(r / 255.0), (g / 255.0), (b / 255.0), (a / 255.0)]
|
149
|
+
end
|
150
|
+
|
147
151
|
def hash
|
148
152
|
[self.class, r, g, b, a].hash
|
149
153
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Hokusai::Util
|
2
2
|
class Selection
|
3
3
|
attr_reader :raw
|
4
|
-
attr_accessor :started
|
4
|
+
attr_accessor :started, :cleared
|
5
5
|
|
6
6
|
def initialize
|
7
7
|
ptr = FFI::MemoryPointer.new :pointer
|
@@ -9,6 +9,9 @@ module Hokusai::Util
|
|
9
9
|
@raw = LibHokusai::HokuSelection.new(ptr.get_pointer(0))
|
10
10
|
ptr.free
|
11
11
|
@started = false
|
12
|
+
@direction = nil
|
13
|
+
@changed_direction = false
|
14
|
+
@cleared = false
|
12
15
|
end
|
13
16
|
|
14
17
|
def type
|
@@ -30,9 +33,14 @@ module Hokusai::Util
|
|
30
33
|
raw[:stop_y] = 0.0
|
31
34
|
raw[:cursor] = nil
|
32
35
|
|
36
|
+
self.cleared = true
|
33
37
|
activate!
|
34
38
|
end
|
35
39
|
|
40
|
+
def changed_direction?
|
41
|
+
@changed_direction
|
42
|
+
end
|
43
|
+
|
36
44
|
def active?
|
37
45
|
type == :active
|
38
46
|
end
|
@@ -73,8 +81,17 @@ module Hokusai::Util
|
|
73
81
|
end
|
74
82
|
|
75
83
|
def stop(x, y)
|
84
|
+
self.cleared = false
|
76
85
|
raw[:stop_x] = x
|
77
86
|
raw[:stop_y] = y
|
87
|
+
|
88
|
+
if up? && @direction == :down || down? && @direction == :up
|
89
|
+
@changed_direction = true
|
90
|
+
else
|
91
|
+
@changed_direction = false
|
92
|
+
end
|
93
|
+
|
94
|
+
@direction = up? ? :up : :down
|
78
95
|
end
|
79
96
|
|
80
97
|
def start_x
|
@@ -93,12 +110,12 @@ module Hokusai::Util
|
|
93
110
|
raw[:start_y]
|
94
111
|
end
|
95
112
|
|
96
|
-
def up?
|
97
|
-
stop_y < start_y
|
113
|
+
def up?(height = 0)
|
114
|
+
stop_y < start_y - height
|
98
115
|
end
|
99
116
|
|
100
|
-
def down?
|
101
|
-
start_y <= stop_y
|
117
|
+
def down?(height = 0)
|
118
|
+
start_y <= stop_y - height
|
102
119
|
end
|
103
120
|
|
104
121
|
def left?
|
@@ -126,7 +143,7 @@ module Hokusai::Util
|
|
126
143
|
return nil if raw[:cursor].null?
|
127
144
|
|
128
145
|
if frozen?
|
129
|
-
return [raw[:cursor][:x], raw[:cursor][:y]
|
146
|
+
return [raw[:cursor][:x], raw[:cursor][:y] + offset_y, raw[:cursor][:w], raw[:cursor][:h]]
|
130
147
|
end
|
131
148
|
|
132
149
|
[raw[:cursor][:x], raw[:cursor][:y], raw[:cursor][:w], raw[:cursor][:h]]
|
@@ -139,7 +156,11 @@ module Hokusai::Util
|
|
139
156
|
def selected(x, y, width, height)
|
140
157
|
return false if none?
|
141
158
|
|
142
|
-
|
159
|
+
if frozen?
|
160
|
+
return LibHokusai.hoku_selection_selected(raw, x, y + offset_y, width, height)
|
161
|
+
else
|
162
|
+
LibHokusai.hoku_selection_selected(raw, x, y, width, height)
|
163
|
+
end
|
143
164
|
end
|
144
165
|
end
|
145
166
|
end
|
@@ -1,193 +1,268 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@extra = extra
|
10
|
-
end
|
1
|
+
class Wrapped
|
2
|
+
attr_accessor :text, :x, :y, :extra
|
3
|
+
|
4
|
+
def initialize(text, x, y, extra)
|
5
|
+
@text = text
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
@extra = extra
|
11
9
|
end
|
10
|
+
end
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
class SelectionWrapper
|
13
|
+
attr_accessor :x, :y, :width, :height, :offset, :buffer
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
def initialize(x, y, w, h)
|
16
|
+
@x = x
|
17
|
+
@y = y
|
18
|
+
@width = w
|
19
|
+
@height = h
|
20
|
+
@offset = 0.0
|
21
|
+
@buffer = ""
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
24
|
+
def <<(w)
|
25
|
+
@width += w
|
26
26
|
end
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
class WrapStream
|
30
|
+
attr_accessor :width, :current_width, :x, :y, :offset_y,
|
31
|
+
:origin_x, :origin_y, :buffer, :stack, :widths, :current_height,
|
32
|
+
:current_offset, :srange, :last, :first
|
33
|
+
attr_reader :on_text_cb, :on_text_selection_cb, :on_advancex_cb, :selector, :padding
|
34
|
+
|
35
|
+
def initialize(width, &measure)
|
36
|
+
@width = width
|
37
|
+
@current_width = 0.0
|
38
|
+
|
39
|
+
@origin_x = 0.0
|
40
|
+
@origin_y = 0.0
|
41
|
+
@x = @origin_x
|
42
|
+
@y = @origin_y
|
43
|
+
|
44
|
+
@current_offset = 0
|
45
|
+
@current_height = 0.0
|
46
|
+
@widths = []
|
47
|
+
@stack = []
|
48
|
+
@srange = nil
|
49
|
+
@selected = ""
|
50
|
+
@buffer = ""
|
51
|
+
@last = nil
|
52
|
+
@first = nil
|
53
|
+
|
54
|
+
@measure_cb = measure
|
55
|
+
end
|
32
56
|
|
33
|
-
|
34
|
-
|
35
|
-
|
57
|
+
def reset(width)
|
58
|
+
@width = width
|
59
|
+
@current_width = 0.0
|
36
60
|
|
37
|
-
|
38
|
-
|
39
|
-
@x = @origin_x
|
40
|
-
@y = @origin_y
|
61
|
+
@x = @origin_x
|
62
|
+
@y = @origin_y
|
41
63
|
|
42
|
-
|
43
|
-
|
64
|
+
@current_offset = 0
|
65
|
+
@current_height = 0.0
|
66
|
+
@widths = []
|
67
|
+
@stack = []
|
68
|
+
@selected = ""
|
69
|
+
@buffer = ""
|
70
|
+
|
71
|
+
@first = nil
|
44
72
|
|
45
|
-
|
46
|
-
|
73
|
+
if @selector&.cleared
|
74
|
+
@selector&.cursor = nil
|
47
75
|
|
48
|
-
|
49
|
-
@
|
76
|
+
@srange = nil
|
77
|
+
@last = nil
|
50
78
|
end
|
79
|
+
end
|
51
80
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
81
|
+
def on_text_selection(selector, padding, &block)
|
82
|
+
@selector = selector
|
83
|
+
@padding = padding || Hokusai::Padding.new(0.0, 0.0, 0.0, 0.0)
|
84
|
+
@on_text_selection_cb = block
|
85
|
+
end
|
57
86
|
|
58
|
-
|
59
|
-
|
60
|
-
|
87
|
+
def on_bounds(&block)
|
88
|
+
@on_bounds_cb = block
|
89
|
+
end
|
61
90
|
|
62
|
-
|
63
|
-
|
64
|
-
|
91
|
+
def on_text(&block)
|
92
|
+
@on_text_cb = block
|
93
|
+
end
|
94
|
+
|
95
|
+
def measure(string, extra)
|
96
|
+
@measure_cb.call(string, extra)
|
97
|
+
end
|
98
|
+
|
99
|
+
def flush
|
100
|
+
sx = x
|
101
|
+
wrapper = nil
|
102
|
+
ii = 0
|
103
|
+
in_bounds = @on_bounds_cb.nil? ? true : @on_bounds_cb.call(y - offset_y)
|
104
|
+
|
105
|
+
stack.each do |(range, extra)|
|
106
|
+
size = buffer[range].size
|
107
|
+
if selector && in_bounds
|
108
|
+
i = 0
|
109
|
+
while i < size
|
110
|
+
nw = widths[i]
|
111
|
+
nh = current_height
|
112
|
+
|
113
|
+
# A char is selected
|
114
|
+
if selector.active? && selector.selected(sx, y, nw, nh)
|
115
|
+
# srange is for selecting downward.
|
116
|
+
# We cache the first char (our pivot point) and as we continue processing
|
117
|
+
# we can just append the next offset to the range
|
118
|
+
# self.srange ||= ((current_offset + i)..(current_offset + i))
|
119
|
+
|
120
|
+
# Selecting upward is more tricky.
|
121
|
+
# We need to cache the current offset as the first and last.
|
122
|
+
# After we process the whole text, we can nil the first offset
|
123
|
+
# And the next iteration the selection will pick up the new first offset
|
124
|
+
# The last offset will remain the origin (pivot)
|
125
|
+
# self.last ||= current_offset + i
|
126
|
+
self.first ||= current_offset + i
|
127
|
+
self.last = current_offset + i
|
128
|
+
self.srange = (first..last)
|
129
|
+
elsif selector.frozen? && selector.up? && selector.selected(sx, y, nw, nh)
|
130
|
+
self.first ||= current_offset + i
|
131
|
+
end
|
65
132
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
str = buffer[range]
|
71
|
-
if selector
|
72
|
-
str.split("").each do |char|
|
73
|
-
nw, nh = measure(char, extra)
|
74
|
-
ox = on_advancex_cb.call(char.codepoints.first)
|
75
|
-
|
76
|
-
if selector.selected(sx, y + padding.top - selector.offset_y, nw, nh)
|
77
|
-
wrapper ||= SelectionWrapper.new(sx, y + padding.top, 0.0, nh)
|
78
|
-
wrapper << ox
|
133
|
+
if srange&.include?(current_offset + i)
|
134
|
+
wrapper ||= begin
|
135
|
+
w = SelectionWrapper.new(sx, y, 0.0, nh)
|
136
|
+
w
|
79
137
|
end
|
80
138
|
|
81
|
-
|
139
|
+
if selector.up?(nh) && first == current_offset + i
|
140
|
+
selector.cursor ||= [sx, y - offset_y, 1.0, nh]
|
141
|
+
elsif selector.down? && last == current_offset + i
|
142
|
+
selector.cursor = [sx + nw, y - offset_y, 1.0, nh]
|
143
|
+
end
|
144
|
+
|
145
|
+
wrapper << nw
|
146
|
+
wrapper.buffer << (buffer[ii + i] || "")
|
82
147
|
end
|
83
148
|
|
84
|
-
|
85
|
-
|
149
|
+
i = i.succ
|
150
|
+
sx += nw
|
86
151
|
end
|
87
152
|
|
88
|
-
|
153
|
+
on_text_selection_cb.call(wrapper) if wrapper
|
154
|
+
wrapper = nil
|
155
|
+
end
|
89
156
|
|
90
|
-
|
91
|
-
|
157
|
+
ii += size
|
158
|
+
self.current_offset += size
|
92
159
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
self.
|
97
|
-
self.buffer = ""
|
98
|
-
stack.clear
|
99
|
-
self.x = origin_x
|
160
|
+
nw = widths[range].sum
|
161
|
+
# hard break on the buffer, split on character
|
162
|
+
wrap_and_call(buffer[range], extra)
|
163
|
+
self.x += nw
|
100
164
|
end
|
101
165
|
|
102
|
-
|
103
|
-
|
104
|
-
|
166
|
+
self.current_width = 0.0
|
167
|
+
self.buffer = ""
|
168
|
+
stack.clear
|
169
|
+
widths.clear
|
170
|
+
self.x = origin_x
|
171
|
+
end
|
105
172
|
|
106
|
-
|
173
|
+
NEW_LINE_REGEX = /\n|\r\n/
|
107
174
|
|
108
|
-
|
109
|
-
|
110
|
-
|
175
|
+
def wrap(text, extra)
|
176
|
+
offset = 0
|
177
|
+
size = text.size
|
111
178
|
|
112
|
-
|
113
|
-
if char =~ /\n|\r\n/
|
114
|
-
flush
|
179
|
+
stack << [((buffer.size)..(text.size + buffer.size - 1)), extra]
|
115
180
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
offset += 1
|
181
|
+
while offset < size
|
182
|
+
w, h = measure(text[offset], extra)
|
183
|
+
self.current_height = h
|
120
184
|
|
121
|
-
|
122
|
-
|
185
|
+
# if it's a newline we want to break
|
186
|
+
if text[offset] =~ NEW_LINE_REGEX
|
187
|
+
self.widths << 0
|
188
|
+
self.buffer << text[offset]
|
189
|
+
flush
|
123
190
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
# if there is a break in this line split the buffer
|
130
|
-
# and render the current line
|
131
|
-
unless idx.nil? || idx < (buffer.size / 2)
|
132
|
-
cur = []
|
133
|
-
nex = []
|
134
|
-
found = false
|
135
|
-
|
136
|
-
# We need to split up both the buffer, and the ranges
|
137
|
-
while payload = stack.shift
|
138
|
-
range, xtra = payload
|
139
|
-
if range.include?(idx)
|
140
|
-
# putting the space on this line
|
141
|
-
cur << [(range.begin..idx), xtra]
|
142
|
-
# pp [range, idx]
|
143
|
-
nex << [(0..(range.end - idx - 1)), xtra] unless idx == range.end
|
144
|
-
|
145
|
-
found = true
|
146
|
-
elsif !found
|
147
|
-
cur << payload
|
148
|
-
else
|
149
|
-
nex << [((range.begin - idx - 1)..(range.end - idx - 1)), xtra]
|
150
|
-
end
|
151
|
-
end
|
191
|
+
stack << [(0...(text.size - offset - 1)), extra]
|
192
|
+
self.y += h
|
193
|
+
self.x = origin_x
|
194
|
+
offset += 1
|
152
195
|
|
153
|
-
|
154
|
-
|
196
|
+
next
|
197
|
+
end
|
155
198
|
|
156
|
-
|
157
|
-
|
199
|
+
# if adding this char extends beyond the boundary
|
200
|
+
if current_width + w > width
|
201
|
+
# find the last space
|
202
|
+
idx = buffer.rindex(" ")
|
203
|
+
|
204
|
+
# if there is a break in this line split the buffer
|
205
|
+
# and render the current line
|
206
|
+
unless idx.nil? || idx < (buffer.size / 2)
|
207
|
+
cur = []
|
208
|
+
nex = []
|
209
|
+
|
210
|
+
found = false
|
211
|
+
|
212
|
+
# We need to split up both the buffer, and the ranges
|
213
|
+
while payload = stack.shift
|
214
|
+
range, xtra = payload
|
215
|
+
if range.include?(idx)
|
216
|
+
# putting the space on this line
|
217
|
+
cur << [(range.begin..idx), xtra]
|
218
|
+
nex << [(0..(range.end - idx - 1)), xtra] unless idx == range.end
|
219
|
+
|
220
|
+
found = true
|
221
|
+
elsif !found
|
222
|
+
cur << payload
|
223
|
+
else
|
224
|
+
nex << [((range.begin - idx - 1)..(range.end - idx - 1)), xtra]
|
225
|
+
end
|
226
|
+
end
|
158
227
|
|
159
|
-
|
160
|
-
|
228
|
+
scur = buffer[0..idx]
|
229
|
+
snex = buffer[(idx + 1)..-1]
|
230
|
+
|
231
|
+
wcur = widths[0..idx]
|
232
|
+
wnex = widths[(idx + 1)..-1]
|
161
233
|
|
162
|
-
|
163
|
-
|
234
|
+
self.buffer = scur
|
235
|
+
self.widths = wcur
|
236
|
+
self.stack = cur
|
237
|
+
flush
|
164
238
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
# break on this word
|
172
|
-
flush
|
173
|
-
|
174
|
-
self.y += h
|
175
|
-
self.current_width = measure(char, extra).first
|
176
|
-
self.buffer = char
|
177
|
-
stack << [(0...(text.size - offset)), extra]
|
178
|
-
end
|
239
|
+
self.buffer = snex + text[offset]
|
240
|
+
self.widths = wnex.concat([w])
|
241
|
+
self.stack = nex
|
242
|
+
self.y += h
|
243
|
+
self.current_width = widths.sum
|
244
|
+
self.x = origin_x
|
179
245
|
else
|
180
|
-
|
181
|
-
|
182
|
-
end
|
246
|
+
# break on this word
|
247
|
+
flush
|
183
248
|
|
184
|
-
|
249
|
+
self.y += h
|
250
|
+
self.current_width = w
|
251
|
+
self.buffer = text[offset]
|
252
|
+
self.widths = [w]
|
253
|
+
stack << [(0...(text.size - offset)), extra]
|
254
|
+
end
|
255
|
+
else
|
256
|
+
self.current_width += w
|
257
|
+
widths << w
|
258
|
+
buffer << text[offset]
|
185
259
|
end
|
186
|
-
|
187
|
-
end
|
188
260
|
|
189
|
-
|
190
|
-
on_text_cb.call Wrapped.new(text, x, y, extra)
|
261
|
+
offset += 1
|
191
262
|
end
|
192
263
|
end
|
264
|
+
|
265
|
+
private def wrap_and_call(text, extra)
|
266
|
+
on_text_cb.call Wrapped.new(text, x, y, extra)
|
267
|
+
end
|
193
268
|
end
|
data/ui/src/hokusai.rb
CHANGED
@@ -13,6 +13,7 @@ require_relative './hokusai/event'
|
|
13
13
|
require_relative './hokusai/painter'
|
14
14
|
require_relative './hokusai/util/selection'
|
15
15
|
require_relative './hokusai/util/piece_table'
|
16
|
+
require_relative './hokusai/util/wrap_stream'
|
16
17
|
|
17
18
|
# A backend agnostic library for authoring
|
18
19
|
# desktop applications
|
@@ -137,6 +138,14 @@ module Hokusai
|
|
137
138
|
@on_set_mouse_cursor&.call(type)
|
138
139
|
end
|
139
140
|
|
141
|
+
def self.on_copy(&block)
|
142
|
+
@on_copy = block
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.copy(text)
|
146
|
+
@on_copy&.call(text)
|
147
|
+
end
|
148
|
+
|
140
149
|
# Mobile support
|
141
150
|
def self.on_show_keyboard(&block)
|
142
151
|
@on_show_keyboard = block
|
@@ -192,4 +201,6 @@ require_relative './hokusai/blocks/shader_begin'
|
|
192
201
|
require_relative './hokusai/blocks/shader_end'
|
193
202
|
require_relative './hokusai/blocks/texture'
|
194
203
|
require_relative './hokusai/blocks/color_picker'
|
195
|
-
require_relative './hokusai/blocks/keyboard'
|
204
|
+
require_relative './hokusai/blocks/keyboard'
|
205
|
+
require_relative './hokusai/blocks/translation'
|
206
|
+
require_relative './hokusai/blocks/text_stream'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hokusai-zero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- skinnyjames
|
@@ -263,9 +263,11 @@ files:
|
|
263
263
|
- ui/src/hokusai/blocks/slider.rb
|
264
264
|
- ui/src/hokusai/blocks/svg.rb
|
265
265
|
- ui/src/hokusai/blocks/text.rb
|
266
|
+
- ui/src/hokusai/blocks/text_stream.rb
|
266
267
|
- ui/src/hokusai/blocks/texture.rb
|
267
268
|
- ui/src/hokusai/blocks/titlebar/osx.rb
|
268
269
|
- ui/src/hokusai/blocks/toggle.rb
|
270
|
+
- ui/src/hokusai/blocks/translation.rb
|
269
271
|
- ui/src/hokusai/blocks/variable.rb
|
270
272
|
- ui/src/hokusai/blocks/vblock.rb
|
271
273
|
- ui/src/hokusai/commands.rb
|
@@ -273,10 +275,13 @@ files:
|
|
273
275
|
- ui/src/hokusai/commands/circle.rb
|
274
276
|
- ui/src/hokusai/commands/image.rb
|
275
277
|
- ui/src/hokusai/commands/rect.rb
|
278
|
+
- ui/src/hokusai/commands/rotation.rb
|
279
|
+
- ui/src/hokusai/commands/scale.rb
|
276
280
|
- ui/src/hokusai/commands/scissor.rb
|
277
281
|
- ui/src/hokusai/commands/shader.rb
|
278
282
|
- ui/src/hokusai/commands/text.rb
|
279
283
|
- ui/src/hokusai/commands/texture.rb
|
284
|
+
- ui/src/hokusai/commands/translation.rb
|
280
285
|
- ui/src/hokusai/diff.rb
|
281
286
|
- ui/src/hokusai/error.rb
|
282
287
|
- ui/src/hokusai/event.rb
|