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,200 @@
1
+ require_relative "../util/piece_table"
2
+
3
+ class Hokusai::Blocks::Input < Hokusai::Block
4
+ template <<~EOF
5
+ [template]
6
+ text {
7
+ @select_offset="handle_select_offset"
8
+ @selected="handle_selected"
9
+ @keypress="dynamic_keypress_handle"
10
+ @height_updated="update_height"
11
+ :content="text_content"
12
+ :size="size"
13
+ :padding="padding"
14
+ :cursor_offset="cursor_offset"
15
+ :color="text_color"
16
+ :selection_color="text_selection_color"
17
+ }
18
+ EOF
19
+
20
+ uses(
21
+ text: Hokusai::Blocks::Text,
22
+ )
23
+
24
+ computed :initial, default: "", convert: proc(&:to_s)
25
+ computed :text_color, default: [33,33,33], convert: Hokusai::Color
26
+ computed :text_selection_color, default: [233,233,233], convert: Hokusai::Color
27
+ computed :cursor_color, default: [244,22,22], convert: Hokusai::Color
28
+ computed :growable, default: false
29
+ computed :size, default: 14, convert: proc(&:to_i)
30
+ computed :padding, default: nil, convert: Hokusai::Padding
31
+ computed :background, default: nil, convert: Hokusai::Color
32
+ computed :rounding, default: 0.0, convert: proc(&:to_f)
33
+ computed :outline, default: [0,0,0,0], convert: Hokusai::Outline
34
+ computed :outline_color, default: nil, convert: Hokusai::Color
35
+
36
+ attr_accessor :table, :buffer, :buffer_count,
37
+ :start_time, :buffer_offset, :offset
38
+
39
+ def initialize(**args)
40
+ super
41
+
42
+ @table = ::Hokusai::Util::PieceTable.new(initial)
43
+ @buffer = ""
44
+ @buffer_count = 0
45
+ @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
46
+ @buffer_offset = 0
47
+ @offset = 0
48
+ end
49
+
50
+ def update_height(value)
51
+ node.meta.props[:height] = value
52
+
53
+ emit("height_updated", value)
54
+ end
55
+
56
+ def handle_selected(start, stop)
57
+ flush
58
+
59
+ return if start.nil?
60
+
61
+ self.offset = start
62
+ self.buffer_count = 0
63
+
64
+ return if stop.nil?
65
+
66
+ self.buffer_count = (stop + 1) - start
67
+ end
68
+
69
+ def handle_select_offset(value)
70
+ flush
71
+
72
+ self.offset = value
73
+ self.buffer_count = 0
74
+ end
75
+
76
+ def dynamic_keypress_handle(event)
77
+ case event
78
+ when proc(&:ctrl), proc(&:super)
79
+ if event.char == "z"
80
+ table.undo
81
+ end
82
+
83
+ return
84
+ else
85
+ case event.key
86
+ when :right
87
+ flush
88
+
89
+ self.offset += 1 unless offset + 1 > table.to_s.size
90
+ when :left
91
+ flush
92
+
93
+ self.offset = offset - 1 <= 0 ? 0 : offset - 1
94
+ when :down, :up
95
+ # no op
96
+ when :delete, :backspace
97
+ flush
98
+
99
+ if buffer_count > 0
100
+ table.delete(offset, buffer_count)
101
+ elsif offset > 0
102
+ table.delete(offset - 1, 1)
103
+
104
+ self.offset -= 1
105
+ end
106
+
107
+ self.buffer_count = 0
108
+
109
+ return
110
+ when :enter
111
+ if buffer.size.zero?
112
+ self.buffer_offset = offset
113
+ end
114
+
115
+ unless growable
116
+ flush
117
+
118
+ emit("change", table.to_s)
119
+
120
+ self.table = ::Hokusai::Util::PieceTable.new("")
121
+ self.buffer_offset = 0
122
+ self.offset = 0
123
+
124
+ return
125
+ end
126
+
127
+ self.buffer += "\n"
128
+ else
129
+ if buffer_count > 0
130
+ flush
131
+
132
+ table.delete(offset, buffer_count)
133
+
134
+ self.buffer_offset = offset
135
+ self.buffer_count = 0
136
+ end
137
+
138
+ if buffer.size.zero?
139
+ self.buffer_offset = offset
140
+ end
141
+
142
+ self.buffer += event.char
143
+ end
144
+ end
145
+ end
146
+
147
+ def flush
148
+ if buffer.size > 0
149
+ table.insert(buffer, buffer_offset)
150
+
151
+ self.offset = cursor_offset
152
+ self.buffer_offset = 0
153
+ self.buffer = ""
154
+ end
155
+ end
156
+
157
+ def cursor_offset
158
+ offset + buffer.size
159
+ end
160
+
161
+ def text_content
162
+ StringIO.open do |io|
163
+ saved = table.to_s
164
+
165
+ io.write saved[0...buffer_offset]
166
+ io.write buffer
167
+
168
+ if last = saved[buffer_offset..-1]
169
+ io.write last
170
+ end
171
+
172
+ io.string
173
+ end
174
+ end
175
+
176
+ def render(canvas)
177
+ if !buffer.empty? && (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) > 10
178
+ flush
179
+
180
+ if table.pieces.size > 10
181
+ self.table = Util::PieceTable.new(table.to_s)
182
+ end
183
+
184
+ self.start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
185
+ end
186
+
187
+ draw do
188
+ rect(canvas.x, canvas.y, canvas.width, canvas.height) do |command|
189
+ command.color = background if background
190
+ command.outline = outline if outline
191
+ command.outline_color = outline_color if outline_color
192
+ command.round = rounding if rounding
193
+
194
+ canvas = command.trim_canvas(canvas)
195
+ end
196
+ end
197
+
198
+ yield canvas
199
+ end
200
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ class Hokusai::Blocks::Label < Hokusai::Block
3
+ template <<~EOF
4
+ [template]
5
+ virtual
6
+ EOF
7
+
8
+ computed! :content
9
+ computed :size, default: 12
10
+ computed :color, default: [33,33,33], convert: Hokusai::Color
11
+ computed :padding, default: [5.0, 5.0, 5.0, 5.0], convert: Hokusai::Padding
12
+
13
+ def initialize(**args)
14
+ @content_width = 0.0
15
+ @content_height = 0.0
16
+ @updated = false
17
+ @last_content = nil
18
+
19
+ super(**args)
20
+ end
21
+
22
+ def render(canvas)
23
+ if @last_content != content
24
+ width, _ = Hokusai.fonts.active.measure(content.to_s, size.to_i)
25
+
26
+ emit("width_updated", width + padding.right + padding.left)
27
+
28
+ @last_content = content
29
+ end
30
+
31
+ draw do
32
+ text(content, canvas.x, canvas.y) do |command|
33
+ command.color = color
34
+ command.size = size
35
+ command.padding = padding
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,126 @@
1
+ class Hokusai::Blocks::Panel < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ hblock {
5
+ :background="background"
6
+ @wheel="wheel_handle"
7
+ }
8
+ clipped { :offset="offset" }
9
+ dynamic { @size_updated="set_size" }
10
+ slot
11
+ [if="scroll_active"]
12
+ scrollbar.scroller {
13
+ @scroll="scroll_complete"
14
+ :top="panel_top"
15
+ :goto="scrollbar_goto"
16
+ :width="scroll_width"
17
+ :background="scroll_background"
18
+ :control_color="scroll_color"
19
+ :control_height="scroll_control_height"
20
+ }
21
+ EOF
22
+
23
+ uses(
24
+ clipped: Hokusai::Blocks::Clipped,
25
+ dynamic: Hokusai::Blocks::Dynamic,
26
+ hblock: Hokusai::Blocks::Hblock,
27
+ scrollbar: Hokusai::Blocks::Scrollbar
28
+ )
29
+
30
+ computed :align, default: "top", convert: proc(&:to_s)
31
+ computed :scroll_goto, default: nil
32
+ computed :scroll_width, default: 14.0, convert: proc(&:to_f)
33
+ computed :scroll_background, default: nil, convert: Hokusai::Color
34
+ computed :scroll_color, default: nil, convert: Hokusai::Color
35
+ computed :background, default: nil, convert: Hokusai::Color
36
+
37
+ provide :panel_offset, :offset
38
+ provide :panel_content_height, :content_height
39
+ provide :panel_height, :panel_height
40
+ provide :panel_top, :panel_top
41
+
42
+ attr_accessor :top, :panel_height, :scroll_y, :scroll_percent,
43
+ :scroll_goto_y, :clipped_offset, :clipped_content_height
44
+
45
+ def initialize(**args)
46
+ @top = nil
47
+ @panel_height = 0.0
48
+ @scroll_y = 0.0
49
+ @scroll_percent = 0.0
50
+ @scroll_goto_y = nil
51
+ @clipped_offset = 0.0
52
+ @clipped_content_height = 0.0
53
+
54
+ super(**args)
55
+ end
56
+
57
+ def wheel_handle(event)
58
+ return if clipped_content_height <= panel_height
59
+
60
+ new_scroll_y = scroll_y + event.scroll * 20
61
+
62
+ if y = top
63
+ if new_scroll_y < y
64
+ self.scroll_goto_y = y
65
+ elsif new_scroll_y > panel_height
66
+ self.scroll_goto_y = panel_height
67
+ else
68
+ self.scroll_goto_y = new_scroll_y
69
+ end
70
+ end
71
+ end
72
+
73
+ def panel_top
74
+ top || 0.0
75
+ end
76
+
77
+ def set_size(_, height)
78
+ if panel_height != clipped_content_height || clipped_content_height.zero?
79
+ self.clipped_content_height = height
80
+ # self.scroll_goto_y = self.scroll_y unless scroll_y == top
81
+ end
82
+ end
83
+
84
+ def offset
85
+ ((panel_content_height * scroll_percent) - (panel_height * scroll_percent))
86
+ end
87
+
88
+ def content_height
89
+ clipped_content_height
90
+ end
91
+
92
+ def panel_content_height
93
+ clipped_content_height < panel_height ? panel_height : clipped_content_height
94
+ end
95
+
96
+ def scroll_active
97
+ clipped_content_height > panel_height
98
+ end
99
+
100
+ def scroll_complete(y, percent:)
101
+ self.scroll_y = y
102
+ self.scroll_percent = percent
103
+ self.scroll_goto_y = nil
104
+
105
+ # todo handle selection
106
+
107
+ emit("scroll", y, percent: percent)
108
+ end
109
+
110
+ def scrollbar_goto
111
+ scroll_goto_y || scroll_goto
112
+ end
113
+
114
+ def scroll_control_height
115
+ return 20.0 if panel_height <= 0.0
116
+
117
+ (panel_height / panel_content_height) * panel_height
118
+ end
119
+
120
+ def render(canvas)
121
+ self.top = canvas.y
122
+ self.panel_height = canvas.height
123
+
124
+ yield canvas
125
+ end
126
+ end
@@ -0,0 +1,24 @@
1
+ class Hokusai::Blocks::Rect < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ slot
5
+ EOF
6
+
7
+ computed :color, default: nil, convert: Hokusai::Color
8
+ computed :rounding, default: 0.0
9
+ computed :outline, default: nil, convert: Hokusai::Outline
10
+ computed :outline_color, default: nil, convert: Hokusai::Color
11
+
12
+ def render(canvas)
13
+ draw do
14
+ rect(canvas.x, canvas.y, canvas.width, canvas.height) do |command|
15
+ command.color = color unless color.nil?
16
+ command.outline = outline unless outline.nil?
17
+ command.outline_color = outline_color unless outline_color.nil?
18
+ command.round = rounding
19
+ end
20
+ end
21
+
22
+ yield canvas
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ class Hokusai::Blocks::ScissorBegin < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ slot
5
+ EOF
6
+
7
+ computed :offset, default: 0.0, convert: proc(&:to_f)
8
+
9
+ def render(canvas)
10
+ draw do
11
+ scissor_begin(canvas.x, canvas.y, canvas.width, canvas.height)
12
+ end
13
+
14
+ canvas.y -= offset
15
+
16
+ yield canvas
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ class Hokusai::Blocks::ScissorEnd < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ virtual
5
+ EOF
6
+
7
+ def render(canvas)
8
+ draw do
9
+ scissor_end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,103 @@
1
+ class Hokusai::Blocks::Scrollbar < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ vblock.scrollbar {
5
+ @mousedown="scroll_start"
6
+ @mousemove="scroll_handle"
7
+ :background="background"
8
+ }
9
+ rect.top {
10
+ :height="scroll_top_height"
11
+ }
12
+ empty
13
+ rect.control {
14
+ :color="control_color"
15
+ :height="control_height"
16
+ :rounding="control_rounding"
17
+ :outline="control_padding"
18
+ outline_color="0,0,0,0"
19
+ }
20
+ empty
21
+ rect.bottom
22
+ empty
23
+ EOF
24
+
25
+ uses(
26
+ vblock: Hokusai::Blocks::Vblock,
27
+ rect: Hokusai::Blocks::Rect,
28
+ empty: Hokusai::Blocks::Empty
29
+ )
30
+
31
+ computed :goto, default: nil
32
+ computed :background, default: [22,22,22], convert: Hokusai::Color
33
+ computed :control_color, default: [66,66,66], convert: Hokusai::Color
34
+ computed :control_height, default: 20.0, convert: proc(&:to_f)
35
+ computed :control_rounding, default: 0.75, convert: proc(&:to_f)
36
+ computed :control_padding, default: 2.0, convert: proc(&:to_f)
37
+
38
+ attr_accessor :scroll_y, :scrolling, :height, :offset
39
+
40
+ def scroll_start(event)
41
+ self.scrolling = true
42
+ do_goto(event.pos.y)
43
+
44
+ event.stop
45
+ end
46
+
47
+ def scroll_handle(event)
48
+ if event.left.down && scrolling
49
+ do_goto(event.pos.y)
50
+ else
51
+ self.scrolling = false
52
+ end
53
+
54
+ # event.stop
55
+ end
56
+
57
+ def scroll_top_height
58
+ start = scroll_y
59
+ control_middle = (control_height / 2)
60
+
61
+ if start <= offset + control_middle
62
+ return 0.0
63
+ elsif start >= offset + height - control_middle
64
+ return height - control_height
65
+ else
66
+ return scroll_y - offset - control_middle
67
+ end
68
+
69
+ 0.0
70
+ end
71
+
72
+ def after_updated
73
+ do_goto(goto) unless goto.nil?
74
+ end
75
+
76
+ def percent_scrolled
77
+ return 0 if scroll_top_height === 0
78
+
79
+ scroll_top_height / (height - control_height)
80
+ end
81
+
82
+ def do_goto(value)
83
+ self.scroll_y = value.to_f
84
+
85
+ emit("scroll", scroll_y, percent: percent_scrolled)
86
+ end
87
+
88
+ def initialize(**args)
89
+ @scroll_y = 0.0
90
+ @scrolling = false
91
+ @height = 0.0
92
+ @offset = 0.0
93
+
94
+ super(**args)
95
+ end
96
+
97
+ def render(canvas)
98
+ self.offset = canvas.y
99
+ self.height = canvas.height
100
+
101
+ yield(canvas)
102
+ end
103
+ end
@@ -0,0 +1,77 @@
1
+ require_relative "../util/selection"
2
+
3
+ module Hokusai::Blocks
4
+ class Selectable < Hokusai::Block
5
+ template <<~EOF
6
+ [template]
7
+ vblock {
8
+ @click="start_selection"
9
+ @hover="update_selection"
10
+ }
11
+ slot
12
+ cursor {
13
+ height="0"
14
+ :color="cursor_color"
15
+ :x="cursor_x"
16
+ :y="cursor_y"
17
+ :cursor_height="cursor_height"
18
+ :show="cursor_show"
19
+ }
20
+ EOF
21
+
22
+ uses(
23
+ vblock: Hokusai::Blocks::Vblock,
24
+ cursor: Hokusai::Blocks::Cursor
25
+ )
26
+
27
+ computed :cursor_color, default: [255,22,22], convert: Hokusai::Color
28
+
29
+ provide :selection, :selection
30
+
31
+ attr_reader :selection
32
+
33
+ def initialize(**args)
34
+ @selection = Hokusai::Util::Selection.new
35
+
36
+ super
37
+ end
38
+
39
+ def start_selection(event)
40
+ if event.left.down && !selection.active?
41
+ selection.start(event.pos.x, event.pos.y)
42
+ end
43
+ end
44
+
45
+ def update_selection(event)
46
+ return unless selection.active?
47
+
48
+ if event.left[:up]
49
+ selection.freeze!
50
+ elsif event.left[:down]
51
+ selection.stop(event.pos.x, event.pos.y)
52
+ end
53
+ end
54
+
55
+ def cursor_x
56
+ cursor(0)
57
+ end
58
+
59
+ def cursor_y
60
+ cursor(1)
61
+ end
62
+
63
+ def cursor_height
64
+ cursor(3)
65
+ end
66
+
67
+ def cursor_show
68
+ !selection.cursor.nil?
69
+ end
70
+
71
+ def cursor(index)
72
+ return if selection.cursor.nil?
73
+
74
+ selection.cursor[index]
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,20 @@
1
+ class Hokusai::Blocks::SVG < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ virtual
5
+ EOF
6
+
7
+ computed! :source
8
+ computed :size, default: 12, convert: proc(&:to_i)
9
+ computed :color, default: [255,255,255], convert: Hokusai::Color
10
+
11
+ def render(canvas)
12
+ draw do
13
+ svg(source, canvas.x, canvas.y, size, size) do |command|
14
+ command.color = color
15
+ end
16
+ end
17
+
18
+ yield canvas
19
+ end
20
+ end