hokusai-zero 0.2.6 → 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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +0 -2
  4. data/README.md +1 -1
  5. data/ast/src/core/ast.c +3 -11
  6. data/ast/src/core/hml.c +214 -40
  7. data/ast/src/core/hml.h +1 -0
  8. data/ast/src/core/input.h +0 -1
  9. data/ast/src/core/log.c +87 -0
  10. data/ast/src/core/log.h +41 -0
  11. data/ast/src/core/util.c +23 -23
  12. data/ast/src/core/util.h +7 -7
  13. data/ast/test/parser.c +1 -0
  14. data/ext/extconf.rb +6 -6
  15. data/hokusai.gemspec +1 -2
  16. data/ui/examples/drag.rb +154 -0
  17. data/ui/examples/embedded.rb +58 -0
  18. data/ui/examples/forum/file.rb +1 -1
  19. data/ui/examples/forum/post.rb +0 -1
  20. data/ui/examples/forum.rb +7 -7
  21. data/ui/examples/game.rb +143 -0
  22. data/ui/examples/keyboard.rb +47 -0
  23. data/ui/examples/overlay.rb +233 -0
  24. data/ui/examples/provider.rb +56 -0
  25. data/ui/examples/shader/test.rb +155 -0
  26. data/ui/examples/shader.rb +100 -0
  27. data/ui/examples/spreadsheet.rb +12 -11
  28. data/ui/examples/wiki.rb +82 -0
  29. data/ui/lib/lib_hokusai.rb +43 -24
  30. data/ui/spec/hokusai/e2e/client_spec.rb +0 -1
  31. data/ui/spec/hokusai/e2e/keyboard_spec.rb +52 -0
  32. data/ui/spec/spec_helper.rb +1 -1
  33. data/ui/src/hokusai/assets/arrow-down-line.png +0 -0
  34. data/ui/src/hokusai/assets/arrow-down-wide-line.png +0 -0
  35. data/ui/src/hokusai/assets/icons/outline/arrow-big-up.svg +19 -0
  36. data/ui/src/hokusai/assets/icons/outline/backspace.svg +20 -0
  37. data/ui/src/hokusai/automation/driver_commands/base.rb +2 -8
  38. data/ui/src/hokusai/automation/driver_commands/trigger_keyboard.rb +3 -6
  39. data/ui/src/hokusai/automation/driver_commands/trigger_mouse.rb +12 -5
  40. data/ui/src/hokusai/automation/server.rb +2 -3
  41. data/ui/src/hokusai/backends/raylib/config.rb +2 -1
  42. data/ui/src/hokusai/backends/raylib/font.rb +55 -4
  43. data/ui/src/hokusai/backends/raylib.rb +199 -36
  44. data/ui/src/hokusai/backends/sdl2/config.rb +9 -6
  45. data/ui/src/hokusai/backends/sdl2/font.rb +3 -1
  46. data/ui/src/hokusai/backends/sdl2.rb +188 -93
  47. data/ui/src/hokusai/blocks/color_picker.rb +1080 -0
  48. data/ui/src/hokusai/blocks/dynamic.rb +2 -0
  49. data/ui/src/hokusai/blocks/input.rb +2 -2
  50. data/ui/src/hokusai/blocks/keyboard.rb +249 -0
  51. data/ui/src/hokusai/blocks/panel.rb +2 -0
  52. data/ui/src/hokusai/blocks/scrollbar.rb +7 -0
  53. data/ui/src/hokusai/blocks/selectable.rb +1 -0
  54. data/ui/src/hokusai/blocks/shader_begin.rb +22 -0
  55. data/ui/src/hokusai/blocks/shader_end.rb +12 -0
  56. data/ui/src/hokusai/blocks/slider.rb +139 -0
  57. data/ui/src/hokusai/blocks/text_stream.rb +130 -0
  58. data/ui/src/hokusai/blocks/texture.rb +23 -0
  59. data/ui/src/hokusai/blocks/translation.rb +91 -0
  60. data/ui/src/hokusai/commands/rect.rb +12 -3
  61. data/ui/src/hokusai/commands/rotation.rb +21 -0
  62. data/ui/src/hokusai/commands/scale.rb +20 -0
  63. data/ui/src/hokusai/commands/shader.rb +33 -0
  64. data/ui/src/hokusai/commands/texture.rb +26 -0
  65. data/ui/src/hokusai/commands/translation.rb +20 -0
  66. data/ui/src/hokusai/commands.rb +49 -3
  67. data/ui/src/hokusai/event.rb +2 -1
  68. data/ui/src/hokusai/events/keyboard.rb +11 -18
  69. data/ui/src/hokusai/events/mouse.rb +10 -8
  70. data/ui/src/hokusai/events/touch.rb +62 -0
  71. data/ui/src/hokusai/meta.rb +13 -6
  72. data/ui/src/hokusai/mounting/loop_entry.rb +4 -4
  73. data/ui/src/hokusai/mounting/update_entry.rb +5 -6
  74. data/ui/src/hokusai/painter.rb +31 -8
  75. data/ui/src/hokusai/types/display.rb +155 -0
  76. data/ui/src/hokusai/types/keyboard.rb +168 -0
  77. data/ui/src/hokusai/types/mouse.rb +36 -0
  78. data/ui/src/hokusai/types/primitives.rb +56 -0
  79. data/ui/src/hokusai/types/touch.rb +181 -0
  80. data/ui/src/hokusai/types.rb +20 -244
  81. data/ui/src/hokusai/util/selection.rb +28 -7
  82. data/ui/src/hokusai/util/wrap_stream.rb +268 -0
  83. data/ui/src/hokusai.rb +72 -35
  84. data/xmake.lua +2 -1
  85. metadata +39 -22
  86. data/ui/src/hokusai/assets/chevron-down.svg +0 -1
@@ -5,6 +5,34 @@ require 'memory_profiler'
5
5
 
6
6
  module Hokusai::Backends
7
7
  class RaylibBackend
8
+
9
+ SDF_SHADER = <<~EOF
10
+ #version 100
11
+
12
+ precision mediump float;
13
+
14
+ // Input vertex attributes (from vertex shader)
15
+ varying vec2 fragTexCoord;
16
+ varying vec4 fragColor;
17
+
18
+ // Input uniform values
19
+ uniform sampler2D texture0;
20
+ uniform vec4 colDiffuse;
21
+
22
+ // NOTE: Add your custom variables here
23
+ const float smoothing = 1.0/32.0;
24
+
25
+ void main()
26
+ {
27
+ // Texel color fetching from texture sampler
28
+ // NOTE: Calculate alpha using signed distance field (SDF)
29
+ float distance = texture2D(texture0, fragTexCoord).a;
30
+ float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance);
31
+
32
+ // Calculate final fragment color
33
+ gl_FragColor = vec4(fragColor.rgb, fragColor.a*alpha);
34
+ }
35
+ EOF
8
36
 
9
37
  RAYLIB_PATH = ENV["RAYLIB_PATH"] || begin
10
38
  path = "#{__dir__}/../../../../vendor/lib"
@@ -28,6 +56,10 @@ module Hokusai::Backends
28
56
  @images ||= {}
29
57
  end
30
58
 
59
+ def self.shaders
60
+ @shaders ||= {}
61
+ end
62
+
31
63
  def self.stopped?
32
64
  @stop = false if @stop.nil?
33
65
 
@@ -69,8 +101,8 @@ module Hokusai::Backends
69
101
  @hml_vec2
70
102
  end
71
103
 
72
- def color(hc)
73
- if @raylib_color.nil?
104
+ def color(hc, fresh = false)
105
+ if @raylib_color.nil? || fresh
74
106
  @raylib_color = Raylib::Color.from_u8(hc.red, hc.green, hc.blue, hc.alpha)
75
107
  else
76
108
  @raylib_color[:r] = hc.red
@@ -109,31 +141,67 @@ module Hokusai::Backends
109
141
  end
110
142
 
111
143
  def process_input(input)
112
- raylib_mouse_pos = Raylib.GetMousePosition
113
- raylib_mouse_delta = Raylib.GetMouseDelta
114
- LibHokusai.hoku_input_mouse_set_scroll(input.raw, Raylib.GetMouseWheelMove)
115
- LibHokusai.hoku_input_set_mouse_position(input.raw, hml_vec2(raylib_mouse_pos.x, raylib_mouse_pos.y))
116
-
117
- input.raw[:mouse][:delta][:x] = raylib_mouse_delta.x
118
- input.raw[:mouse][:delta][:y] = raylib_mouse_delta.y
144
+ if !config.touch
145
+ raylib_mouse_pos = Raylib.GetMousePosition
146
+ raylib_mouse_delta = Raylib.GetMouseDelta
147
+
148
+ input.mouse.pos.x = raylib_mouse_pos.x
149
+ input.mouse.pos.y = raylib_mouse_pos.y
150
+ input.mouse.delta.x = raylib_mouse_delta.x
151
+ input.mouse.delta.y = raylib_mouse_delta.y
152
+ input.mouse.scroll = Raylib.GetMouseWheelMove
153
+ end
119
154
 
120
- [0,1,2].each do |button_id|
121
- clicked = Raylib.IsMouseButtonPressed(button_id)
122
- down = Raylib.IsMouseButtonDown(button_id)
123
- released = Raylib.IsMouseButtonReleased(button_id)
124
- up = Raylib.IsMouseButtonUp(button_id)
155
+ if config.touch
156
+ count = Raylib.GetTouchPointCount
157
+ if count > 0
158
+ vec = Raylib.GetTouchPosition(0)
159
+ unless vec.x.zero? && vec.y.zero?
160
+ input.touch.record(0, vec.x, vec.y)
161
+ end
162
+ else
163
+ input.touch.clear
164
+ end
165
+ else
166
+ {left: 0, middle: 1, right: 2}.each do |key, button_id|
125
167
 
126
- button = mouse_button(clicked: clicked, down: down, released: released, up: up)
127
- LibHokusai.hoku_input_mouse_set_button(input.raw, button, button_id)
168
+ button = input.mouse.public_send(key)
169
+ button.clicked = Raylib.IsMouseButtonPressed(button_id)
170
+ button.down = Raylib.IsMouseButtonDown(button_id)
171
+ button.released = Raylib.IsMouseButtonReleased(button_id)
172
+ button.up = Raylib.IsMouseButtonUp(button_id)
173
+ end
128
174
  end
129
175
 
130
- LibHokusai.hoku_input_keyboard_start(input.raw)
176
+ if config.touch
177
+ if input.touch.touching?
178
+ token = input.touch.token
131
179
 
132
- Keys.each do |(hoku_key, raylib_key)|
133
- LibHokusai.hoku_input_keyboard_set_key(input.raw, hoku_key, Raylib.IsKeyDown(raylib_key))
180
+ input.mouse.pos.x = token[:x]
181
+ input.mouse.pos.y = token[:y]
182
+ end
183
+
184
+ # translate taps to clicks
185
+ if input.touch.tapped?
186
+ input.mouse.left.clicked = true
187
+ input.mouse.left.down = true
188
+ input.mouse.left.released = false
189
+ input.mouse.left.up = false
190
+ else
191
+ input.mouse.left.clicked = true
192
+ input.mouse.left.down = true
193
+ input.mouse.left.released = false
194
+ input.mouse.left.up = false
195
+ end
134
196
  end
135
197
 
136
- LibHokusai.hoku_input_keyboard_stop(input.raw)
198
+ if !input.keyboard_override
199
+ input.keyboard.reset
200
+
201
+ Keys.each do |(hoku_key, raylib_key)|
202
+ input.keyboard.set(hoku_key, Raylib.IsKeyDown(raylib_key))
203
+ end
204
+ end
137
205
  end
138
206
 
139
207
  def self.run(app)
@@ -149,7 +217,6 @@ module Hokusai::Backends
149
217
  self.class.reset
150
218
 
151
219
  Raylib.load_lib(RAYLIB_PATH)
152
-
153
220
  resize = false
154
221
  initial = true
155
222
  width = config.width
@@ -157,11 +224,8 @@ module Hokusai::Backends
157
224
 
158
225
  register_command_handlers
159
226
 
160
- ptr = FFI::MemoryPointer.new :pointer
161
- LibHokusai.hoku_input_init(ptr)
162
- raw = LibHokusai::HmlInput.new(ptr.get_pointer(0))
163
- input = Hokusai::Input.new(raw)
164
- ptr.free
227
+ input = Hokusai::Input.new
228
+ input.support_touch! if config.touch
165
229
 
166
230
  Raylib.SetConfigFlags(config.config_flags)
167
231
  Raylib.InitWindow(config.width, config.height, config.title)
@@ -215,7 +279,7 @@ module Hokusai::Backends
215
279
  block.update
216
280
  block.public_send(:after_updated) if block.respond_to?(:after_updated)
217
281
 
218
- canvas.reset(nil, nil, width.to_f, height.to_f)
282
+ canvas.reset(0.0, 0.0, width.to_f, height.to_f)
219
283
 
220
284
  Raylib.BeginDrawing
221
285
  Raylib.ClearBackground(config.background)
@@ -239,7 +303,6 @@ module Hokusai::Backends
239
303
  Raylib.DrawFPS(10, 10) if ENV["PROFILE"] || ENV["FPS"]
240
304
  Raylib.EndDrawing
241
305
 
242
-
243
306
  break if self.class.stopped?
244
307
  end
245
308
 
@@ -251,7 +314,9 @@ module Hokusai::Backends
251
314
  Raylib.UnloadTexture(texture)
252
315
  end
253
316
 
254
- LibHokusai.hoku_input_free(input.raw)
317
+ self.class.shaders.each do |k, shader|
318
+ Raylib.UnloadShader(shader)
319
+ end
255
320
 
256
321
  if ENV["PROFILE"]
257
322
  report = MemoryProfiler.stop
@@ -263,6 +328,26 @@ module Hokusai::Backends
263
328
  config.automation_driver&.stop
264
329
  end
265
330
 
331
+ def shader_value(value, type)
332
+ case type
333
+ when Hokusai::SHADER_UNIFORM_FLOAT
334
+ ptr = FFI::MemoryPointer.new(:float)
335
+ ptr.write_float(value)
336
+ ptr
337
+ when Hokusai::SHADER_UNIFORM_INT
338
+ ptr = FFI::MemoryPointer.new(:int)
339
+ ptr.write_int(value)
340
+ ptr
341
+ when Hokusai::SHADER_UNIFORM_IVEC2, Hokusai::SHADER_UNIFORM_UIVEC2, Hokusai::SHADER_UNIFORM_VEC2
342
+ Raylib::Vector2.create(*value)
343
+ when Hokusai::SHADER_UNIFORM_VEC3, Hokusai::SHADER_UNIFORM_IVEC3, Hokusai::SHADER_UNIFORM_UIVEC3
344
+ Raylib::Vector3.create(*value)
345
+ when Hokusai::SHADER_UNIFORM_VEC4, Hokusai::SHADER_UNIFORM_UIVEC4, Hokusai::SHADER_UNIFORM_IVEC4
346
+ Raylib::Vector4.create(*value)
347
+ end
348
+ end
349
+
350
+
266
351
  def inside_scissor(x, y, h = 0)
267
352
  return true if @scissor.nil?
268
353
 
@@ -282,11 +367,21 @@ module Hokusai::Backends
282
367
  ibeam: Raylib::MOUSE_CURSOR_IBEAM,
283
368
  crosshair: Raylib::MOUSE_CURSOR_CROSSHAIR,
284
369
  pointer: Raylib::MOUSE_CURSOR_POINTING_HAND,
370
+ none: -1,
285
371
  }[type]
286
372
 
287
373
  raise Hokusai::Error.new("Cursor #{type} not recognized") if raylib_type.nil?
288
374
 
289
- Raylib::SetMouseCursor(raylib_type)
375
+ if raylib_type == -1
376
+ Raylib.HideCursor
377
+ else
378
+ Raylib.ShowCursor
379
+ Raylib::SetMouseCursor(raylib_type)
380
+ end
381
+ end
382
+
383
+ Hokusai.on_copy do |text|
384
+ Raylib.SetClipboardText(text)
290
385
  end
291
386
 
292
387
  Hokusai.on_set_mouse_position do |mouse|
@@ -319,6 +414,66 @@ module Hokusai::Backends
319
414
  inside_scissor(canvas.x, canvas.y, canvas.height)
320
415
  end
321
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
+
445
+ Hokusai::Commands::ShaderBegin.on_draw do |command|
446
+ self.class.shaders[command.hash] ||= Raylib.LoadShaderFromMemory(command.vertex_shader, command.fragment_shader)
447
+
448
+ unless command.uniforms.empty?
449
+ command.uniforms.each do |key, value|
450
+ location = Raylib.GetShaderLocation(self.class.shaders[command.hash], key.to_s)
451
+
452
+ ptr = shader_value(value[0], value[1])
453
+ Raylib.SetShaderValue(self.class.shaders[command.hash], location, ptr, value[1])
454
+ ptr.free if ptr.is_a?(FFI::MemoryPointer)
455
+ end
456
+ end
457
+
458
+ Raylib.BeginShaderMode(self.class.shaders[command.hash])
459
+
460
+ @shader = self.class.shaders[command.hash]
461
+ end
462
+
463
+ Hokusai::Commands::ShaderEnd.on_draw do |_|
464
+ Raylib.EndShaderMode
465
+
466
+ @shader = nil
467
+ end
468
+
469
+ Hokusai::Commands::Texture.on_draw do |command|
470
+ self.class.images[command.hash] ||= begin
471
+ image = Raylib.GenImageColor(command.width, command.height, Raylib::BLANK)
472
+ Raylib.LoadTextureFromImage(image)
473
+ end
474
+ Raylib.DrawTexture(self.class.images[command.hash], command.x, command.y, Raylib::WHITE)
475
+ end
476
+
322
477
  Hokusai::Commands::ScissorBegin.on_draw do |command|
323
478
  Raylib.BeginScissorMode(command.x, command.y, command.width, command.height)
324
479
  @scissor = [command.x, command.y, command.width, command.height]
@@ -384,9 +539,9 @@ module Hokusai::Backends
384
539
  next unless inside_scissor(command.x, command.y, command.size)
385
540
 
386
541
  active_name = Hokusai.fonts.active_font_name
387
- font = Hokusai.fonts.active
542
+ font_name = command.font.nil? ? active_name : command.font
388
543
 
389
- Hokusai.fonts.activate command.font.nil? ? active_name : command.font
544
+ Hokusai.fonts.activate font_name
390
545
  font = Hokusai.fonts.active
391
546
 
392
547
  c = color(command.color)
@@ -394,9 +549,16 @@ module Hokusai::Backends
394
549
  y = command.y + command.padding.t
395
550
 
396
551
  if fnt = font
397
- # content = FFI::MemoryPointer.from_string(command.content)
552
+ if fnt.sdf
553
+ self.class.shaders["font-sdf-#{font_name}"] ||= Raylib.LoadShaderFromMemory(nil, SDF_SHADER)
554
+ Raylib.BeginShaderMode(self.class.shaders["font-sdf-#{font_name}"])
555
+ end
556
+
398
557
  Raylib.DrawTextEx(fnt.raw, command.content.to_s, vec2(x, y), command.size, fnt.spacing, c)
399
- # content.free
558
+
559
+ if fnt.sdf
560
+ Raylib.EndShaderMode
561
+ end
400
562
  else
401
563
  Raylib.DrawText(command.content, x, y, command.size, c)
402
564
  end
@@ -453,8 +615,9 @@ module Hokusai::Backends
453
615
  end
454
616
  else
455
617
  rect = rect(command.background_boundary)
456
-
457
- if command.rounding > 0
618
+ if command.gradient
619
+ Raylib.DrawRectangleGradientEx(rect, color(command.gradient[0], true), color(command.gradient[1], true), color(command.gradient[2], true), color(command.gradient[3], true))
620
+ elsif command.rounding > 0
458
621
  Raylib.DrawRectangleRounded(rect, command.rounding, 50, background_color)
459
622
  else
460
623
  Raylib.DrawRectangleRec(rect, background_color)
@@ -3,8 +3,8 @@ module Hokusai::Backends
3
3
  class Config
4
4
  attr_accessor :width, :height, :fps, :init_flags,
5
5
  :title, :background, :automation_driver,
6
- :after_load_cb, :host, :port, :automated,
7
- :window_config_flags
6
+ :after_load_cb, :host, :port, :automated, :mobile,
7
+ :window_config_flags, :touch
8
8
 
9
9
  def after_load(&block)
10
10
  self.after_load_cb = block
@@ -14,16 +14,19 @@ module Hokusai::Backends
14
14
  @width = 500
15
15
  @height = 500
16
16
  @fps = 60
17
+ @touch = false
17
18
 
18
19
  @init_flags = ::SDL::INIT_VIDEO | ::SDL::INIT_EVENTS
19
- @window_config_flags = SDL::WINDOW_RESIZABLE
20
+ @window_config_flags = SDL::WINDOW_RESIZABLE | SDL::WINDOW_ALLOW_HIGHDPI
20
21
  @title = "(Unknown Title)"
21
22
  @background = SDLBackend.color(255,255,255, 0)
22
23
 
23
24
  after_load do
24
- font = Hokusai::Backends::SDLBackend::Font.from "#{__dir__}/Monaco.ttf"
25
- Hokusai.fonts.register "default", font
26
- Hokusai.fonts.activate "default"
25
+ if Hokusai.fonts.get("default").nil?
26
+ font = Hokusai::Backends::SDLBackend::Font.from "#{__dir__}/Monaco.ttf"
27
+ Hokusai.fonts.register "default", font
28
+ Hokusai.fonts.activate "default"
29
+ end
27
30
  end
28
31
  end
29
32
  end
@@ -21,7 +21,7 @@ module Hokusai::Backends
21
21
  end
22
22
 
23
23
  class Font < Hokusai::Font
24
- def self.from(file = "#{__dir__}/Monaco.ttf", size=50)
24
+ def self.from(file = "#{__dir__}/Monaco.ttf", size=60)
25
25
  raw = SDL.TTF_OpenFont(file, size)
26
26
 
27
27
  raise Hokusai::Error.new("Cannot open font from #{file}: #{ttf_error}") if raw.null?
@@ -51,6 +51,8 @@ module Hokusai::Backends
51
51
  set_style(**args)
52
52
 
53
53
  render_color = SDLBackend.color(color.r, color.g, color.b, color.a)
54
+ # bg = SDLBackend.color(0, 0, 0, 0)
55
+
54
56
  SDL.TTF_RenderUTF8_Blended(raw, text, render_color)
55
57
  end
56
58