hokusai-zero 0.1.1

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 (166) hide show
  1. checksums.yaml +7 -0
  2. data/Dockerfile +26 -0
  3. data/Gemfile +15 -0
  4. data/Gemfile.lock +91 -0
  5. data/LICENSE +21 -0
  6. data/README.md +28 -0
  7. data/ast/genheader +3 -0
  8. data/ast/include/hashmap.c +1151 -0
  9. data/ast/include/hashmap.c.license +20 -0
  10. data/ast/include/hashmap.h +54 -0
  11. data/ast/src/core/ast.c +448 -0
  12. data/ast/src/core/ast.h +259 -0
  13. data/ast/src/core/common.h +24 -0
  14. data/ast/src/core/component.c +85 -0
  15. data/ast/src/core/component.h +35 -0
  16. data/ast/src/core/hml.c +665 -0
  17. data/ast/src/core/hml.h +11 -0
  18. data/ast/src/core/input.c +458 -0
  19. data/ast/src/core/input.h +118 -0
  20. data/ast/src/core/style.c +101 -0
  21. data/ast/src/core/style.h +41 -0
  22. data/ast/src/core/text.c +784 -0
  23. data/ast/src/core/text.h +93 -0
  24. data/ast/src/core/util.c +140 -0
  25. data/ast/src/core/util.h +48 -0
  26. data/ast/src/hokusai.c +6 -0
  27. data/ast/src/hokusai.h +6 -0
  28. data/ast/test/fixtures/test.ui +13 -0
  29. data/ast/test/greatest.h +1266 -0
  30. data/ast/test/hokusai.c +14 -0
  31. data/ast/test/parser.c +234 -0
  32. data/ast/test/text.c +116 -0
  33. data/ext/extconf.rb +27 -0
  34. data/grammar/Cargo.lock +80 -0
  35. data/grammar/Cargo.toml +26 -0
  36. data/grammar/binding.gyp +20 -0
  37. data/grammar/bindings/node/binding.cc +28 -0
  38. data/grammar/bindings/node/index.js +19 -0
  39. data/grammar/bindings/rust/build.rs +40 -0
  40. data/grammar/bindings/rust/lib.rs +52 -0
  41. data/grammar/corpus/1_document.txt +131 -0
  42. data/grammar/corpus/2_selectors.txt +58 -0
  43. data/grammar/corpus/3_spaces.txt +69 -0
  44. data/grammar/corpus/4_errors.txt +10 -0
  45. data/grammar/corpus/5_macros.txt +175 -0
  46. data/grammar/corpus/6_styles.txt +81 -0
  47. data/grammar/grammar.js +275 -0
  48. data/grammar/package-lock.json +34 -0
  49. data/grammar/package.json +33 -0
  50. data/grammar/src/grammar.json +1269 -0
  51. data/grammar/src/node-types.json +474 -0
  52. data/grammar/src/parser.c +5772 -0
  53. data/grammar/src/scanner.c +258 -0
  54. data/grammar/src/tree_sitter/parser.h +230 -0
  55. data/grammar/src/tree_sitter/scanner.h +12 -0
  56. data/grammar/test.nml +10 -0
  57. data/hokusai.gemspec +19 -0
  58. data/ui/examples/assets/DigitalDisplay.ttf +0 -0
  59. data/ui/examples/assets/OpenSans-Regular.ttf +0 -0
  60. data/ui/examples/assets/addy.png +0 -0
  61. data/ui/examples/assets/baby_sean.png +0 -0
  62. data/ui/examples/assets/football-troll.png +0 -0
  63. data/ui/examples/assets/gear.png +0 -0
  64. data/ui/examples/assets/icecold.ttf +0 -0
  65. data/ui/examples/assets/science-troll.png +0 -0
  66. data/ui/examples/buddy.rb +31 -0
  67. data/ui/examples/clock.rb +58 -0
  68. data/ui/examples/counter.rb +123 -0
  69. data/ui/examples/dynamic.rb +147 -0
  70. data/ui/examples/foobar.rb +236 -0
  71. data/ui/examples/stock.rb +115 -0
  72. data/ui/examples/stock_decider/option.rb +74 -0
  73. data/ui/examples/tic_tac_toe.rb +246 -0
  74. data/ui/lib/lib_hokusai.rb +425 -0
  75. data/ui/spec/hokusai/ast_spec.rb +88 -0
  76. data/ui/spec/hokusai/automation/keys_transcoder_spec.rb +50 -0
  77. data/ui/spec/hokusai/automation/selector_spec.rb +68 -0
  78. data/ui/spec/hokusai/block_spec.rb +126 -0
  79. data/ui/spec/hokusai/directives_spec.rb +327 -0
  80. data/ui/spec/hokusai/e2e/client_spec.rb +58 -0
  81. data/ui/spec/hokusai/e2e/meta_spec.rb +42 -0
  82. data/ui/spec/hokusai/publisher_spec.rb +38 -0
  83. data/ui/spec/hokusai/slots_spec.rb +150 -0
  84. data/ui/spec/hokusai/util/piece_table_spec.rb +90 -0
  85. data/ui/spec/hokusai_spec.rb +0 -0
  86. data/ui/spec/spec_helper.rb +30 -0
  87. data/ui/src/hokusai/ast.rb +446 -0
  88. data/ui/src/hokusai/automation/client.rb +167 -0
  89. data/ui/src/hokusai/automation/constants.rb +98 -0
  90. data/ui/src/hokusai/automation/converters/selector_converter.rb +61 -0
  91. data/ui/src/hokusai/automation/driver.rb +54 -0
  92. data/ui/src/hokusai/automation/driver_command_queue.rb +50 -0
  93. data/ui/src/hokusai/automation/driver_commands/base.rb +79 -0
  94. data/ui/src/hokusai/automation/driver_commands/get_attribute.rb +41 -0
  95. data/ui/src/hokusai/automation/driver_commands/invoke.rb +33 -0
  96. data/ui/src/hokusai/automation/driver_commands/locate.rb +48 -0
  97. data/ui/src/hokusai/automation/driver_commands/trigger_keyboard.rb +94 -0
  98. data/ui/src/hokusai/automation/driver_commands/trigger_mouse.rb +213 -0
  99. data/ui/src/hokusai/automation/keys_transcoder.rb +128 -0
  100. data/ui/src/hokusai/automation/selector.rb +39 -0
  101. data/ui/src/hokusai/automation/server.rb +114 -0
  102. data/ui/src/hokusai/automation.rb +3 -0
  103. data/ui/src/hokusai/backends/raylib/config.rb +47 -0
  104. data/ui/src/hokusai/backends/raylib/font.rb +113 -0
  105. data/ui/src/hokusai/backends/raylib/keys.rb +124 -0
  106. data/ui/src/hokusai/backends/raylib.rb +449 -0
  107. data/ui/src/hokusai/backends/sdl2/Monaco.ttf +0 -0
  108. data/ui/src/hokusai/backends/sdl2/color.rb +12 -0
  109. data/ui/src/hokusai/backends/sdl2/config.rb +31 -0
  110. data/ui/src/hokusai/backends/sdl2/font.rb +127 -0
  111. data/ui/src/hokusai/backends/sdl2/keys.rb +119 -0
  112. data/ui/src/hokusai/backends/sdl2.rb +529 -0
  113. data/ui/src/hokusai/block.rb +237 -0
  114. data/ui/src/hokusai/blocks/button.rb +100 -0
  115. data/ui/src/hokusai/blocks/checkbox.rb +51 -0
  116. data/ui/src/hokusai/blocks/circle.rb +28 -0
  117. data/ui/src/hokusai/blocks/clipped.rb +23 -0
  118. data/ui/src/hokusai/blocks/cursor.rb +49 -0
  119. data/ui/src/hokusai/blocks/dynamic.rb +37 -0
  120. data/ui/src/hokusai/blocks/empty.rb +10 -0
  121. data/ui/src/hokusai/blocks/hblock.rb +35 -0
  122. data/ui/src/hokusai/blocks/image.rb +18 -0
  123. data/ui/src/hokusai/blocks/input.rb +200 -0
  124. data/ui/src/hokusai/blocks/label.rb +39 -0
  125. data/ui/src/hokusai/blocks/panel.rb +126 -0
  126. data/ui/src/hokusai/blocks/rect.rb +24 -0
  127. data/ui/src/hokusai/blocks/scissor_begin.rb +18 -0
  128. data/ui/src/hokusai/blocks/scissor_end.rb +12 -0
  129. data/ui/src/hokusai/blocks/scrollbar.rb +103 -0
  130. data/ui/src/hokusai/blocks/selectable.rb +77 -0
  131. data/ui/src/hokusai/blocks/svg.rb +20 -0
  132. data/ui/src/hokusai/blocks/text.rb +214 -0
  133. data/ui/src/hokusai/blocks/titlebar/osx.rb +145 -0
  134. data/ui/src/hokusai/blocks/toggle.rb +55 -0
  135. data/ui/src/hokusai/blocks/vblock.rb +35 -0
  136. data/ui/src/hokusai/commands/base.rb +22 -0
  137. data/ui/src/hokusai/commands/circle.rb +47 -0
  138. data/ui/src/hokusai/commands/image.rb +45 -0
  139. data/ui/src/hokusai/commands/rect.rb +158 -0
  140. data/ui/src/hokusai/commands/scissor.rb +22 -0
  141. data/ui/src/hokusai/commands/text.rb +92 -0
  142. data/ui/src/hokusai/commands.rb +87 -0
  143. data/ui/src/hokusai/diff.rb +124 -0
  144. data/ui/src/hokusai/error.rb +3 -0
  145. data/ui/src/hokusai/event.rb +54 -0
  146. data/ui/src/hokusai/events/keyboard.rb +84 -0
  147. data/ui/src/hokusai/events/mouse.rb +172 -0
  148. data/ui/src/hokusai/font.rb +280 -0
  149. data/ui/src/hokusai/meta.rb +152 -0
  150. data/ui/src/hokusai/mounting/loop_entry.rb +230 -0
  151. data/ui/src/hokusai/mounting/mount_entry.rb +74 -0
  152. data/ui/src/hokusai/mounting/update_entry.rb +101 -0
  153. data/ui/src/hokusai/node.rb +98 -0
  154. data/ui/src/hokusai/node_mounter.rb +102 -0
  155. data/ui/src/hokusai/painter.rb +214 -0
  156. data/ui/src/hokusai/publisher.rb +32 -0
  157. data/ui/src/hokusai/style.rb +72 -0
  158. data/ui/src/hokusai/types.rb +266 -0
  159. data/ui/src/hokusai/util/clamping_iterator.rb +202 -0
  160. data/ui/src/hokusai/util/piece_table.rb +111 -0
  161. data/ui/src/hokusai/util/selection.rb +145 -0
  162. data/ui/src/hokusai.rb +120 -0
  163. data/ui/vendor/.gitkeep +0 -0
  164. data/vendor/.gitkeep +0 -0
  165. data/xmake.lua +192 -0
  166. metadata +222 -0
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./event"
4
+
5
+ module Hokusai
6
+ class PainterEntry
7
+ attr_reader :block, :parent, :x, :y, :w, :h
8
+ def initialize(block, x, y, w, h)
9
+ @block = block
10
+ @x = x
11
+ @y = y
12
+ @w = w
13
+ @h = h
14
+ end
15
+ end
16
+
17
+ class CursorState
18
+ attr_accessor :set
19
+
20
+ def initialize
21
+ @set = false
22
+ end
23
+ end
24
+
25
+ class Painter
26
+ attr_reader :root, :input, :before_render, :after_render,
27
+ :events
28
+
29
+ def initialize(root, input)
30
+ state = CursorState.new
31
+
32
+ @root = root
33
+ @input = input
34
+ @events = {
35
+ hover: HoverEvent.new(input, state),
36
+ wheel: WheelEvent.new(input, state),
37
+ click: ClickEvent.new(input, state),
38
+ mousemove: MouseMoveEvent.new(input, state),
39
+ mouseout: MouseOutEvent.new(input, state),
40
+ mouseup: MouseUpEvent.new(input, state),
41
+ mousedown: MouseDownEvent.new(input, state),
42
+ keyup: KeyUpEvent.new(input, state),
43
+ keypress: KeyPressEvent.new(input, state)
44
+ }
45
+ end
46
+
47
+ def on_before_render(&block)
48
+ @before_render = block
49
+ end
50
+
51
+ def on_after_render(&block)
52
+ @after_render = block
53
+ end
54
+
55
+ # @return [Array(Commands::Base)] the command list
56
+ def render(canvas, resize = false, capture: true)
57
+ return if root.children.empty?
58
+
59
+ @root.on_resize(canvas) if resize
60
+
61
+ before_render&.call([root, nil], canvas, input)
62
+
63
+ root_children = (canvas.reverse? ? root.children?&.reverse.dup : root.children?&.dup) || []
64
+ groups = []
65
+ groups << [root, measure(root_children, canvas)]
66
+
67
+ mouse_y = input.mouse.pos[:y]
68
+ can_capture = mouse_y >= (canvas.y || 0.0) && mouse_y <= (canvas.y || 0.0) + canvas.height
69
+
70
+ hovered = false
71
+
72
+ while payload = groups.pop
73
+ group_parent, group_children = payload
74
+
75
+ while group = group_children.shift
76
+ canvas.reset(group.x, group.y, group.w, group.h)
77
+
78
+ before_render&.call([group.block, group.parent], canvas, input.raw)
79
+
80
+ if capture && can_capture
81
+ capture_events(group.block, canvas, hovered: hovered)
82
+ end
83
+
84
+ if resize
85
+ group.block.on_resize(canvas)
86
+ end
87
+
88
+ breaked = false
89
+
90
+ group.block.render(canvas) do |local_canvas|
91
+ local_children = (local_canvas.reverse? ? group.block.children?&.reverse : group.block.children?)
92
+
93
+ unless local_children.nil?
94
+ groups << [group_parent, group_children]
95
+ groups << [group.block, measure(local_children, local_canvas)]
96
+ breaked = true
97
+ else
98
+ breaked = false
99
+ end
100
+ end
101
+
102
+ break if breaked
103
+ end
104
+ end
105
+
106
+ if capture
107
+ events[:hover].bubble
108
+ events[:wheel].bubble
109
+ events[:click].bubble
110
+ events[:keyup].bubble
111
+ events[:keypress].bubble
112
+ events[:mousemove].bubble
113
+ events[:mouseout].bubble
114
+ events[:mousedown].bubble
115
+ events[:mouseup].bubble
116
+ end
117
+
118
+ after_render&.call
119
+ end
120
+
121
+ def measure(children, canvas)
122
+ x = canvas.x || 0.0
123
+ y = canvas.y || 0.0
124
+ width = canvas.width
125
+ height = canvas.height
126
+ vertical = canvas.vertical
127
+
128
+ count = 0
129
+ wcount = 0
130
+ hcount = 0
131
+ wsum = 0.0
132
+ hsum = 0.0
133
+
134
+ children.each do |block|
135
+ # children.each do |block|
136
+ h = block.node.meta.get_prop?(:height)&.to_f
137
+ w = block.node.meta.get_prop?(:width)&.to_f
138
+
139
+ if w
140
+ wsum += w
141
+ wcount = wcount.succ
142
+ end
143
+
144
+ if h
145
+ hsum += h
146
+ hcount = hcount.succ
147
+ end
148
+
149
+ count = count.succ
150
+ end
151
+
152
+ neww = width
153
+ newh = height
154
+
155
+ if vertical
156
+ c = (count - hcount)
157
+ newh = (newh - hsum) / (c.zero? ? 1 : c)
158
+ else
159
+ c = (count - wcount)
160
+ neww = (neww - wsum) / (c.zero? ? 1 : c)
161
+ end
162
+
163
+ entries = []
164
+
165
+ children.each do |block|
166
+ # nw, nh = ntuple
167
+ w = block.node.meta.get_prop?(:width)&.to_f || neww
168
+ h = block.node.meta.get_prop?(:height)&.to_f || newh
169
+
170
+ # local_canvas = Hokusai::Canvas.new(w, h, x, y)
171
+ # block.node.meta.props[:height] ||= h
172
+ # block.node.meta.props[:width] ||= w
173
+
174
+ entries << PainterEntry.new(block, x, y, w, h).freeze
175
+
176
+ if vertical
177
+ y += h
178
+ else
179
+ x += w
180
+ end
181
+ end
182
+
183
+ entries
184
+ end
185
+
186
+ def capture_events(block, canvas, hovered: false)
187
+ if block.node.portal.nil?
188
+ return
189
+ end
190
+
191
+
192
+ rect = canvas.to_hoku_rect
193
+ block_is_hovered = LibHokusai.hoku_input_is_hovered(input.raw, rect)
194
+
195
+ if block_is_hovered
196
+ events[:hover].capture(block, canvas)
197
+ events[:click].capture(block, canvas)
198
+ events[:wheel].capture(block, canvas)
199
+ events[:mousedown].capture(block, canvas)
200
+ events[:mouseup].capture(block, canvas)
201
+ else
202
+ events[:mouseout].capture(block, canvas)
203
+ end
204
+
205
+ events[:mousemove].capture(block, canvas)
206
+
207
+ if block_is_hovered || block.node.meta.focused
208
+ events[:keyup].capture(block, canvas)
209
+ events[:keypress].capture(block, canvas)
210
+ end
211
+ end
212
+ end
213
+ end
214
+
@@ -0,0 +1,32 @@
1
+ module Hokusai
2
+ # An event emitter
3
+ class Publisher
4
+ attr_reader :listeners
5
+
6
+ def initialize(listeners = [])
7
+ @listeners = listeners
8
+ end
9
+
10
+ # Adds a listener that subscribes
11
+ # to events emitted
12
+ # by this publisher
13
+ #
14
+ # @param [Hokusai::Block] listener
15
+ def add(listener)
16
+ listeners << listener
17
+ end
18
+
19
+ # emits `event` with `**args`
20
+ # to all subscribers
21
+ # @see
22
+ # @param [String] name the event name
23
+ # @param [**args] the args to emit
24
+ def notify(name, *args, **kwargs)
25
+ listeners.each do |listener|
26
+ raise Hokusai::Error.new("No target `##{name}` on #{listener.class}") unless listener.respond_to?(name)
27
+
28
+ listener.public_send(name, *args, **kwargs)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,72 @@
1
+ module Hokusai
2
+ class Style
3
+ attr_reader :raw
4
+
5
+ def self.parse(template)
6
+ raw = LibHokusai.parse_style(template)
7
+ new(parse_styles(raw))
8
+ end
9
+
10
+ def self.parse_styles(raw)
11
+ styles = {}
12
+
13
+ until raw.null?
14
+ styles[raw[:name]] = parse_attributes(raw[:attributes])
15
+
16
+ raw = raw[:next]
17
+ end
18
+
19
+ styles
20
+ end
21
+
22
+ def self.parse_attributes(raw)
23
+ attributes = {}
24
+
25
+ until raw.null?
26
+ case raw[:type]
27
+ when :style_int
28
+ value = raw[:value].to_i
29
+ when :style_bool
30
+ value = (raw[:value] == "true")
31
+ when :style_float
32
+ value = raw[:value].to_f
33
+ when :style_string
34
+ value = raw[:value]
35
+ when :style_func
36
+ value = parse_function(raw[:function_name], raw[:value])
37
+ end
38
+
39
+ attributes[raw[:name]] = value
40
+
41
+ raw = raw[:next]
42
+ end
43
+
44
+ attributes
45
+ end
46
+
47
+ def self.parse_function(name, value)
48
+ case name
49
+ when "rgb"
50
+ Color.convert(value)
51
+ when "outline"
52
+ Outline.convert(value)
53
+ when "padding"
54
+ Padding.convert(value)
55
+ else
56
+ raise Hokusai::Error.new("Unknown style function #{name}")
57
+ end
58
+ end
59
+
60
+ def initialize(elements)
61
+ @elements = elements
62
+ end
63
+
64
+ def [](element_name)
65
+ @elements[element_name]
66
+ end
67
+
68
+ def keys
69
+ @elements.keys
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hokusai
4
+ # Rect = Struct.new(:x, :y, :width, :height) do
5
+ class Rect
6
+ attr_accessor :x, :y, :width, :height
7
+
8
+ def initialize(x, y, width, height)
9
+ @x = x
10
+ @y = y
11
+ @width = width
12
+ @height = height
13
+ end
14
+ def to_hoku_rect
15
+ @hoku_rect ||= LibHokusai::HmlRect.create(x, y, width, height)
16
+ end
17
+
18
+ def includes_y?(y)
19
+ LibHokusai.hoku_rect_includes_y(to_hoku_rect, y)
20
+ end
21
+
22
+ def includes_x?(x)
23
+ LibHokusai.hoku_rect_includes_x(to_hoku_rect, x)
24
+ end
25
+
26
+ def move_x_left(times = 1)
27
+ LibHokusai.hoku_rect_x_left(to_hoku_rect, times)
28
+ end
29
+
30
+ def move_x_right(times = 1)
31
+ LibHokusai.hoku_rect_x_right(to_hoku_rect, times)
32
+ end
33
+
34
+ def move_y_up(times = 1)
35
+ LibHokusai.hoku_rect_y_up(to_hoku_rect, times)
36
+ end
37
+
38
+ def move_y_down(times = 1)
39
+ LibHokusai.hoku_rect_y_down(to_hoku_rect, times)
40
+ end
41
+
42
+ def self.from_hoku_rect(rect)
43
+ self.x = rect[:x]
44
+ self.y = rect[:y]
45
+ self.width = rect[:w]
46
+ self.height = rect[:h]
47
+ end
48
+ end
49
+
50
+ Outline = Struct.new(:top, :right, :bottom, :left) do
51
+ def self.default
52
+ new(0.0, 0.0, 0.0, 0.0)
53
+ end
54
+
55
+ def hash
56
+ [self.class, top, right, bottom, left].hash
57
+ end
58
+
59
+ def self.convert(value)
60
+ case value
61
+ when String
62
+ if value =~ /,/
63
+ convert(value.split(",").map(&:to_f))
64
+ else
65
+ convert(value.to_f)
66
+ end
67
+ when Float
68
+ new(value, value, value, value)
69
+ when Array
70
+ new(value[0] || 0.0, value[1] || 0.0, value[2] || 0.0, value[3] || 0.0)
71
+ when Outline
72
+ value
73
+ end
74
+ end
75
+
76
+ def present?
77
+ top > 0.0 || right > 0.0 || bottom > 0.0 || left > 0.0
78
+ end
79
+
80
+ def uniform?
81
+ top == right && top == bottom && top == left
82
+ end
83
+ end
84
+
85
+ class Padding
86
+ attr_reader :top, :left, :right, :bottom
87
+ def initialize(top, right, bottom, left)
88
+ @top = top
89
+ @left = left
90
+ @right = right
91
+ @bottom = bottom
92
+ end
93
+
94
+ alias_method :t, :top
95
+ alias_method :l, :left
96
+ alias_method :r, :right
97
+ alias_method :b, :bottom
98
+
99
+ def self.convert(value)
100
+ case value
101
+ when String
102
+ if value =~ /,/
103
+ convert(value.split(",").map(&:to_f))
104
+ else
105
+ convert(value.to_i)
106
+ end
107
+ when Integer
108
+ new(value, value, value, value)
109
+ when Array
110
+ new(value[0], value[1], value[2], value[3])
111
+ when Padding
112
+ value
113
+ else
114
+ raise Hokusai::Error.new("Unsupported conversion type #{value.class} for Hokusai::Padding")
115
+ end
116
+ end
117
+
118
+ def hash
119
+ [self.class, top, right, bottom, left].hash
120
+ end
121
+ end
122
+
123
+ class Canvas
124
+ attr_accessor :width, :height, :x, :y, :vertical, :reverse
125
+ def initialize(width, height, x = 0.0, y = 0.0, vertical = true, reverse = false)
126
+ @width = width
127
+ @height = height
128
+ @x = x
129
+ @y = y
130
+ @vertical = vertical
131
+ @reverse = reverse
132
+ end
133
+
134
+ def reset(x, y, width, height, vertical: true, reverse: false)
135
+ self.x = x
136
+ self.y = y
137
+ self.width = width
138
+ self.height = height
139
+ self.vertical = vertical
140
+ self.reverse = reverse
141
+ end
142
+
143
+ def to_bounds
144
+ Hokusai::Rect.new(x, y, width, height)
145
+ end
146
+
147
+ def reverse?
148
+ reverse
149
+ end
150
+
151
+ def to_hoku_rect
152
+ LibHokusai::HmlRect.create(x, y, width, height)
153
+ end
154
+ end
155
+
156
+ # Color = Struct.new(:red, :green, :blue, :alpha) do
157
+ class Color
158
+ attr_reader :red, :green, :blue, :alpha
159
+ def initialize(red, green, blue, alpha = 255)
160
+ @red = red.freeze
161
+ @green = green.freeze
162
+ @blue = blue.freeze
163
+ @alpha = alpha.freeze
164
+ end
165
+
166
+ alias_method :r, :red
167
+ alias_method :b, :blue
168
+ alias_method :g, :green
169
+ alias_method :a, :alpha
170
+
171
+ def self.convert(value)
172
+ case value
173
+ when String
174
+ value = value.split(",").map(&:to_i)
175
+ when Array
176
+ when Color
177
+ return value
178
+ else
179
+ raise Hokusai::Error.new("Unsupported conversion type #{value.class} for Hokusai::Color")
180
+ end
181
+
182
+ new(value[0], value[1], value[2], value[3] || 255)
183
+ end
184
+
185
+ def hash
186
+ [self.class, r, g, b, a].hash
187
+ end
188
+ end
189
+
190
+ class Keyboard
191
+ attr_reader :raw
192
+
193
+ [
194
+ :shift, :super, :ctrl,
195
+ :alt, :pressed, :pressed_len, :released,
196
+ :released_len
197
+ ].each do |name|
198
+ define_method(name) do
199
+ raw[name]
200
+ end
201
+ end
202
+
203
+ def keyboard_key
204
+ pressed.read_array_of_type(TYPE_UINT8, :read_uint8, pressed_len).first
205
+ end
206
+
207
+ def key
208
+ keyboard_key[:key][:key]
209
+ end
210
+
211
+ def initialize(raw)
212
+ @raw = raw
213
+ end
214
+ end
215
+
216
+ class Mouse
217
+ attr_reader :raw
218
+
219
+ def initialize(raw)
220
+ @raw = raw
221
+ end
222
+
223
+ [
224
+ :pos, :delta, :scroll,
225
+ :scroll_delta, :selection,
226
+ :selection_type, :left,
227
+ :middle, :right
228
+ ].each do |name|
229
+ define_method(name) do
230
+ # instance_variable_set("@#{name}", raw[name]) if instance_variable_get("@#{name}").nil?
231
+ #
232
+ # instance_variable_get("@#{name}")
233
+
234
+ raw[name]
235
+ end
236
+
237
+ define_method("#{name}=") do |val|
238
+ raw[name] = val
239
+ end
240
+ end
241
+ end
242
+
243
+ class Input
244
+ attr_reader :raw
245
+
246
+ def hash
247
+ [self.class, mouse.pos.x, mouse.pos.y, mouse.scroll, mouse.left.clicked, mouse.left.down, mouse.left.up].hash
248
+ end
249
+
250
+ def initialize(raw)
251
+ @raw = raw
252
+ end
253
+
254
+ def keyboard
255
+ Keyboard.new(@raw[:keyboard])
256
+ end
257
+
258
+ def mouse
259
+ @mouse ||= Mouse.new(@raw[:mouse])
260
+ end
261
+
262
+ def hovered?(canvas)
263
+ LibHokusai.hoku_input_is_hovered(raw, canvas.to_hoku_rect)
264
+ end
265
+ end
266
+ end