hokusai-zero 0.1.3 → 0.1.4
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 +2 -0
- data/ast/src/core/hml.c +9 -9
- data/ast/src/core/text.c +1 -3
- data/ast/test/text.c +3 -3
- data/docs.sh +29 -0
- data/ext/extconf.rb +50 -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 +2 -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 +0 -1
- 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 +16 -7
- data/ui/src/hokusai/backends/sdl2/font.rb +13 -9
- data/ui/src/hokusai/backends/sdl2.rb +5 -5
- data/ui/src/hokusai/block.rb +7 -0
- data/ui/src/hokusai/blocks/hblock.rb +2 -2
- data/ui/src/hokusai/blocks/image.rb +5 -1
- data/ui/src/hokusai/blocks/input.rb +17 -0
- data/ui/src/hokusai/blocks/label.rb +5 -2
- data/ui/src/hokusai/blocks/text.rb +10 -5
- 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 +33 -31
- 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 +10 -16
- data/ui/src/hokusai/node.rb +1 -1
- data/ui/src/hokusai/painter.rb +1 -2
- data/ui/src/hokusai/util/clamping_iterator.rb +5 -6
- data/ui/src/hokusai.rb +36 -4
- metadata +37 -3
data/ui/src/hokusai/ast.rb
CHANGED
@@ -25,15 +25,21 @@ module Hokusai
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# The loop variable
|
28
|
+
#
|
28
29
|
# eg [for="item in list"]
|
29
30
|
# the var is `item`
|
31
|
+
#
|
32
|
+
# @return [String]
|
30
33
|
def var
|
31
34
|
@name ||= raw[:name].freeze
|
32
35
|
end
|
33
36
|
|
34
37
|
# The loop method
|
38
|
+
#
|
35
39
|
# eg [for="item in list"]
|
36
40
|
# the method is `list`
|
41
|
+
#
|
42
|
+
# @return [String]
|
37
43
|
def method
|
38
44
|
@list_name ||= raw[:list_name].freeze
|
39
45
|
end
|
@@ -49,6 +55,7 @@ module Hokusai
|
|
49
55
|
end
|
50
56
|
|
51
57
|
# Name of the func
|
58
|
+
#
|
52
59
|
# eg @target="run_this(one,two)"
|
53
60
|
# the name is `run_this`
|
54
61
|
#
|
@@ -58,6 +65,7 @@ module Hokusai
|
|
58
65
|
end
|
59
66
|
|
60
67
|
# Args of the func
|
68
|
+
#
|
61
69
|
# eg @target="run_this(one, two)"
|
62
70
|
# the args are [one, two]
|
63
71
|
#
|
@@ -74,6 +82,7 @@ module Hokusai
|
|
74
82
|
class Event
|
75
83
|
attr_reader :raw
|
76
84
|
|
85
|
+
# @param [LibHokusai::Event] raw event
|
77
86
|
def initialize(raw)
|
78
87
|
@raw = raw
|
79
88
|
end
|
@@ -93,6 +102,7 @@ module Hokusai
|
|
93
102
|
end
|
94
103
|
end
|
95
104
|
|
105
|
+
# A node representing an ast prop
|
96
106
|
class Prop
|
97
107
|
attr_reader :raw
|
98
108
|
|
@@ -100,14 +110,17 @@ module Hokusai
|
|
100
110
|
@raw = raw
|
101
111
|
end
|
102
112
|
|
113
|
+
# @return [Bool] is this prop computed?
|
103
114
|
def computed?
|
104
115
|
raw[:computed]
|
105
116
|
end
|
106
117
|
|
118
|
+
# @return [String] the props name
|
107
119
|
def name
|
108
120
|
@name ||= raw[:name].freeze
|
109
121
|
end
|
110
122
|
|
123
|
+
# @return [Ast::Func] the props value
|
111
124
|
def value
|
112
125
|
@call ||= raw[:call]
|
113
126
|
|
@@ -166,60 +179,74 @@ module Hokusai
|
|
166
179
|
end
|
167
180
|
end
|
168
181
|
|
182
|
+
# Marks this ast as dirty
|
183
|
+
# @return [Void]
|
169
184
|
def dirty!
|
170
185
|
@dirty = true
|
171
186
|
end
|
172
187
|
|
188
|
+
# Is this ast dirty?
|
189
|
+
# @return [Bool]
|
173
190
|
def dirty?
|
174
191
|
@dirty
|
175
192
|
end
|
176
193
|
|
194
|
+
# @return [Bool] is this node a slot?
|
177
195
|
def slot?
|
178
196
|
type == "slot"
|
179
197
|
end
|
180
198
|
|
199
|
+
# @return [Bool] is this node virtual?
|
181
200
|
def virtual?
|
182
201
|
type == "virtual"
|
183
202
|
end
|
184
203
|
|
204
|
+
# @return [Bool] does this node belong to a loop?
|
185
205
|
def loop?
|
186
206
|
@loop_condition = !raw[:loop].null? if @loop_condition.nil?
|
187
207
|
|
188
208
|
@loop_condition
|
189
209
|
end
|
190
210
|
|
211
|
+
# @return [Bool] does this node have an if condition?
|
191
212
|
def has_if_condition?
|
192
213
|
@if_condition = !raw[:cond].null? if @if_condition.nil?
|
193
214
|
|
194
215
|
@if_condition
|
195
216
|
end
|
196
217
|
|
218
|
+
# @return [Bool] does this node have an else condition?
|
197
219
|
def has_else_condition?
|
198
220
|
@else_condition = !raw[:else_relations].null? if @else_condition.nil?
|
199
221
|
|
200
222
|
@else_condition
|
201
223
|
end
|
202
224
|
|
225
|
+
# @return [Bool] is the else condition on this node currently active?
|
203
226
|
def else_condition_active?
|
204
227
|
has_else_condition? && @else_active == 1
|
205
228
|
end
|
206
229
|
|
230
|
+
# @param [Bool] else condition is active or not
|
207
231
|
def else_active=(val)
|
208
232
|
@else_active = val
|
209
233
|
end
|
210
234
|
|
235
|
+
# @return [Hokusai::Ast?] the ast of the else condition
|
211
236
|
def else_ast
|
212
237
|
return nil unless has_else_condition?
|
213
238
|
|
214
239
|
Ast.new(raw[:else_relations][:next_child])
|
215
240
|
end
|
216
241
|
|
242
|
+
# @return [Ast::Loop?] the loop that the ast belongs to
|
217
243
|
def loop
|
218
244
|
return nil unless loop?
|
219
245
|
|
220
246
|
@loop ||= Loop.new(raw[:loop])
|
221
247
|
end
|
222
248
|
|
249
|
+
# @return [Ast::Func?] the if condition of this ast
|
223
250
|
def if
|
224
251
|
@cond ||= raw[:cond]
|
225
252
|
|
@@ -232,14 +259,17 @@ module Hokusai
|
|
232
259
|
@func ||= Func.new(@call)
|
233
260
|
end
|
234
261
|
|
262
|
+
# @return [String] the node type
|
235
263
|
def type
|
236
264
|
@type ||= raw[:type].nil? ? "(null)" : raw[:type]
|
237
265
|
end
|
238
266
|
|
267
|
+
# @return [String] the node id
|
239
268
|
def id
|
240
269
|
@id ||= raw[:id].nil? ? "(null)" : raw[:id]
|
241
270
|
end
|
242
271
|
|
272
|
+
# @return [Array<String>] the list of classes for this node
|
243
273
|
def classes
|
244
274
|
return @classes unless @classes.nil?
|
245
275
|
|
@@ -251,6 +281,7 @@ module Hokusai
|
|
251
281
|
@classes
|
252
282
|
end
|
253
283
|
|
284
|
+
# @return [Array<Hokusai::Ast>] all the children of this node
|
254
285
|
def children
|
255
286
|
return @children unless @children.nil?
|
256
287
|
|
@@ -262,6 +293,7 @@ module Hokusai
|
|
262
293
|
@children
|
263
294
|
end
|
264
295
|
|
296
|
+
# @return [Array<Hokusai::Ast>] all the siblings of this node
|
265
297
|
def siblings
|
266
298
|
return @siblings unless @siblings.nil?
|
267
299
|
|
@@ -277,6 +309,7 @@ module Hokusai
|
|
277
309
|
@siblings
|
278
310
|
end
|
279
311
|
|
312
|
+
# @return [Array<Hokusai::Ast>] the children of the else condition for this node
|
280
313
|
def else_children
|
281
314
|
children = []
|
282
315
|
|
@@ -290,6 +323,7 @@ module Hokusai
|
|
290
323
|
children
|
291
324
|
end
|
292
325
|
|
326
|
+
# @return [Array<Ast::Prop>] all the props of this node
|
293
327
|
def props
|
294
328
|
return @props unless @props.nil?
|
295
329
|
|
@@ -302,6 +336,7 @@ module Hokusai
|
|
302
336
|
@props
|
303
337
|
end
|
304
338
|
|
339
|
+
# @return [Array<Ast::Event>] all the events of this node
|
305
340
|
def events
|
306
341
|
return @events unless @events.nil?
|
307
342
|
|
@@ -382,60 +417,24 @@ module Hokusai
|
|
382
417
|
i.free
|
383
418
|
end
|
384
419
|
|
420
|
+
# Fetches a prop by name
|
421
|
+
#
|
422
|
+
# @param [String] name of the prop
|
423
|
+
# @return [Ast::Prop?] the prop or nil
|
385
424
|
def prop(name)
|
386
425
|
props[name]
|
387
426
|
end
|
388
427
|
|
389
|
-
#
|
390
|
-
#
|
391
|
-
# @param [String] name the name of the prop to find
|
392
|
-
# @return [Hokusai::Ast::Prop?] the prop if found
|
393
|
-
# def prop(name)
|
394
|
-
# prop_ptr = FFI::MemoryPointer.new :pointer
|
395
|
-
# code = LibHokusai.hoku_ast_prop_init(prop_ptr, name)
|
428
|
+
# Fetches an event by name
|
396
429
|
#
|
397
|
-
#
|
398
|
-
#
|
399
|
-
# prop_ptr2 = prop_ptr.get_pointer(0)
|
400
|
-
# prop = LibHokusai::HmlAstProp.new(prop_ptr2)
|
401
|
-
# response = LibHokusai.hoku_ast_get_prop(raw, prop)
|
402
|
-
# return nil if response.null?
|
403
|
-
#
|
404
|
-
# Prop.new(response)
|
405
|
-
# ensure
|
406
|
-
# prop_ptr.free
|
407
|
-
# end
|
408
|
-
|
430
|
+
# @param [String] the name of the event
|
431
|
+
# @return [Ast::Event?] the event or nil
|
409
432
|
def event(name)
|
410
433
|
events[name]
|
411
434
|
end
|
412
435
|
|
413
|
-
# Search for an event by name
|
414
|
-
#
|
415
|
-
# @param [String] name the name of the prop to find
|
416
|
-
# @return [Prop?] the prop if found
|
417
|
-
# def event(name)
|
418
|
-
# event_ptr = FFI::MemoryPointer.new :pointer
|
419
|
-
# code = LibHokusai.hoku_ast_prop_init(event_ptr, name)
|
420
|
-
#
|
421
|
-
# raise Hokusai::Error.new("Failed to allocate prop") unless code.zero?
|
422
|
-
#
|
423
|
-
# event_ptr2 = event_ptr.get_pointer(0)
|
424
|
-
# event = LibHokusai::HmlAstEvent.new(event_ptr2)
|
425
|
-
# response = LibHokusai.hoku_ast_get_event(raw, event)
|
426
|
-
# event_ptr.free
|
427
|
-
#
|
428
|
-
# return nil if response.null?
|
429
|
-
#
|
430
|
-
# Event.new(response)
|
431
|
-
# ensure
|
432
|
-
# event_ptr.free
|
433
|
-
# end
|
434
|
-
|
435
436
|
def destroy
|
436
|
-
puts "before destroy"
|
437
437
|
LibHokusai.hoku_ast_free(raw)
|
438
|
-
puts "after destroy"
|
439
438
|
end
|
440
439
|
|
441
440
|
# dumps this ast to STDOUT
|
@@ -20,7 +20,7 @@ module Hokusai
|
|
20
20
|
OnWidthCb = Proc.new do |char, ffi_pointer|
|
21
21
|
data = DataForCb.new ffi_pointer
|
22
22
|
|
23
|
-
Raylib.MeasureTextEx(data[:raw], "#{char.chr}", data[:size], data[:spacing]).x + data[:spacing]
|
23
|
+
Raylib.MeasureTextEx(data[:raw], "#{char.chr}", data[:size], data[:spacing]).x + 1.565#@ data[:spacing]
|
24
24
|
end
|
25
25
|
|
26
26
|
class Font < Hokusai::Font
|
@@ -83,7 +83,6 @@ module Hokusai
|
|
83
83
|
raw.baseSize
|
84
84
|
end
|
85
85
|
|
86
|
-
|
87
86
|
def clamp_markdown(text, size, width, initial_offset = 0.0)
|
88
87
|
clamping_pointer = FFI::MemoryPointer.new :pointer
|
89
88
|
RaylibBackend::DataForCb.create(size, spacing, raw) do |ptr|
|
@@ -37,8 +37,8 @@ module Hokusai::Backends
|
|
37
37
|
yield(config)
|
38
38
|
end
|
39
39
|
|
40
|
-
def vec2(x, y)
|
41
|
-
if @raylib_vec2.nil?
|
40
|
+
def vec2(x, y, fresh = false)
|
41
|
+
if @raylib_vec2.nil? || fresh
|
42
42
|
@raylib_vec2 = Raylib::Vector2.new
|
43
43
|
end
|
44
44
|
@raylib_vec2[:x] = x
|
@@ -201,7 +201,9 @@ module Hokusai::Backends
|
|
201
201
|
height = last_height
|
202
202
|
|
203
203
|
process_input(input)
|
204
|
+
block.public_send(:before_updated) if block.respond_to?(:before_updated)
|
204
205
|
block.update
|
206
|
+
block.public_send(:after_updated) if block.respond_to?(:after_updated)
|
205
207
|
|
206
208
|
canvas.reset(nil, nil, width.to_f, height.to_f)
|
207
209
|
|
@@ -368,7 +370,12 @@ module Hokusai::Backends
|
|
368
370
|
Hokusai::Commands::Text.on_draw do |command|
|
369
371
|
next unless inside_scissor(command.x, command.y, command.size)
|
370
372
|
|
373
|
+
active_name = Hokusai.fonts.active_font_name
|
371
374
|
font = Hokusai.fonts.active
|
375
|
+
|
376
|
+
Hokusai.fonts.activate command.font.nil? ? active_name : command.font
|
377
|
+
font = Hokusai.fonts.active
|
378
|
+
|
372
379
|
c = color(command.color)
|
373
380
|
x = command.x + command.padding.l
|
374
381
|
y = command.y + command.padding.t
|
@@ -380,6 +387,8 @@ module Hokusai::Backends
|
|
380
387
|
else
|
381
388
|
Raylib.DrawText(command.content, x, y, command.size, c)
|
382
389
|
end
|
390
|
+
|
391
|
+
Hokusai.fonts.activate active_name
|
383
392
|
end
|
384
393
|
|
385
394
|
Hokusai::Commands::Rect.on_draw do |command|
|
@@ -407,26 +416,26 @@ module Hokusai::Backends
|
|
407
416
|
|
408
417
|
# now draw the outlines
|
409
418
|
if command.outline_uniform? && command.rounding > 0.0 && outline_color.a > 0
|
410
|
-
Raylib.
|
419
|
+
Raylib.DrawRectangleRoundedLinesEx(outline_rect, command.rounding, 50, command.outline.top + 1, outline_color)
|
411
420
|
elsif command.outline_uniform? && !(command.rounding <= 0.0) && outline_color.a > 0
|
412
421
|
Raylib.DrawRectangleLinesEx(outline_rect, command.outline.top, outline_color)
|
413
422
|
elsif !command.outline_uniform?
|
414
423
|
ox, oy, ow, oh = command.background_boundary
|
415
424
|
|
416
425
|
if command.outline.top > 0.0
|
417
|
-
Raylib.DrawLineEx(vec2(ox, oy), vec2(ox + ow, oy), command.outline.top, outline_color)
|
426
|
+
Raylib.DrawLineEx(vec2(ox, oy, true), vec2(ox + ow, oy, true), command.outline.top, outline_color)
|
418
427
|
end
|
419
428
|
|
420
429
|
if command.outline.left > 0.0
|
421
|
-
Raylib.DrawLineEx(vec2(ox, oy), vec2(ox, oy + oh), command.outline.left, outline_color)
|
430
|
+
Raylib.DrawLineEx(vec2(ox, oy, true), vec2(ox, oy + oh, true), command.outline.left, outline_color)
|
422
431
|
end
|
423
432
|
|
424
433
|
if command.outline.right > 0.0
|
425
|
-
Raylib.DrawLineEx(vec2(ox + ow, oy), vec2(ox + ow, oy + oh), command.outline.right, outline_color)
|
434
|
+
Raylib.DrawLineEx(vec2(ox + ow, oy, true), vec2(ox + ow, oy + oh, true), command.outline.right, outline_color)
|
426
435
|
end
|
427
436
|
|
428
437
|
if command.outline.bottom > 0.0
|
429
|
-
Raylib.DrawLineEx(vec2(ox, oy + oh), vec2(ox + ow, oy + oh), command.outline.bottom, outline_color)
|
438
|
+
Raylib.DrawLineEx(vec2(ox, oy + oh, true), vec2(ox + ow, oy + oh, true), command.outline.bottom, outline_color)
|
430
439
|
end
|
431
440
|
end
|
432
441
|
else
|
@@ -80,7 +80,6 @@ module Hokusai::Backends
|
|
80
80
|
|
81
81
|
def clamp_markdown(text, size, width, initial_offset = 0.0)
|
82
82
|
self.size = size
|
83
|
-
|
84
83
|
clamping_pointer = FFI::MemoryPointer.new :pointer
|
85
84
|
|
86
85
|
ret = LibHokusai.hoku_text_md_clamp(clamping_pointer, text, width, initial_offset, raw, SDLBackend::OnWidthCb)
|
@@ -105,18 +104,23 @@ module Hokusai::Backends
|
|
105
104
|
end
|
106
105
|
|
107
106
|
def measure(text, size = 15)
|
108
|
-
|
109
|
-
|
107
|
+
begin
|
108
|
+
self.size = size
|
109
|
+
w = FFI::MemoryPointer.new :int
|
110
|
+
h = FFI::MemoryPointer.new :int
|
110
111
|
|
111
|
-
|
112
|
+
SDL.TTF_SizeText(raw, text, w, h)
|
112
113
|
|
113
|
-
|
114
|
-
|
114
|
+
width = w.read_int
|
115
|
+
height = h.read_int
|
115
116
|
|
116
|
-
|
117
|
-
|
117
|
+
w.free
|
118
|
+
h.free
|
118
119
|
|
119
|
-
|
120
|
+
[width, height]
|
121
|
+
ensure
|
122
|
+
self.size = height
|
123
|
+
end
|
120
124
|
end
|
121
125
|
|
122
126
|
def size=(val)
|
@@ -57,7 +57,7 @@ module Hokusai::Backends
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def run(block)
|
60
|
-
resize =
|
60
|
+
resize = true
|
61
61
|
self.render_width = config.width
|
62
62
|
self.render_height = config.height
|
63
63
|
|
@@ -89,7 +89,7 @@ module Hokusai::Backends
|
|
89
89
|
|
90
90
|
Hokusai.fonts.register "default", SDLBackend::Font.from("#{__dir__}/sdl2/Monaco.ttf", 121)
|
91
91
|
Hokusai.fonts.activate "default"
|
92
|
-
config.
|
92
|
+
config.after_load_cb&.call
|
93
93
|
|
94
94
|
register_command_handlers(renderer, window)
|
95
95
|
# MemoryProfiler.start if ENV["PROFILE"]
|
@@ -107,7 +107,7 @@ module Hokusai::Backends
|
|
107
107
|
if process_input(input, event)
|
108
108
|
# since we are using wait event, we need to process the render twice
|
109
109
|
# once to capture all events, and once after updating block state.
|
110
|
-
SDL.SetRenderDrawBlendMode(renderer, SDL::
|
110
|
+
SDL.SetRenderDrawBlendMode(renderer, SDL::BLENDMODE_NONE)
|
111
111
|
SDL.SetRenderDrawColor(renderer, config.background[:r], config.background[:g], config.background[:b], config.background[:a])
|
112
112
|
SDL.RenderClear(renderer)
|
113
113
|
canvas.reset(nil, nil, render_width.to_f, render_height.to_f)
|
@@ -116,7 +116,7 @@ module Hokusai::Backends
|
|
116
116
|
block.update
|
117
117
|
SDL.RenderPresent(renderer)
|
118
118
|
|
119
|
-
SDL.SetRenderDrawBlendMode(renderer, SDL::
|
119
|
+
SDL.SetRenderDrawBlendMode(renderer, SDL::BLENDMODE_NONE)
|
120
120
|
SDL.SetRenderDrawColor(renderer, config.background[:r], config.background[:g], config.background[:b], config.background[:a])
|
121
121
|
SDL.RenderClear(renderer)
|
122
122
|
canvas.reset(nil, nil, render_width.to_f, render_height.to_f)
|
@@ -404,7 +404,6 @@ module Hokusai::Backends
|
|
404
404
|
|
405
405
|
Hokusai::Commands::Rect.on_draw do |command|
|
406
406
|
next unless inside_scissor(command.x, command.y, command.height)
|
407
|
-
|
408
407
|
# draw background first
|
409
408
|
x, y, w, h = [command.x, command.y, command.width, command.height]
|
410
409
|
|
@@ -480,6 +479,7 @@ module Hokusai::Backends
|
|
480
479
|
rounding = command.rounding
|
481
480
|
|
482
481
|
if command.rounding > 0
|
482
|
+
pp command.color
|
483
483
|
SDL.roundedBoxRGBA(renderer, x, y, x + w, y + h, (command.rounding).ceil.to_i, command.color.r, command.color.g, command.color.b, command.color.a)
|
484
484
|
else
|
485
485
|
rect = SDL::Rect.new
|
data/ui/src/hokusai/block.rb
CHANGED
@@ -4,6 +4,11 @@ require "forwardable"
|
|
4
4
|
|
5
5
|
module Hokusai
|
6
6
|
module Blocks; end
|
7
|
+
# A UI Component
|
8
|
+
#
|
9
|
+
# Blocks are reusable and can be mounted in other blocks via templates
|
10
|
+
#
|
11
|
+
# Blocks have `props`` and emit `events`
|
7
12
|
class Block
|
8
13
|
attr_reader :node
|
9
14
|
attr_reader :publisher
|
@@ -61,6 +66,8 @@ module Hokusai
|
|
61
66
|
@styles || {}
|
62
67
|
end
|
63
68
|
|
69
|
+
# Defines blocks that this block uses in it's template
|
70
|
+
# Keys map to template node names, values map to a `Hokusai::Block`
|
64
71
|
def self.uses(**args)
|
65
72
|
args.each do |key, value|
|
66
73
|
raise Hokusai::Error.new("#{key} value must be a Block, got #{value}") unless value.is_a?(Block.class)
|
@@ -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]
|
@@ -9,8 +11,10 @@ class Hokusai::Blocks::Image < Hokusai::Block
|
|
9
11
|
computed :height, default: nil
|
10
12
|
|
11
13
|
def render(canvas)
|
14
|
+
src = Pathname.new(source).absolute? ? source : "#{File.dirname(caller[-1].split(":")[0])}/#{source}"
|
15
|
+
|
12
16
|
draw do
|
13
|
-
image(
|
17
|
+
image(src, canvas.x, canvas.y, width&.to_f || canvas.width, height&.to_f || canvas.height)
|
14
18
|
end
|
15
19
|
|
16
20
|
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
|
@@ -15,13 +15,14 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
15
15
|
uses(empty: Hokusai::Blocks::Empty)
|
16
16
|
|
17
17
|
computed! :content
|
18
|
+
computed :font, default: nil
|
18
19
|
computed :size, default: 16, convert: proc(&:to_i)
|
19
20
|
computed :line_height, default: 5, convert: proc(&:to_f)
|
20
21
|
computed :color, default: [33,33,33], convert: Hokusai::Color
|
21
22
|
computed :selection_color, default: [233,233,233], convert: Hokusai::Color
|
22
23
|
computed :padding, default: [5.0, 5.0, 5.0, 5.0], convert: Hokusai::Padding
|
23
24
|
computed :cursor_offset, default: nil
|
24
|
-
computed :markdown, default: false
|
25
|
+
computed :markdown, default: false, convert: ->(val) { val == "true" || val == true }
|
25
26
|
|
26
27
|
inject :selection
|
27
28
|
inject :panel_top
|
@@ -65,7 +66,6 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
65
66
|
|
66
67
|
draw_with do |commands|
|
67
68
|
iterator.on_draw do |text, x, y, group|
|
68
|
-
|
69
69
|
if selector = selection
|
70
70
|
if cursor_offset&.zero? && !selector.started
|
71
71
|
selector.cursor = [x, y + offset_y, 0.0, size.to_f]
|
@@ -86,6 +86,7 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
86
86
|
commands.text(text, x, y) do |command|
|
87
87
|
command.color = color
|
88
88
|
command.size = size
|
89
|
+
command.font = font unless font.nil?
|
89
90
|
|
90
91
|
if group.respond_to?(:bold?)
|
91
92
|
|
@@ -186,7 +187,8 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
186
187
|
|
187
188
|
def clamp(text, width)
|
188
189
|
if markdown
|
189
|
-
Hokusai.fonts.active.clamp_markdown(text, size.to_i, width - size.to_f - padding.right)
|
190
|
+
clamping = Hokusai.fonts.active.clamp_markdown(text, size.to_i, width - size.to_f - padding.right)
|
191
|
+
clamping
|
190
192
|
else
|
191
193
|
Hokusai.fonts.active.clamp(text, size.to_i, width - size.to_f, padding.right)
|
192
194
|
end
|
@@ -203,10 +205,13 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
203
205
|
|
204
206
|
height = internal_render(last_clamping, canvas)
|
205
207
|
self.last_content = last_content
|
206
|
-
|
208
|
+
|
209
|
+
new_height = height + padding.bottom + padding.top
|
210
|
+
new_height -= size if markdown
|
207
211
|
|
208
|
-
|
212
|
+
emit("height_updated", new_height) unless last_height == new_height
|
209
213
|
|
214
|
+
self.last_height = new_height
|
210
215
|
|
211
216
|
yield canvas
|
212
217
|
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
|