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,158 @@
1
+ module Hokusai
2
+ class Commands::Rect < Commands::Base
3
+ attr_reader :x, :y, :width, :height,
4
+ :rounding, :color, :outline,
5
+ :outline_color, :padding
6
+
7
+ def initialize(x, y, width, height)
8
+ @x = x.to_f
9
+ @y = y.to_f
10
+ @width = width.to_f
11
+ @height = height.to_f
12
+ @outline = Outline.default
13
+ @rounding = 0.0
14
+ @color = Color.new(0, 0, 0, 0)
15
+ @outline_color = Color.new(0, 0, 0, 255)
16
+ @padding = Padding.new(0.0, 0.0, 0.0, 0.0)
17
+ end
18
+
19
+ def hash
20
+ [self.class, x, y, width, height, rounding, color.hash, outline.hash, outline_color.hash, padding.hash].hash
21
+ end
22
+
23
+ # Modifies the parameter *Canvas*
24
+ # to offset the boundary with
25
+ # this rectangle's computed geometry
26
+ def trim_canvas(canvas)
27
+ x, y, w, h = background_boundary
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)
33
+
34
+ canvas
35
+ end
36
+
37
+ # Shorthand for #width
38
+ def w
39
+ width
40
+ end
41
+
42
+ # Shorthand for #height
43
+ def h
44
+ height
45
+ end
46
+
47
+ # Sets padding for the rectangle
48
+ # `value` is an array with padding declarations
49
+ # at [top, right, bottom, left]
50
+ def padding=(value)
51
+ case value
52
+ when Padding
53
+ @padding = value
54
+ else
55
+ @padding = Padding.convert(value)
56
+ end
57
+
58
+ self
59
+ end
60
+
61
+ # Sets an outline at `weight`
62
+ def outline=(outline)
63
+ @outline = outline
64
+
65
+ self
66
+ end
67
+
68
+ # Sets the outline color to `value`
69
+ def outline_color=(value)
70
+ case value
71
+ when Color
72
+ @outline_color = value
73
+ when Array
74
+ @outline_color = Color.new(value[0], value[1], value[2], value[3] || 255)
75
+ else
76
+ raise "Basd color"
77
+ end
78
+
79
+ self
80
+ end
81
+
82
+
83
+ # Sets the color of the rectangle
84
+ # from an array of rgba values
85
+ def color=(value)
86
+ case value
87
+ when Color
88
+ @color = value
89
+ when Array
90
+ @color = Color.new(value[0], value[1], value[2], value[3] || 255)
91
+ end
92
+
93
+ self
94
+ end
95
+
96
+ # Rounding amount for this rect
97
+ def round=(amount)
98
+ @rounding = amount
99
+
100
+ self
101
+ end
102
+
103
+ # Returns true if the rectangle has any padding
104
+ def padding?
105
+ [padding.t, padding.r, padding.b, padding.l].any? do |p|
106
+ p != 0.0
107
+ end
108
+ end
109
+
110
+ # Returns a tuple with the
111
+ # geometric boundary for this rectangle
112
+ def boundary
113
+ [x, y, width, height]
114
+ end
115
+
116
+ # Returns a tuple with the
117
+ # computed geometric **inner** boundary for this rectangle
118
+ # with outlines subtracted
119
+ def background_boundary
120
+ nx = x.dup
121
+ ny = y.dup
122
+ nw = width.dup
123
+ nh = height.dup
124
+
125
+ if outline.top > 0.0
126
+ ny += outline.top
127
+ nh -= outline.top
128
+ end
129
+
130
+ if outline.left > 0.0
131
+ nx += outline.left
132
+ nw -= outline.left
133
+ end
134
+
135
+ if outline.bottom > 0.0
136
+ nh -= outline.bottom
137
+ end
138
+
139
+ if outline.right > 0.0
140
+ nw -= outline.right
141
+ end
142
+
143
+ [nx, ny, nw, nh]
144
+ end
145
+
146
+ # Returns true if this rectangle
147
+ # has an outline
148
+ def outline?
149
+ outline.present?
150
+ end
151
+
152
+ # Returns true if this rectangle's
153
+ # outline is uniform
154
+ def outline_uniform?
155
+ outline.uniform?
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,22 @@
1
+ module Hokusai
2
+ class Commands::ScissorBegin < Commands::Base
3
+ attr_reader :x, :y, :width, :height
4
+
5
+ def initialize(x, y, width, height)
6
+ @x = x
7
+ @y = y
8
+ @width = width
9
+ @height = height
10
+ end
11
+
12
+ def hash
13
+ [self.class, x, y, width, height].hash
14
+ end
15
+ end
16
+
17
+ class Commands::ScissorEnd < Commands::Base;
18
+ def hash
19
+ [self.class].hash
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,92 @@
1
+ module Hokusai
2
+ class Commands::Text < Commands::Base
3
+ attr_reader :x, :y, :size, :color,
4
+ :padding, :wrap, :content,
5
+ :font, :static, :line_height,
6
+ :bold, :italic
7
+
8
+ def initialize(content, x, y)
9
+ @content = content
10
+ @x = x.to_f
11
+ @y = y.to_f
12
+ @color = Color.new(0, 0, 0, 255)
13
+ @padding = Padding.new(0.0, 0.0, 0.0, 0.0)
14
+ @size = 17
15
+ @wrap = false
16
+ @font = nil
17
+ @static = true
18
+ @bold = false
19
+ @italic = false
20
+ @line_height = 0.0
21
+ end
22
+
23
+ def hash
24
+ [self.class, content, color.hash, padding.hash, size, font, wrap].hash
25
+ end
26
+
27
+ def bold=(value)
28
+ @bold = value
29
+ end
30
+
31
+ def italic=(value)
32
+ @italic = value
33
+ end
34
+
35
+ def static=(value)
36
+ @static = !!value
37
+ end
38
+
39
+ def line_height=(value)
40
+ @line_height = value
41
+ end
42
+
43
+ def dynamic=(value)
44
+ @static = !value
45
+ end
46
+
47
+ def font=(value)
48
+ @font = value
49
+ end
50
+
51
+ def content=(value)
52
+ @content = value
53
+ end
54
+
55
+ def size=(height)
56
+ @size = height.to_f
57
+ end
58
+
59
+ # Sets padding for the text
60
+ # `value` is an array with padding declarations
61
+ # at [top, right, bottom, left]
62
+ def padding=(value)
63
+ case value
64
+ when Array
65
+ @padding = Padding.new(value[0], value[1], value[2], value[3])
66
+ when Integer
67
+ @padding = Padding.new(value, value, value, value)
68
+ when Padding
69
+ @padding = value
70
+ end
71
+
72
+ self
73
+ end
74
+
75
+ # Sets the color of the text
76
+ # from an array of rgba values
77
+ def color=(value)
78
+ case value
79
+ when Color
80
+ @color = value
81
+ when Array
82
+ @color = Color.new(value[0], value[1], value[2], value[3] || 255)
83
+ end
84
+ end
85
+
86
+ def padding?
87
+ [padding.t, padding.r, padding.b, padding.l].any? do |p|
88
+ p != 0.0
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,87 @@
1
+ require_relative "./commands/base"
2
+ require_relative "./commands/circle"
3
+ require_relative "./commands/image"
4
+ require_relative "./commands/rect"
5
+ require_relative "./commands/scissor"
6
+ require_relative "./commands/text"
7
+
8
+ module Hokusai
9
+ class Commands
10
+ attr_reader :queue
11
+
12
+ def initialize
13
+ # @queue = []
14
+ end
15
+
16
+ # def hash
17
+ # [self.class, *@queue.map(&:hash)].hash
18
+ # end
19
+
20
+ def rect(x, y, w, h)
21
+ command = Commands::Rect.new(x, y, w, h)
22
+ yield(command)
23
+
24
+ command.draw
25
+ # add(command)
26
+ end
27
+
28
+ def circle(x, y, radius)
29
+ command = Commands::Circle.new(x, y, radius)
30
+ yield(command)
31
+
32
+ command.draw
33
+ # add(command)
34
+ end
35
+
36
+ def svg(source, x, y, w, h)
37
+ command = Commands::SVG.new(source, x, y, w, h)
38
+ yield(command)
39
+
40
+ # add(command)
41
+ end
42
+
43
+ # Invokes an image command
44
+ # from a filename, at position {x,y} with `w`x`h` dimensions
45
+ def image(source, x, y, w, h)
46
+ Commands::Image.new(source, x, y, w, h).draw
47
+ # add(Commands::Image.new(source, x, y, w, h))
48
+ end
49
+
50
+ # Invokes a scissor begin command
51
+ # at position {x,y} with `w`x`h` dimensions
52
+ def scissor_begin(x, y, w, h)
53
+ Commands::ScissorBegin.new(x, y, w, h).draw
54
+ # add(Commands::ScissorBegin.new(x, y, w, h))
55
+ end
56
+
57
+ # Invokes a scissor stop command
58
+ def scissor_end
59
+ Commands::ScissorEnd.new.draw
60
+ # add(Commands::ScissorEnd.new)
61
+ end
62
+
63
+ def text(content, x, y)
64
+ command = Commands::Text.new(content, x, y)
65
+ yield command
66
+
67
+ command.draw
68
+ # add(command)
69
+ end
70
+
71
+ def each
72
+ queue.each do |cmd|
73
+ yield(cmd)
74
+ end
75
+ end
76
+
77
+ def clear!
78
+ # @queue = []
79
+ end
80
+
81
+ def add(command)
82
+ # @queue << command
83
+
84
+ self
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,124 @@
1
+ module Hokusai
2
+ class MovePatch
3
+ attr_accessor :from, :to, :delete
4
+
5
+ def initialize(from:, to:, delete: false)
6
+ @from = from
7
+ @to = to
8
+ @delete = delete
9
+ end
10
+ end
11
+
12
+ class InsertPatch
13
+ attr_accessor :target, :value, :delete
14
+
15
+ def initialize(target:, value:, delete: false)
16
+ @target = target
17
+ @value = value
18
+ @delete = delete
19
+ end
20
+ end
21
+
22
+ class UpdatePatch
23
+ attr_accessor :target, :value
24
+
25
+ def initialize(target:, value:)
26
+ @target = target
27
+ @value = value
28
+ end
29
+ end
30
+
31
+ class DeletePatch
32
+ attr_accessor :target
33
+
34
+ def initialize(target)
35
+ @target = target
36
+ end
37
+ end
38
+
39
+ class Diff
40
+ attr_reader :before, :after, :insertions
41
+
42
+ def initialize(before, after)
43
+ @before = before
44
+ @after = after
45
+ @insertions = {}
46
+ end
47
+
48
+ def map(list)
49
+ memo = {}
50
+ list.each_with_index do |(key, value), index|
51
+ memo[key] = { value: value, index: index }
52
+ end
53
+
54
+ memo
55
+ end
56
+
57
+ def patch
58
+ i = 0
59
+ deletions = 0
60
+ mapbefore = map(before)
61
+ mapafter = map(after)
62
+
63
+ while i < after.size
64
+ # left right
65
+ # [d, a, c] [(c), e, a, b]
66
+ #
67
+ # 1. [c, a] [c, (e), a]
68
+ #
69
+ # 2. [c, e, a] [c, e, b, (a),]
70
+ #
71
+ # 3. [c, e, b, a]
72
+ #
73
+ # is value (c) in left?
74
+ # yes ->
75
+ # is left[0] (a) in right?
76
+ # yes -> move c to 0, move a to 2
77
+ # no -> delete a, move c to 0
78
+ #
79
+ akey, value = after[i] # b
80
+ ckey, current = before[i] || nil # a
81
+
82
+ if bi = mapbefore.delete(akey) # 2
83
+ if bi[:index] != i # true (2 != 0)
84
+ if mapafter[ckey] # true
85
+ # move a to 2
86
+ before[bi[:index]] = [ckey, current] # before[2] = a
87
+ # update index
88
+ mapbefore[ckey] = { index: bi, value: current }
89
+
90
+ # move c to 0
91
+ yield MovePatch.new(from: bi, to: i)
92
+ else
93
+ yield MovePatch.new(from: bi, to: i, delete: true)
94
+ mapbefore[ckey] = nil
95
+ deletions += 1
96
+ # next
97
+ end
98
+ elsif value != current
99
+ yield UpdatePatch.new(target: i, value: value)
100
+ end
101
+ else # insert logic
102
+ if mapafter[ckey]
103
+ before[i + 1] = [ckey, current]
104
+ mapbefore[ckey] = { index: i + 1, value: current }
105
+
106
+ yield InsertPatch.new(target: i, value: value)
107
+ else
108
+ yield InsertPatch.new(target: i, value: value, delete: true)
109
+ mapbefore[ckey] = nil
110
+
111
+ end
112
+ end
113
+
114
+ i += 1
115
+ end
116
+
117
+ mapbefore.values.each do |value|
118
+ next if value.nil?
119
+
120
+ yield DeletePatch.new(value[:index]) unless value[:index].nil?
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,3 @@
1
+ module Hokusai
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hokusai
4
+ class Event
5
+ attr_reader :captures, :bubbles
6
+ attr_accessor :stopped
7
+
8
+ def self.name(name)
9
+ @name = name
10
+ end
11
+
12
+ def name
13
+ self.class.instance_variable_get("@name")
14
+ end
15
+
16
+ def stopped
17
+ @stopped ||= false
18
+ end
19
+
20
+ def stop
21
+ self.stopped = true
22
+ end
23
+
24
+ def captures
25
+ @captures ||= []
26
+ end
27
+
28
+ def bubbles
29
+ @bubbles ||= []
30
+ end
31
+
32
+ def to_json
33
+ raise Hokusai::Error.new("#{self.class} must implement to_json")
34
+ end
35
+
36
+ def matches(block)
37
+ return false if block.node.portal.nil?
38
+
39
+ val = block.node.portal.ast.event(name)
40
+ !!val
41
+ end
42
+
43
+ def bubble
44
+ while block = captures.pop
45
+ block.emit(name, self)
46
+
47
+ break if stopped
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ require_relative './events/keyboard'
54
+ require_relative './events/mouse'
@@ -0,0 +1,84 @@
1
+ module Hokusai
2
+ class KeyboardEvent < Event
3
+ extend Forwardable
4
+
5
+ def_delegators :@keyboard, :shift, :super, :ctrl,
6
+ :alt, :pressed, :pressed_len, :released,
7
+ :released_len
8
+
9
+ attr_reader :input
10
+
11
+ def initialize(input, state)
12
+ @input = input
13
+ @state = state
14
+ @keyboard = input.keyboard
15
+ end
16
+
17
+ def hovered(canvas)
18
+ input.hovered?(canvas)
19
+ end
20
+
21
+ def to_json
22
+ {
23
+ keypress: {
24
+ keycode: code,
25
+ char: char.to_s,
26
+ super: self.super,
27
+ control: ctrl,
28
+ shift: shift,
29
+ alt: alt
30
+ }
31
+ }.to_json
32
+ end
33
+ end
34
+
35
+ class KeyUpEvent < KeyboardEvent
36
+ name "keyup"
37
+
38
+ def keyboard_key
39
+ input.raw[:keyboard][:released]
40
+ end
41
+
42
+ def key
43
+ keyboard_key[:key][:key]
44
+ end
45
+
46
+ def code
47
+ keyboard_key[:char_code]
48
+ end
49
+
50
+ def char
51
+ code.chr
52
+ end
53
+
54
+ def capture(block, _)
55
+ captures << block if matches(block) && released_len > 0
56
+ end
57
+ end
58
+
59
+ class KeyPressEvent < KeyboardEvent
60
+ name "keypress"
61
+
62
+ def keyboard_key
63
+ input.raw[:keyboard][:pressed]
64
+ end
65
+
66
+ def key
67
+ keyboard_key[:key][:key]
68
+ end
69
+
70
+ def code
71
+ keyboard_key[:char_code]
72
+ end
73
+
74
+ def char
75
+ code.chr
76
+ end
77
+
78
+ def capture(block, _)
79
+ return unless matches(block) && pressed_len > 0
80
+
81
+ captures << block
82
+ end
83
+ end
84
+ end