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
+ require_relative "../util/clamping_iterator"
2
+
3
+ class Hokusai::Blocks::Text < Hokusai::Block
4
+ template <<~EOF
5
+ [template]
6
+ empty {
7
+ cursor="manual"
8
+ @hover="set_hover_position"
9
+ @mouseout="unset_hover_position"
10
+ @click="set_click_position"
11
+ @mouseup="clear_click_position"
12
+ }
13
+ EOF
14
+
15
+ uses(empty: Hokusai::Blocks::Empty)
16
+
17
+ computed! :content
18
+ computed :size, default: 16, convert: proc(&:to_i)
19
+ computed :line_height, default: 5, convert: proc(&:to_f)
20
+ computed :color, default: [33,33,33], convert: Hokusai::Color
21
+ computed :selection_color, default: [233,233,233], convert: Hokusai::Color
22
+ computed :padding, default: [5.0, 5.0, 5.0, 5.0], convert: Hokusai::Padding
23
+ computed :cursor_offset, default: nil
24
+ computed :markdown, default: false
25
+
26
+ inject :selection
27
+ inject :panel_top
28
+ inject :panel_height
29
+ inject :panel_offset
30
+
31
+ attr_accessor :last_content, :last_clamping, :last_width,
32
+ :last_height, :click_position, :last_cursor_offset,
33
+ :last_char_index, :hover_position
34
+
35
+ def set_hover_position(event)
36
+ self.hover_position = [event.pos.x, event.pos.y]
37
+ end
38
+
39
+ def unset_hover_position(event)
40
+ if !event.state.set
41
+ Hokusai.set_mouse_cursor(:default)
42
+
43
+ event.state.set = true
44
+ end
45
+
46
+ self.hover_position = nil
47
+ end
48
+
49
+ def set_click_position(event)
50
+ self.click_position = [event.pos.x, event.pos.y]
51
+ end
52
+
53
+ def clear_click_position(_)
54
+ self.click_position = nil
55
+ end
56
+
57
+ def internal_render(clamping, canvas)
58
+ iterator = Hokusai::Util::ClampingIterator.new(clamping, canvas.x + padding.left, canvas.y + padding.top)
59
+ boundary = panel_height || canvas.y + canvas.height
60
+ offset_y = panel_offset || 0.0
61
+
62
+ boundary += panel_top if panel_top
63
+ hovered = false
64
+ link_hovered = false
65
+
66
+ draw_with do |commands|
67
+ iterator.on_draw do |text, x, y, group|
68
+
69
+ if selector = selection
70
+ if cursor_offset&.zero? && !selector.started
71
+ selector.cursor = [x, y + offset_y, 0.0, size.to_f]
72
+
73
+ self.last_cursor_offset = cursor_offset
74
+ end
75
+ end
76
+
77
+ if hover_position
78
+ cx, cy = hover_position
79
+
80
+ if cx > x && cx <= x + group.width && cy > y && cy <= y + size && y > (panel_top || 0.0)
81
+ hovered = true
82
+ end
83
+ end
84
+
85
+
86
+ commands.text(text, x, y) do |command|
87
+ command.color = color
88
+ command.size = size
89
+
90
+ if group.respond_to?(:bold?)
91
+
92
+ unless group.normal?
93
+ command.bold = group.bold?
94
+ command.italic = group.italics?
95
+
96
+ if group.link? && hovered
97
+ cx, cy = hover_position
98
+
99
+ if cx > x && cx <= x + group.width && cy > y && cy <= y + size
100
+ link_hovered = true
101
+ end
102
+ end
103
+
104
+ if group.link? && click_position
105
+ cx, cy = click_position
106
+
107
+ if cx > x && cx <= x + group.width && cy > y && cy <= y + size && cy > (panel_top || 0)
108
+ # that's a click
109
+ case RbConfig::CONFIG['host_os']
110
+ when /darwin/
111
+ system("open #{group.link}")
112
+ when /linux/
113
+ system("xdg-open #{group.link}")
114
+ end
115
+
116
+ self.click_position = nil
117
+ end
118
+ end
119
+
120
+ command.color = Hokusai::Color.new(50, 110, 188) if group.link?
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ iterator.on_draw_selection do |x, y, width, height|
127
+ # commands.rect(x, y, width, height) do |command|
128
+ # command.color = selection_color
129
+ # end
130
+ end
131
+
132
+ iterator.on_selection_change do |start, stop|
133
+ emit("selected", start, stop)
134
+ end
135
+
136
+ iterator.on_cursor_change do |x, y, w, h|
137
+ selection.cursor = [x, y + offset_y, w, h] if selection
138
+ end
139
+
140
+ iterator.on_char do |char, hitbox, offset|
141
+ unless selection.nil?
142
+ if cursor_offset && offset == cursor_offset - 1 && (last_cursor_offset != cursor_offset || last_content != content)
143
+ selection.cursor = [hitbox.move_x_right(2), hitbox.y + offset_y, hitbox.width, hitbox.height]
144
+
145
+ self.last_cursor_offset = cursor_offset
146
+ end
147
+
148
+ unless click_position.nil?
149
+ click_x, click_y = click_position
150
+
151
+ rect = Hokusai::Rect.new(hitbox.move_x_right, hitbox.y, hitbox.width, hitbox.height)
152
+
153
+ if rect.includes_x?(click_x) && hitbox.includes_y?(click_y)
154
+ selection.cursor = [hitbox.move_x_right(2), hitbox.y + offset_y, hitbox.width, hitbox.height]
155
+
156
+ if offset + 1 != last_cursor_offset
157
+ emit("select_offset", offset + 1)
158
+ self.last_cursor_offset = offset + 1
159
+ end
160
+
161
+ self.click_position = nil
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ while segment = iterator.next
168
+ segment.draw(size, [canvas.y, boundary], selection: selection)
169
+ end
170
+ end
171
+
172
+ if link_hovered
173
+ Hokusai.set_mouse_cursor(:pointer)
174
+ elsif hovered
175
+ Hokusai.set_mouse_cursor(:ibeam)
176
+ elsif hover_position
177
+ Hokusai.set_mouse_cursor(:default)
178
+ end
179
+
180
+ iterator.height
181
+ end
182
+
183
+ def after_updated
184
+ node.meta.set_prop(:height, last_height)
185
+ end
186
+
187
+ def clamp(text, width)
188
+ if markdown
189
+ Hokusai.fonts.active.clamp_markdown(text, size.to_i, width - size.to_f - padding.right)
190
+ else
191
+ Hokusai.fonts.active.clamp(text, size.to_i, width - size.to_f, padding.right)
192
+ end
193
+ end
194
+
195
+ def render(canvas)
196
+ if Hokusai.can_render(canvas)
197
+ unless last_content == content && last_width == canvas.width
198
+ # self.last_clamping&.free
199
+ self.last_clamping = clamp(content, canvas.width - (padding.left + padding.right))
200
+ self.last_width = canvas.width
201
+ self.last_content = content
202
+ end
203
+
204
+ height = internal_render(last_clamping, canvas)
205
+ self.last_content = last_content
206
+ emit("height_updated", height + padding.bottom) unless last_height == height + padding.bottom
207
+
208
+ self.last_height = height + padding.bottom + padding.top
209
+
210
+
211
+ yield canvas
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,145 @@
1
+ require "time"
2
+ module Hokusai::Blocks::Titlebar
3
+ class OSX < Hokusai::Block
4
+ GREEN = [38, 200, 75]
5
+ YELLOW = [253, 189, 61]
6
+ RED = [255, 92, 87]
7
+ DEFAULT = [133, 133, 133]
8
+ DRAG = [46,49,63]
9
+
10
+ template <<-EOF
11
+ [template]
12
+ hblock {
13
+ :background="get_background"
14
+ :outline="outline"
15
+ :outline_color="outline_color"
16
+ :rounding="rounding"
17
+ @mousedown="handle_move_start"
18
+ @mousemove="handle_move"
19
+ @hover="set_hover"
20
+ @mouseout="clear_hover"
21
+ }
22
+ vblock { width="4" }
23
+ empty
24
+ vblock { width="60" }
25
+ hblock
26
+ circle { @click="close" @hover="hover_red" @mouseout="blur_red" :radius="radius" :color="red" }
27
+ circle { @click="minimize" @hover="hover_yellow" @mouseout="blur_yellow" :radius="radius" :color="yellow" }
28
+ circle { @click="maximize" @hover="hover_green" @mouseout="blur_green" :radius="radius" :color="green" }
29
+ vblock
30
+ hblock
31
+ slot
32
+ EOF
33
+
34
+ computed :rounding, default: 0.0, convert: proc(&:to_f)
35
+ computed :outline, default: [0.0, 0.0, 0.0, 0.0], convert: Hokusai::Outline
36
+ computed :outline_color, default: [0,0,0,0], convert: Hokusai::Color
37
+ computed :unhovered_color, default: DEFAULT, convert: Hokusai::Color
38
+ computed :radius, default: 6.0, convert: proc(&:to_f)
39
+ computed :background, default: nil, convert: Hokusai::Color
40
+ computed :background_drag, default: nil, convert: Hokusai::Color
41
+
42
+
43
+ uses(
44
+ circle: Hokusai::Blocks::Circle,
45
+ vblock: Hokusai::Blocks::Vblock,
46
+ hblock: Hokusai::Blocks::Hblock,
47
+ empty: Hokusai::Blocks::Empty
48
+ )
49
+
50
+ attr_accessor :moving, :last_event, :hovering, :maximized
51
+
52
+ def get_background
53
+ moving ? background_drag : background
54
+ end
55
+
56
+ def handle_move_start(event)
57
+ self.last_event = [event.pos.x, event.pos.y] unless moving
58
+ self.moving = true
59
+ end
60
+
61
+ def handle_move(event)
62
+ if moving && event.left.down
63
+ x = event.pos.x - last_event[0]
64
+ y = event.pos.y - last_event[1]
65
+ # pp ["set window", Time.now.strftime("%H:%M:%S %L")]
66
+ Hokusai.set_window_position([x, y])
67
+ else
68
+ self.moving = false
69
+ end
70
+ end
71
+
72
+ def set_hover(_)
73
+ self.hovering = true
74
+ end
75
+
76
+ def clear_hover(_)
77
+ self.hovering = false
78
+ end
79
+
80
+ def close(_)
81
+ Hokusai.close_window
82
+ end
83
+
84
+ def minimize(_)
85
+ Hokusai.minimize_window
86
+ end
87
+
88
+ def maximize(_)
89
+ if maximized
90
+ Hokusai.restore_window
91
+ self.maximized = false
92
+ else
93
+ Hokusai.maximize_window
94
+ self.maximized = true
95
+ end
96
+ end
97
+
98
+ def blur_red(_)
99
+ @hovered_red = false
100
+ end
101
+
102
+ def blur_yellow(_)
103
+ @hovered_yellow = false
104
+ end
105
+
106
+ def blur_green(_)
107
+ @hovered_green = false
108
+ end
109
+
110
+ def hover_red(_)
111
+ @hovered_red = true
112
+ end
113
+
114
+ def hover_yellow(_)
115
+ @hovered_yellow = true
116
+ end
117
+
118
+ def hover_green(_)
119
+ @hovered_green = true
120
+ end
121
+
122
+ def red
123
+ @hovered_red ? RED : unhovered_color
124
+ end
125
+
126
+ def yellow
127
+ @hovered_yellow ? YELLOW : unhovered_color
128
+ end
129
+
130
+ def green
131
+ @hovered_green ? GREEN : unhovered_color
132
+ end
133
+
134
+ def initialize(**args)
135
+ super(**args)
136
+ @hovered_red = false
137
+ @hovered_yellow = false
138
+ @hovered_green = false
139
+ @moving = false
140
+ @hovering = false
141
+ @last_event = nil
142
+ @maximized = false
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,55 @@
1
+ class Hokusai::Blocks::Toggle < Hokusai::Block
2
+ template <<-EOF
3
+ [template]
4
+ empty { @click="toggle" }
5
+ EOF
6
+
7
+ uses(empty: Hokusai::Blocks::Empty)
8
+
9
+ computed :size, default: 30.0, convert: proc(&:to_f)
10
+ computed :active_color, default: [137, 126, 186], convert: Hokusai::Color
11
+ computed :inactive_color, default: [61, 57, 81], convert: Hokusai::Color
12
+ computed :color, default: [215, 212, 226], convert: Hokusai::Color
13
+
14
+ attr_accessor :toggled
15
+
16
+ def toggle(_)
17
+ self.toggled = !toggled
18
+
19
+ emit("toggle", value: toggled)
20
+ end
21
+
22
+ def computed_color
23
+ toggled ? active_color : inactive_color
24
+ end
25
+
26
+ def initialize(**args)
27
+ @toggled = false
28
+
29
+ super(**args)
30
+ end
31
+
32
+ def render(canvas)
33
+ width = size * 2
34
+ radius = size / 2
35
+
36
+ start = toggled ? (canvas.x + width - radius) : canvas.x + radius
37
+
38
+ draw do
39
+ rect(canvas.x, canvas.y, width.to_f, size) do |command|
40
+ command.color = computed_color
41
+ command.round = size
42
+ command.padding = Hokusai::Padding.convert(20)
43
+ end
44
+
45
+ circle(start, canvas.y + radius, radius) do |command|
46
+ command.color = color
47
+ end
48
+ end
49
+
50
+ canvas.width = size * 2
51
+ canvas.height = size
52
+
53
+ yield(canvas)
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ class Hokusai::Blocks::Vblock < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ slot
5
+ EOF
6
+
7
+ computed :padding, default: [0, 0, 0, 0], convert: Hokusai::Padding
8
+ computed :background, default: nil, convert: Hokusai::Color
9
+ computed :rounding, default: 0.0
10
+ computed :outline, default: Hokusai::Outline.default, convert: Hokusai::Outline
11
+ computed :outline_color, default: nil, convert: Hokusai::Color
12
+ computed :reverse, default: false
13
+
14
+ def render(canvas)
15
+ canvas.vertical = true
16
+ canvas.reverse = reverse
17
+
18
+ if background.nil?
19
+ yield canvas
20
+ else
21
+ draw do
22
+ rect(canvas.x, canvas.y, canvas.width, canvas.height) do |command|
23
+ command.color = background
24
+ command.outline = outline if outline
25
+ command.outline_color = outline_color if outline_color
26
+ command.round = rounding.to_f if rounding
27
+ command.padding = padding
28
+ canvas = command.trim_canvas(canvas)
29
+ end
30
+ end
31
+
32
+ yield canvas
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hokusai::Commands
4
+ class Base
5
+ def self.on_draw(&block)
6
+ @draw = block
7
+ end
8
+
9
+ def self.draw
10
+ @draw
11
+ end
12
+
13
+ def draw
14
+ raise Hokusai::Error.new("No draw callback made for #{self.class}") if self.class.draw.nil?
15
+
16
+ self.class.draw.call(self.freeze)
17
+ end
18
+
19
+ def after_draw(canvas)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ module Hokusai
2
+ class Commands::Circle < Commands::Base
3
+ attr_reader :x, :y, :radius, :color, :outline_color,
4
+ :outline
5
+
6
+ def initialize(x, y, radius)
7
+ @x = x
8
+ @y = y
9
+ @radius = radius
10
+ @color = Color.new(255, 255, 255, 255)
11
+ @outline_color = Color.new(0, 0, 0, 0)
12
+ @outline = 0.0
13
+ end
14
+
15
+ def hash
16
+ [self.class, x, y, radius, color.hash, outline_color.hash, outline].hash
17
+ end
18
+
19
+ def outline=(weight)
20
+ @outline = weight
21
+
22
+ self
23
+ end
24
+
25
+ def color=(value)
26
+ case value
27
+ when Color
28
+ @color = value
29
+ when Array
30
+ @color = Color.new(value[0], value[1], value[2], value[3] || 255)
31
+ end
32
+
33
+ self
34
+ end
35
+
36
+ def outline_color=(value)
37
+ case value
38
+ when Color
39
+ @outline_color = value
40
+ when Array
41
+ @outline_color = Color.new(value[0], value[1], value[2], value[3] || 255)
42
+ end
43
+
44
+ self
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,45 @@
1
+ module Hokusai
2
+ class Commands::Image < Commands::Base
3
+ attr_reader :x, :y, :width, :height, :source
4
+
5
+ def initialize(source, x, y, width, height)
6
+ @source = source
7
+ @x = x
8
+ @y = y
9
+ @width = width
10
+ @height = height
11
+ end
12
+
13
+ def hash
14
+ [self.class, x, y, width, height, source].hash
15
+ end
16
+
17
+ def cache
18
+ [source, width, height].hash
19
+ end
20
+ end
21
+
22
+ class Commands::SVG < Commands::Base
23
+ attr_reader :x, :y, :width, :height, :source, :color
24
+
25
+ def initialize(source, x, y, width, height)
26
+ @source = source
27
+ @x = x
28
+ @y = y
29
+ @width = width
30
+ @height = height
31
+ @color = Color.new(255, 255, 255, 255)
32
+ end
33
+
34
+ def color=(value)
35
+ case value
36
+ when Color
37
+ @color = value
38
+ when Array
39
+ @color = Color.new(value[0], value[1], value[2], value[3] || 255)
40
+ end
41
+
42
+ self
43
+ end
44
+ end
45
+ end