hokusai-zero 0.1.3 → 0.1.5
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/Gemfile +3 -1
- data/Gemfile.lock +4 -0
- data/README.md +33 -83
- data/ast/src/core/hml.c +9 -9
- data/ast/src/core/text.c +21 -3
- data/ast/test/text.c +3 -3
- data/docs.sh +29 -0
- data/ext/extconf.rb +69 -14
- data/grammar/corpus/1_document.txt +24 -0
- data/grammar/corpus/6_styles.txt +23 -0
- data/grammar/grammar.js +4 -4
- data/grammar/src/grammar.json +19 -19
- data/grammar/src/parser.c +1904 -1956
- data/grammar/test.nml +10 -8
- data/hokusai.gemspec +3 -1
- data/ui/examples/assets/Delius-Regular.ttf +0 -0
- data/ui/examples/assets/DoHyeon.ttf +0 -0
- data/ui/examples/assets/Inter-Regular.ttf +0 -0
- data/ui/examples/assets/ernest.gif +0 -0
- data/ui/examples/assets/icons/audio-x-generic.png +0 -0
- data/ui/examples/assets/icons/image-x-generic.png +0 -0
- data/ui/examples/assets/icons/media-playback-pause.png +0 -0
- data/ui/examples/assets/icons/media-playback-start.png +0 -0
- data/ui/examples/assets/icons/media-playback-stop.png +0 -0
- data/ui/examples/assets/icons/package-x-generic.png +0 -0
- data/ui/examples/assets/icons/text-x-generic.png +0 -0
- data/ui/examples/assets/icons/video-x-generic.png +0 -0
- data/ui/examples/buddy.rb +16 -14
- data/ui/examples/clock.rb +38 -36
- data/ui/examples/counter.rb +100 -98
- data/ui/examples/dynamic.rb +115 -113
- data/ui/examples/foobar.rb +189 -187
- data/ui/examples/forum/file.rb +54 -0
- data/ui/examples/forum/music.rb +76 -0
- data/ui/examples/forum/post.rb +146 -0
- data/ui/examples/forum.rb +198 -0
- data/ui/examples/spreadsheet/csv.rb +261 -0
- data/ui/examples/spreadsheet.rb +138 -0
- data/ui/examples/stock.rb +86 -92
- data/ui/examples/stock_decider/option.rb +1 -1
- data/ui/examples/tic_tac_toe.rb +193 -191
- data/ui/lib/lib_hokusai.rb +2 -1
- data/ui/src/hokusai/assets/arrow-drop-down-line.png +0 -0
- data/ui/src/hokusai/assets/chevron-down.svg +1 -0
- data/ui/src/hokusai/assets/close-large-line.png +0 -0
- data/ui/src/hokusai/ast.rb +42 -43
- data/ui/src/hokusai/backends/raylib/font.rb +1 -2
- data/ui/src/hokusai/backends/raylib.rb +29 -17
- data/ui/src/hokusai/backends/sdl2/font.rb +13 -9
- data/ui/src/hokusai/backends/sdl2.rb +32 -5
- data/ui/src/hokusai/block.rb +14 -7
- data/ui/src/hokusai/blocks/dropdown.rb +205 -0
- data/ui/src/hokusai/blocks/hblock.rb +2 -2
- data/ui/src/hokusai/blocks/image.rb +6 -1
- data/ui/src/hokusai/blocks/input.rb +17 -0
- data/ui/src/hokusai/blocks/label.rb +5 -2
- data/ui/src/hokusai/blocks/modal.rb +62 -0
- data/ui/src/hokusai/blocks/panel.rb +2 -2
- data/ui/src/hokusai/blocks/scrollbar.rb +0 -2
- data/ui/src/hokusai/blocks/text.rb +12 -6
- data/ui/src/hokusai/blocks/titlebar/osx.rb +4 -4
- data/ui/src/hokusai/blocks/variable.rb +33 -0
- data/ui/src/hokusai/blocks/vblock.rb +1 -1
- data/ui/src/hokusai/commands/rect.rb +4 -4
- data/ui/src/hokusai/commands.rb +46 -29
- data/ui/src/hokusai/diff.rb +11 -0
- data/ui/src/hokusai/event.rb +19 -5
- data/ui/src/hokusai/events/mouse.rb +9 -1
- data/ui/src/hokusai/font.rb +60 -0
- data/ui/src/hokusai/meta.rb +11 -24
- data/ui/src/hokusai/node.rb +1 -1
- data/ui/src/hokusai/painter.rb +66 -8
- data/ui/src/hokusai/util/clamping_iterator.rb +5 -6
- data/ui/src/hokusai.rb +53 -4
- metadata +56 -3
@@ -0,0 +1,205 @@
|
|
1
|
+
class Hokusai::Blocks::DropDownItem < Hokusai::Block
|
2
|
+
style <<~EOF
|
3
|
+
[style]
|
4
|
+
cursorStyle {
|
5
|
+
cursor: "pointer";
|
6
|
+
}
|
7
|
+
EOF
|
8
|
+
|
9
|
+
template <<~EOF
|
10
|
+
[template]
|
11
|
+
text {
|
12
|
+
@hover="hover_handler"
|
13
|
+
@mouseout="mouseout_handler"
|
14
|
+
...cursorStyle
|
15
|
+
:content="option"
|
16
|
+
:color="color"
|
17
|
+
:size="size"
|
18
|
+
@click="emit_option"
|
19
|
+
@height_updated="update_height"
|
20
|
+
}
|
21
|
+
EOF
|
22
|
+
|
23
|
+
uses(text: Hokusai::Blocks::Text)
|
24
|
+
|
25
|
+
computed! :index
|
26
|
+
computed! :option
|
27
|
+
computed! :size
|
28
|
+
computed! :color
|
29
|
+
|
30
|
+
attr_accessor :hovered
|
31
|
+
|
32
|
+
def hover_handler(event)
|
33
|
+
self.hovered = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def mouseout_handler(event)
|
37
|
+
self.hovered = false
|
38
|
+
end
|
39
|
+
|
40
|
+
def update_height(height)
|
41
|
+
node.meta.set_prop(:height, height)
|
42
|
+
|
43
|
+
emit("height", index, height)
|
44
|
+
end
|
45
|
+
|
46
|
+
def emit_option(event)
|
47
|
+
emit("option", index)
|
48
|
+
end
|
49
|
+
|
50
|
+
def render(canvas)
|
51
|
+
if hovered
|
52
|
+
draw do
|
53
|
+
rect(canvas.x, canvas.y, canvas.width, canvas.height) do |command|
|
54
|
+
command.color = Hokusai::Color.new(255, 255, 255, 20)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
yield canvas
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Hokusai::Blocks::Dropdown < Hokusai::Block
|
64
|
+
style <<~EOF
|
65
|
+
[style]
|
66
|
+
panelStyle {
|
67
|
+
z: 1;
|
68
|
+
}
|
69
|
+
rootStyle {
|
70
|
+
height: 70.0;
|
71
|
+
color: rgb(122, 118, 135);
|
72
|
+
}
|
73
|
+
|
74
|
+
cursorStyle {
|
75
|
+
cursor: "pointer";
|
76
|
+
}
|
77
|
+
EOF
|
78
|
+
|
79
|
+
template <<~EOF
|
80
|
+
[template]
|
81
|
+
vblock {
|
82
|
+
@hover="dropdown_hover"
|
83
|
+
@mouseout="dropdown_mouseout"
|
84
|
+
@click="dropdown_toggle"
|
85
|
+
:background="background"
|
86
|
+
:height="size_with_padding"
|
87
|
+
:outline="outline"
|
88
|
+
:outline_color="outline_color"
|
89
|
+
}
|
90
|
+
hblock
|
91
|
+
label {
|
92
|
+
...cursorStyle
|
93
|
+
:content="dropdown_option_selected"
|
94
|
+
:size="size"
|
95
|
+
:color="text_color"
|
96
|
+
}
|
97
|
+
image {
|
98
|
+
padding="5.0,10.0,5.0,5.0"
|
99
|
+
:source="dropdown_icon"
|
100
|
+
:height="size"
|
101
|
+
:width="size"
|
102
|
+
}
|
103
|
+
[if="dropdown_is_toggled"]
|
104
|
+
panel#first {
|
105
|
+
...panelStyle
|
106
|
+
:height="panel_height"
|
107
|
+
:background="background"
|
108
|
+
}
|
109
|
+
[for="option in options"]
|
110
|
+
dropdown_item {
|
111
|
+
:color="text_color"
|
112
|
+
:size="size"
|
113
|
+
:height="option_height(index)"
|
114
|
+
:option="option_content(option)"
|
115
|
+
:key="dropdown_key(option, index)"
|
116
|
+
:index="index"
|
117
|
+
@option="option_emit_selected"
|
118
|
+
@height="option_update_height"
|
119
|
+
}
|
120
|
+
EOF
|
121
|
+
|
122
|
+
uses(
|
123
|
+
vblock: Hokusai::Blocks::Vblock,
|
124
|
+
hblock: Hokusai::Blocks::Hblock,
|
125
|
+
label: Hokusai::Blocks::Text,
|
126
|
+
panel: Hokusai::Blocks::Panel,
|
127
|
+
image: Hokusai::Blocks::Image,
|
128
|
+
dropdown_item: Hokusai::Blocks::DropDownItem
|
129
|
+
)
|
130
|
+
|
131
|
+
computed! :options
|
132
|
+
computed :option_callback, default: ->(option) { option }
|
133
|
+
computed :size, default: 25, convert: proc(&:to_i)
|
134
|
+
computed :padding, default: Hokusai::Padding.new(5.0, 5.0, 5.0, 5.0), convert: Hokusai::Padding
|
135
|
+
computed :background, default: Hokusai::Color.new(67, 64, 92), convert: Hokusai::Color
|
136
|
+
computed :outline, default: Hokusai::Outline.new(0.0, 0.0, 1.0, 0.0), convert: Hokusai::Outline
|
137
|
+
computed :outline_color, default: Hokusai::Color.new(122, 118, 135), convert: Hokusai::Color
|
138
|
+
computed :text_color, default: Hokusai::Color.new(255, 255, 255), convert: Hokusai::Color
|
139
|
+
computed :panel_width, default: 200.0, convert: proc(&:to_f)
|
140
|
+
computed :panel_height, default: 300.0, convert: proc(&:to_f)
|
141
|
+
computed :icon, default: "#{ASSETS_FOLDER}/arrow-drop-down-line.png"
|
142
|
+
|
143
|
+
attr_accessor :selected, :dropdown_is_toggled, :hovered
|
144
|
+
attr_reader :heights
|
145
|
+
|
146
|
+
def dropdown_icon
|
147
|
+
icon
|
148
|
+
end
|
149
|
+
|
150
|
+
def dropdown_hover(event)
|
151
|
+
self.hovered = true
|
152
|
+
end
|
153
|
+
|
154
|
+
def dropdown_mouseout(event)
|
155
|
+
self.hovered = false
|
156
|
+
end
|
157
|
+
|
158
|
+
def size_with_padding
|
159
|
+
size + padding.top + padding.bottom
|
160
|
+
end
|
161
|
+
|
162
|
+
def dropdown_key(option, index)
|
163
|
+
"option-#{option_callback.call(option)}-#{index}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def dropdown_toggle(event)
|
167
|
+
self.dropdown_is_toggled = !dropdown_is_toggled
|
168
|
+
end
|
169
|
+
|
170
|
+
def dropdown_option_selected
|
171
|
+
option_callback.call(selected)
|
172
|
+
end
|
173
|
+
|
174
|
+
def option_height(index)
|
175
|
+
heights[index]
|
176
|
+
end
|
177
|
+
|
178
|
+
def option_update_height(index, height)
|
179
|
+
heights[index] = height
|
180
|
+
end
|
181
|
+
|
182
|
+
def option_content(option)
|
183
|
+
option_callback.call(option)
|
184
|
+
end
|
185
|
+
|
186
|
+
def after_updated
|
187
|
+
node.meta.set_prop(:height, size * 2)
|
188
|
+
end
|
189
|
+
|
190
|
+
def option_emit_selected(index)
|
191
|
+
emit("selected", options[index])
|
192
|
+
|
193
|
+
self.selected = options[index]
|
194
|
+
self.dropdown_is_toggled = false
|
195
|
+
end
|
196
|
+
|
197
|
+
def initialize(**args)
|
198
|
+
super
|
199
|
+
|
200
|
+
@hovered = false
|
201
|
+
@heights = {}
|
202
|
+
@dropdown_is_toggled = false
|
203
|
+
@selected = options.first
|
204
|
+
end
|
205
|
+
end
|
@@ -15,12 +15,12 @@ class Hokusai::Blocks::Hblock < Hokusai::Block
|
|
15
15
|
canvas.vertical = false
|
16
16
|
canvas.reverse = reverse
|
17
17
|
|
18
|
-
if background.nil?
|
18
|
+
if background.nil? && outline.nil?
|
19
19
|
yield canvas
|
20
20
|
else
|
21
21
|
draw do
|
22
22
|
rect(canvas.x, canvas.y, canvas.width, canvas.height) do |command|
|
23
|
-
command.color = background
|
23
|
+
command.color = background if background
|
24
24
|
command.outline = outline if outline
|
25
25
|
command.outline_color = outline_color if outline_color
|
26
26
|
command.round = rounding.to_f if rounding
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
1
3
|
class Hokusai::Blocks::Image < Hokusai::Block
|
2
4
|
template <<~EOF
|
3
5
|
[template]
|
@@ -7,10 +9,13 @@ class Hokusai::Blocks::Image < Hokusai::Block
|
|
7
9
|
computed! :source
|
8
10
|
computed :width, default: nil
|
9
11
|
computed :height, default: nil
|
12
|
+
computed :padding, default: Hokusai::Padding.new(0.0, 0.0, 0.0, 0.0), convert: Hokusai::Padding
|
10
13
|
|
11
14
|
def render(canvas)
|
15
|
+
src = Pathname.new(source).absolute? ? source : "#{File.dirname(caller[-1].split(":")[0])}/#{source}"
|
16
|
+
|
12
17
|
draw do
|
13
|
-
image(
|
18
|
+
image(src, canvas.x + padding.left, canvas.y + padding.top, (width&.to_f || canvas.width) - padding.right, (height&.to_f || canvas.height) - padding.bottom)
|
14
19
|
end
|
15
20
|
|
16
21
|
yield canvas
|
@@ -45,6 +45,19 @@ class Hokusai::Blocks::Input < Hokusai::Block
|
|
45
45
|
@start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
46
46
|
@buffer_offset = 0
|
47
47
|
@offset = 0
|
48
|
+
@changed = false
|
49
|
+
end
|
50
|
+
|
51
|
+
def after_updated
|
52
|
+
if @changed && !growable
|
53
|
+
@table = ::Hokusai::Util::PieceTable.new(initial)
|
54
|
+
@buffer = ""
|
55
|
+
@buffer_count = 0
|
56
|
+
@start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
57
|
+
@buffer_offset = 0
|
58
|
+
@offset = 0
|
59
|
+
@changed = false
|
60
|
+
end
|
48
61
|
end
|
49
62
|
|
50
63
|
def update_height(value)
|
@@ -74,6 +87,7 @@ class Hokusai::Blocks::Input < Hokusai::Block
|
|
74
87
|
end
|
75
88
|
|
76
89
|
def dynamic_keypress_handle(event)
|
90
|
+
return unless node.meta.focused
|
77
91
|
case event
|
78
92
|
when proc(&:ctrl), proc(&:super)
|
79
93
|
if event.char == "z"
|
@@ -116,6 +130,7 @@ class Hokusai::Blocks::Input < Hokusai::Block
|
|
116
130
|
flush
|
117
131
|
|
118
132
|
emit("change", table.to_s)
|
133
|
+
@changed = true
|
119
134
|
|
120
135
|
self.table = ::Hokusai::Util::PieceTable.new("")
|
121
136
|
self.buffer_offset = 0
|
@@ -142,6 +157,8 @@ class Hokusai::Blocks::Input < Hokusai::Block
|
|
142
157
|
self.buffer += event.char
|
143
158
|
end
|
144
159
|
end
|
160
|
+
|
161
|
+
emit("modified")
|
145
162
|
end
|
146
163
|
|
147
164
|
def flush
|
@@ -6,6 +6,7 @@ class Hokusai::Blocks::Label < Hokusai::Block
|
|
6
6
|
EOF
|
7
7
|
|
8
8
|
computed! :content
|
9
|
+
computed :font, default: nil
|
9
10
|
computed :size, default: 12
|
10
11
|
computed :color, default: [33,33,33], convert: Hokusai::Color
|
11
12
|
computed :padding, default: [5.0, 5.0, 5.0, 5.0], convert: Hokusai::Padding
|
@@ -21,8 +22,9 @@ class Hokusai::Blocks::Label < Hokusai::Block
|
|
21
22
|
|
22
23
|
def render(canvas)
|
23
24
|
if @last_content != content
|
24
|
-
width,
|
25
|
-
|
25
|
+
width, height = Hokusai.fonts.active.measure(content.to_s, size.to_i)
|
26
|
+
node.meta.set_prop(:width, width + padding.right + padding.left)
|
27
|
+
node.meta.set_prop(:height, height + padding.top + padding.bottom)
|
26
28
|
emit("width_updated", width + padding.right + padding.left)
|
27
29
|
|
28
30
|
@last_content = content
|
@@ -33,6 +35,7 @@ class Hokusai::Blocks::Label < Hokusai::Block
|
|
33
35
|
command.color = color
|
34
36
|
command.size = size
|
35
37
|
command.padding = padding
|
38
|
+
command.font = font unless font.nil?
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Hokusai::Blocks::Modal < Hokusai::Block
|
2
|
+
style <<~EOF
|
3
|
+
[style]
|
4
|
+
closeButtonStyle {
|
5
|
+
width: 40;
|
6
|
+
height: 40;
|
7
|
+
cursor: "pointer";
|
8
|
+
padding: padding(10.0, 10.0, 10.0, 0.0)
|
9
|
+
}
|
10
|
+
EOF
|
11
|
+
|
12
|
+
template <<~EOF
|
13
|
+
[template]
|
14
|
+
hblock
|
15
|
+
empty
|
16
|
+
empty
|
17
|
+
image {
|
18
|
+
:source="close_icon"
|
19
|
+
...closeButtonStyle
|
20
|
+
@click="emit_close"
|
21
|
+
|
22
|
+
}
|
23
|
+
hblock
|
24
|
+
empty
|
25
|
+
slot
|
26
|
+
empty
|
27
|
+
hblock
|
28
|
+
empty
|
29
|
+
EOF
|
30
|
+
|
31
|
+
uses(
|
32
|
+
vblock: Hokusai::Blocks::Vblock,
|
33
|
+
hblock: Hokusai::Blocks::Hblock,
|
34
|
+
empty: Hokusai::Blocks::Empty,
|
35
|
+
image: Hokusai::Blocks::Image
|
36
|
+
)
|
37
|
+
|
38
|
+
computed :active, default: false
|
39
|
+
computed :background, default: [0, 0, 0, 200], convert: Hokusai::Color
|
40
|
+
computed :close_icon, default: "#{ASSETS_FOLDER}/close-large-line.png"
|
41
|
+
|
42
|
+
def emit_close(event)
|
43
|
+
emit("close")
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_mounted
|
47
|
+
node.meta.set_prop(:z, 1)
|
48
|
+
node.meta.set_prop(:ztarget, "root")
|
49
|
+
end
|
50
|
+
|
51
|
+
def render(canvas)
|
52
|
+
return unless active
|
53
|
+
|
54
|
+
draw do
|
55
|
+
rect(canvas.x, canvas.y, canvas.width, canvas.height) do |command|
|
56
|
+
command.color = background
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
yield canvas
|
61
|
+
end
|
62
|
+
end
|
@@ -62,8 +62,8 @@ class Hokusai::Blocks::Panel < Hokusai::Block
|
|
62
62
|
if y = top
|
63
63
|
if new_scroll_y < y
|
64
64
|
self.scroll_goto_y = y
|
65
|
-
elsif new_scroll_y
|
66
|
-
self.scroll_goto_y = panel_height
|
65
|
+
elsif new_scroll_y - top >= panel_height
|
66
|
+
self.scroll_goto_y = panel_height if scroll_percent != 1.0
|
67
67
|
else
|
68
68
|
self.scroll_goto_y = new_scroll_y
|
69
69
|
end
|
@@ -15,13 +15,15 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
15
15
|
uses(empty: Hokusai::Blocks::Empty)
|
16
16
|
|
17
17
|
computed! :content
|
18
|
+
computed :cursor, default: nil
|
19
|
+
computed :font, default: nil
|
18
20
|
computed :size, default: 16, convert: proc(&:to_i)
|
19
21
|
computed :line_height, default: 5, convert: proc(&:to_f)
|
20
22
|
computed :color, default: [33,33,33], convert: Hokusai::Color
|
21
23
|
computed :selection_color, default: [233,233,233], convert: Hokusai::Color
|
22
24
|
computed :padding, default: [5.0, 5.0, 5.0, 5.0], convert: Hokusai::Padding
|
23
25
|
computed :cursor_offset, default: nil
|
24
|
-
computed :markdown, default: false
|
26
|
+
computed :markdown, default: false, convert: ->(val) { val == "true" || val == true }
|
25
27
|
|
26
28
|
inject :selection
|
27
29
|
inject :panel_top
|
@@ -65,7 +67,6 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
65
67
|
|
66
68
|
draw_with do |commands|
|
67
69
|
iterator.on_draw do |text, x, y, group|
|
68
|
-
|
69
70
|
if selector = selection
|
70
71
|
if cursor_offset&.zero? && !selector.started
|
71
72
|
selector.cursor = [x, y + offset_y, 0.0, size.to_f]
|
@@ -86,6 +87,7 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
86
87
|
commands.text(text, x, y) do |command|
|
87
88
|
command.color = color
|
88
89
|
command.size = size
|
90
|
+
command.font = font unless font.nil?
|
89
91
|
|
90
92
|
if group.respond_to?(:bold?)
|
91
93
|
|
@@ -169,7 +171,7 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
169
171
|
end
|
170
172
|
end
|
171
173
|
|
172
|
-
if link_hovered
|
174
|
+
if link_hovered || cursor == "pointer"
|
173
175
|
Hokusai.set_mouse_cursor(:pointer)
|
174
176
|
elsif hovered
|
175
177
|
Hokusai.set_mouse_cursor(:ibeam)
|
@@ -186,7 +188,8 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
186
188
|
|
187
189
|
def clamp(text, width)
|
188
190
|
if markdown
|
189
|
-
Hokusai.fonts.active.clamp_markdown(text, size.to_i, width - size.to_f - padding.right)
|
191
|
+
clamping = Hokusai.fonts.active.clamp_markdown(text, size.to_i, width - size.to_f - padding.right)
|
192
|
+
clamping
|
190
193
|
else
|
191
194
|
Hokusai.fonts.active.clamp(text, size.to_i, width - size.to_f, padding.right)
|
192
195
|
end
|
@@ -203,10 +206,13 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
203
206
|
|
204
207
|
height = internal_render(last_clamping, canvas)
|
205
208
|
self.last_content = last_content
|
206
|
-
|
209
|
+
|
210
|
+
new_height = height + padding.bottom + padding.top
|
211
|
+
new_height -= size if markdown
|
207
212
|
|
208
|
-
|
213
|
+
emit("height_updated", new_height) unless last_height == new_height
|
209
214
|
|
215
|
+
self.last_height = new_height
|
210
216
|
|
211
217
|
yield canvas
|
212
218
|
end
|
@@ -32,12 +32,12 @@ module Hokusai::Blocks::Titlebar
|
|
32
32
|
EOF
|
33
33
|
|
34
34
|
computed :rounding, default: 0.0, convert: proc(&:to_f)
|
35
|
-
computed :outline, default:
|
36
|
-
computed :outline_color, default:
|
35
|
+
computed :outline, default: nil
|
36
|
+
computed :outline_color, default: nil
|
37
37
|
computed :unhovered_color, default: DEFAULT, convert: Hokusai::Color
|
38
38
|
computed :radius, default: 6.0, convert: proc(&:to_f)
|
39
|
-
computed :background, default: nil
|
40
|
-
computed :background_drag, default: nil
|
39
|
+
computed :background, default: nil
|
40
|
+
computed :background_drag, default: nil
|
41
41
|
|
42
42
|
|
43
43
|
uses(
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Hokusai::Blocks::Variable < Hokusai::Block
|
2
|
+
template <<~EOF
|
3
|
+
[template]
|
4
|
+
empty
|
5
|
+
EOF
|
6
|
+
|
7
|
+
uses(empty: Hokusai::Blocks::Empty)
|
8
|
+
|
9
|
+
computed! :script
|
10
|
+
|
11
|
+
def after_updated
|
12
|
+
if @last_height != children[0].node.meta.get_prop(:height)
|
13
|
+
@last_height = children[0].node.meta.get_prop(:height)
|
14
|
+
|
15
|
+
node.meta.set_prop(:height, @last_height)
|
16
|
+
emit("height_updated", @last_height)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_mounted
|
21
|
+
klass = eval(script)
|
22
|
+
|
23
|
+
raise Hokusai::Error.new("Class #{klass} is not a Hokusai::Block") unless klass.ancestors.include?(Hokusai::Block)
|
24
|
+
|
25
|
+
node.meta.set_child(0, klass.mount)
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(canvas)
|
29
|
+
if Hokusai.can_render(canvas)
|
30
|
+
yield canvas
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -26,10 +26,10 @@ module Hokusai
|
|
26
26
|
def trim_canvas(canvas)
|
27
27
|
x, y, w, h = background_boundary
|
28
28
|
|
29
|
-
canvas.x = x + padding.left
|
30
|
-
canvas.y = y + padding.top
|
31
|
-
canvas.width = w - (padding.left + padding.right)
|
32
|
-
canvas.height = h - (padding.top + padding.bottom)
|
29
|
+
canvas.x = x + padding.left + outline.left
|
30
|
+
canvas.y = y + padding.top + outline.top
|
31
|
+
canvas.width = w - (padding.left + padding.right + outline.left + outline.right)
|
32
|
+
canvas.height = h - (padding.top + padding.bottom + outline.top + outline.bottom)
|
33
33
|
|
34
34
|
canvas
|
35
35
|
end
|
data/ui/src/hokusai/commands.rb
CHANGED
@@ -6,82 +6,99 @@ require_relative "./commands/scissor"
|
|
6
6
|
require_relative "./commands/text"
|
7
7
|
|
8
8
|
module Hokusai
|
9
|
+
# A proxy class for invoking various UI commands
|
10
|
+
#
|
11
|
+
# Invocations of commands are immediately sent to the backend
|
12
|
+
# for drawing
|
13
|
+
#
|
14
|
+
# Used as part of the drawing api for Hokusai::Block
|
9
15
|
class Commands
|
10
16
|
attr_reader :queue
|
11
17
|
|
12
18
|
def initialize
|
13
|
-
|
19
|
+
@queue = []
|
14
20
|
end
|
15
21
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
|
22
|
+
# Draw a rectangle
|
23
|
+
#
|
24
|
+
# @param [Float] the x coordinate
|
25
|
+
# @param [Float] the y coordinate
|
26
|
+
# @param [Float] the width of the rectangle
|
27
|
+
# @param [Float] height of the rectangle
|
20
28
|
def rect(x, y, w, h)
|
21
29
|
command = Commands::Rect.new(x, y, w, h)
|
30
|
+
|
22
31
|
yield(command)
|
23
32
|
|
24
|
-
command
|
25
|
-
#
|
33
|
+
queue << command
|
34
|
+
# command.draw
|
26
35
|
end
|
27
36
|
|
37
|
+
# Draw a circle
|
38
|
+
#
|
39
|
+
# @param [Float] x coordinate
|
40
|
+
# @param [Float] y coordinate
|
41
|
+
# @param [Float] radius of the circle
|
28
42
|
def circle(x, y, radius)
|
29
43
|
command = Commands::Circle.new(x, y, radius)
|
44
|
+
|
30
45
|
yield(command)
|
31
46
|
|
32
|
-
|
33
|
-
|
47
|
+
|
48
|
+
queue << command
|
49
|
+
# command.draw
|
34
50
|
end
|
35
51
|
|
52
|
+
# Draws an SVG
|
53
|
+
#
|
54
|
+
# @param [String] location of the svg
|
55
|
+
# @param [Float] x coord
|
56
|
+
# @param [Float] y coord
|
57
|
+
# @param [Float] width of the svg
|
58
|
+
# @param [Float] height of the svg
|
36
59
|
def svg(source, x, y, w, h)
|
37
60
|
command = Commands::SVG.new(source, x, y, w, h)
|
61
|
+
|
38
62
|
yield(command)
|
39
63
|
|
40
|
-
|
64
|
+
queue << command
|
41
65
|
end
|
42
66
|
|
43
67
|
# Invokes an image command
|
44
68
|
# from a filename, at position {x,y} with `w`x`h` dimensions
|
45
69
|
def image(source, x, y, w, h)
|
46
|
-
Commands::Image.new(source, x, y, w, h)
|
47
|
-
# add(Commands::Image.new(source, x, y, w, h))
|
70
|
+
queue << Commands::Image.new(source, x, y, w, h)
|
48
71
|
end
|
49
72
|
|
50
73
|
# Invokes a scissor begin command
|
51
74
|
# at position {x,y} with `w`x`h` dimensions
|
52
75
|
def scissor_begin(x, y, w, h)
|
53
|
-
Commands::ScissorBegin.new(x, y, w, h)
|
54
|
-
# add(Commands::ScissorBegin.new(x, y, w, h))
|
76
|
+
queue << Commands::ScissorBegin.new(x, y, w, h)
|
55
77
|
end
|
56
78
|
|
57
79
|
# Invokes a scissor stop command
|
58
80
|
def scissor_end
|
59
|
-
Commands::ScissorEnd.new
|
60
|
-
# add(Commands::ScissorEnd.new)
|
81
|
+
queue << Commands::ScissorEnd.new
|
61
82
|
end
|
62
83
|
|
84
|
+
# Draws text
|
85
|
+
#
|
86
|
+
# @param [String] the text content
|
87
|
+
# @param [Float] x coord
|
88
|
+
# @param [Float] y coord
|
63
89
|
def text(content, x, y)
|
64
90
|
command = Commands::Text.new(content, x, y)
|
65
91
|
yield command
|
66
92
|
|
67
|
-
command
|
68
|
-
# add(command)
|
93
|
+
queue << command
|
69
94
|
end
|
70
95
|
|
71
|
-
def
|
72
|
-
queue.each
|
73
|
-
yield(cmd)
|
74
|
-
end
|
96
|
+
def execute
|
97
|
+
queue.each(&:draw)
|
75
98
|
end
|
76
99
|
|
77
100
|
def clear!
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
def add(command)
|
82
|
-
# @queue << command
|
83
|
-
|
84
|
-
self
|
101
|
+
queue.clear
|
85
102
|
end
|
86
103
|
end
|
87
104
|
end
|