hokusai-zero 0.1.2 → 0.1.4

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +4 -0
  4. data/README.md +4 -2
  5. data/ast/src/core/hml.c +9 -9
  6. data/ast/src/core/text.c +1 -3
  7. data/ast/test/text.c +3 -3
  8. data/docs.sh +29 -0
  9. data/ext/extconf.rb +50 -12
  10. data/grammar/corpus/1_document.txt +24 -0
  11. data/grammar/corpus/6_styles.txt +23 -0
  12. data/grammar/grammar.js +4 -4
  13. data/grammar/src/grammar.json +19 -19
  14. data/grammar/src/parser.c +1904 -1956
  15. data/grammar/test.nml +10 -8
  16. data/hokusai.gemspec +5 -1
  17. data/ui/examples/assets/Delius-Regular.ttf +0 -0
  18. data/ui/examples/assets/DoHyeon.ttf +0 -0
  19. data/ui/examples/assets/Inter-Regular.ttf +0 -0
  20. data/ui/examples/assets/ernest.gif +0 -0
  21. data/ui/examples/assets/icons/audio-x-generic.png +0 -0
  22. data/ui/examples/assets/icons/image-x-generic.png +0 -0
  23. data/ui/examples/assets/icons/media-playback-pause.png +0 -0
  24. data/ui/examples/assets/icons/media-playback-start.png +0 -0
  25. data/ui/examples/assets/icons/media-playback-stop.png +0 -0
  26. data/ui/examples/assets/icons/package-x-generic.png +0 -0
  27. data/ui/examples/assets/icons/text-x-generic.png +0 -0
  28. data/ui/examples/assets/icons/video-x-generic.png +0 -0
  29. data/ui/examples/buddy.rb +16 -14
  30. data/ui/examples/clock.rb +38 -36
  31. data/ui/examples/counter.rb +100 -98
  32. data/ui/examples/dynamic.rb +115 -113
  33. data/ui/examples/foobar.rb +189 -187
  34. data/ui/examples/forum/file.rb +54 -0
  35. data/ui/examples/forum/music.rb +76 -0
  36. data/ui/examples/forum/post.rb +146 -0
  37. data/ui/examples/forum.rb +198 -0
  38. data/ui/examples/spreadsheet/csv.rb +261 -0
  39. data/ui/examples/spreadsheet.rb +138 -0
  40. data/ui/examples/stock.rb +86 -92
  41. data/ui/examples/stock_decider/option.rb +1 -1
  42. data/ui/examples/tic_tac_toe.rb +193 -191
  43. data/ui/lib/lib_hokusai.rb +0 -1
  44. data/ui/spec/hokusai/automation/keys_transcoder_spec.rb +5 -5
  45. data/ui/spec/hokusai/e2e/client_spec.rb +1 -0
  46. data/ui/spec/hokusai/e2e/meta_spec.rb +36 -36
  47. data/ui/spec/spec_helper.rb +13 -6
  48. data/ui/src/hokusai/ast.rb +42 -43
  49. data/ui/src/hokusai/backends/raylib/font.rb +1 -2
  50. data/ui/src/hokusai/backends/raylib.rb +16 -7
  51. data/ui/src/hokusai/backends/sdl2/font.rb +13 -9
  52. data/ui/src/hokusai/backends/sdl2.rb +5 -5
  53. data/ui/src/hokusai/block.rb +7 -0
  54. data/ui/src/hokusai/blocks/hblock.rb +2 -2
  55. data/ui/src/hokusai/blocks/image.rb +5 -1
  56. data/ui/src/hokusai/blocks/input.rb +17 -0
  57. data/ui/src/hokusai/blocks/label.rb +5 -2
  58. data/ui/src/hokusai/blocks/text.rb +10 -5
  59. data/ui/src/hokusai/blocks/titlebar/osx.rb +4 -4
  60. data/ui/src/hokusai/blocks/variable.rb +33 -0
  61. data/ui/src/hokusai/blocks/vblock.rb +1 -1
  62. data/ui/src/hokusai/commands/rect.rb +4 -4
  63. data/ui/src/hokusai/commands.rb +33 -31
  64. data/ui/src/hokusai/diff.rb +11 -0
  65. data/ui/src/hokusai/event.rb +19 -5
  66. data/ui/src/hokusai/events/mouse.rb +9 -1
  67. data/ui/src/hokusai/font.rb +60 -0
  68. data/ui/src/hokusai/meta.rb +10 -16
  69. data/ui/src/hokusai/node.rb +1 -1
  70. data/ui/src/hokusai/painter.rb +1 -2
  71. data/ui/src/hokusai/util/clamping_iterator.rb +5 -6
  72. data/ui/src/hokusai.rb +36 -4
  73. data/xmake.lua +5 -4
  74. metadata +79 -3
@@ -45,6 +45,19 @@ class Hokusai::Blocks::Input < Hokusai::Block
45
45
  @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
46
46
  @buffer_offset = 0
47
47
  @offset = 0
48
+ @changed = false
49
+ end
50
+
51
+ def after_updated
52
+ if @changed && !growable
53
+ @table = ::Hokusai::Util::PieceTable.new(initial)
54
+ @buffer = ""
55
+ @buffer_count = 0
56
+ @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
57
+ @buffer_offset = 0
58
+ @offset = 0
59
+ @changed = false
60
+ end
48
61
  end
49
62
 
50
63
  def update_height(value)
@@ -74,6 +87,7 @@ class Hokusai::Blocks::Input < Hokusai::Block
74
87
  end
75
88
 
76
89
  def dynamic_keypress_handle(event)
90
+ return unless node.meta.focused
77
91
  case event
78
92
  when proc(&:ctrl), proc(&:super)
79
93
  if event.char == "z"
@@ -116,6 +130,7 @@ class Hokusai::Blocks::Input < Hokusai::Block
116
130
  flush
117
131
 
118
132
  emit("change", table.to_s)
133
+ @changed = true
119
134
 
120
135
  self.table = ::Hokusai::Util::PieceTable.new("")
121
136
  self.buffer_offset = 0
@@ -142,6 +157,8 @@ class Hokusai::Blocks::Input < Hokusai::Block
142
157
  self.buffer += event.char
143
158
  end
144
159
  end
160
+
161
+ emit("modified")
145
162
  end
146
163
 
147
164
  def flush
@@ -6,6 +6,7 @@ class Hokusai::Blocks::Label < Hokusai::Block
6
6
  EOF
7
7
 
8
8
  computed! :content
9
+ computed :font, default: nil
9
10
  computed :size, default: 12
10
11
  computed :color, default: [33,33,33], convert: Hokusai::Color
11
12
  computed :padding, default: [5.0, 5.0, 5.0, 5.0], convert: Hokusai::Padding
@@ -21,8 +22,9 @@ class Hokusai::Blocks::Label < Hokusai::Block
21
22
 
22
23
  def render(canvas)
23
24
  if @last_content != content
24
- width, _ = Hokusai.fonts.active.measure(content.to_s, size.to_i)
25
-
25
+ width, height = Hokusai.fonts.active.measure(content.to_s, size.to_i)
26
+ node.meta.set_prop(:width, width + padding.right + padding.left)
27
+ node.meta.set_prop(:height, height + padding.top + padding.bottom)
26
28
  emit("width_updated", width + padding.right + padding.left)
27
29
 
28
30
  @last_content = content
@@ -33,6 +35,7 @@ class Hokusai::Blocks::Label < Hokusai::Block
33
35
  command.color = color
34
36
  command.size = size
35
37
  command.padding = padding
38
+ command.font = font unless font.nil?
36
39
  end
37
40
  end
38
41
  end
@@ -15,13 +15,14 @@ class Hokusai::Blocks::Text < Hokusai::Block
15
15
  uses(empty: Hokusai::Blocks::Empty)
16
16
 
17
17
  computed! :content
18
+ computed :font, default: nil
18
19
  computed :size, default: 16, convert: proc(&:to_i)
19
20
  computed :line_height, default: 5, convert: proc(&:to_f)
20
21
  computed :color, default: [33,33,33], convert: Hokusai::Color
21
22
  computed :selection_color, default: [233,233,233], convert: Hokusai::Color
22
23
  computed :padding, default: [5.0, 5.0, 5.0, 5.0], convert: Hokusai::Padding
23
24
  computed :cursor_offset, default: nil
24
- computed :markdown, default: false
25
+ computed :markdown, default: false, convert: ->(val) { val == "true" || val == true }
25
26
 
26
27
  inject :selection
27
28
  inject :panel_top
@@ -65,7 +66,6 @@ class Hokusai::Blocks::Text < Hokusai::Block
65
66
 
66
67
  draw_with do |commands|
67
68
  iterator.on_draw do |text, x, y, group|
68
-
69
69
  if selector = selection
70
70
  if cursor_offset&.zero? && !selector.started
71
71
  selector.cursor = [x, y + offset_y, 0.0, size.to_f]
@@ -86,6 +86,7 @@ class Hokusai::Blocks::Text < Hokusai::Block
86
86
  commands.text(text, x, y) do |command|
87
87
  command.color = color
88
88
  command.size = size
89
+ command.font = font unless font.nil?
89
90
 
90
91
  if group.respond_to?(:bold?)
91
92
 
@@ -186,7 +187,8 @@ class Hokusai::Blocks::Text < Hokusai::Block
186
187
 
187
188
  def clamp(text, width)
188
189
  if markdown
189
- Hokusai.fonts.active.clamp_markdown(text, size.to_i, width - size.to_f - padding.right)
190
+ clamping = Hokusai.fonts.active.clamp_markdown(text, size.to_i, width - size.to_f - padding.right)
191
+ clamping
190
192
  else
191
193
  Hokusai.fonts.active.clamp(text, size.to_i, width - size.to_f, padding.right)
192
194
  end
@@ -203,10 +205,13 @@ class Hokusai::Blocks::Text < Hokusai::Block
203
205
 
204
206
  height = internal_render(last_clamping, canvas)
205
207
  self.last_content = last_content
206
- emit("height_updated", height + padding.bottom) unless last_height == height + padding.bottom
208
+
209
+ new_height = height + padding.bottom + padding.top
210
+ new_height -= size if markdown
207
211
 
208
- self.last_height = height + padding.bottom + padding.top
212
+ emit("height_updated", new_height) unless last_height == new_height
209
213
 
214
+ self.last_height = new_height
210
215
 
211
216
  yield canvas
212
217
  end
@@ -32,12 +32,12 @@ module Hokusai::Blocks::Titlebar
32
32
  EOF
33
33
 
34
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
35
+ computed :outline, default: nil
36
+ computed :outline_color, default: nil
37
37
  computed :unhovered_color, default: DEFAULT, convert: Hokusai::Color
38
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
39
+ computed :background, default: nil
40
+ computed :background_drag, default: nil
41
41
 
42
42
 
43
43
  uses(
@@ -0,0 +1,33 @@
1
+ class Hokusai::Blocks::Variable < Hokusai::Block
2
+ template <<~EOF
3
+ [template]
4
+ empty
5
+ EOF
6
+
7
+ uses(empty: Hokusai::Blocks::Empty)
8
+
9
+ computed! :script
10
+
11
+ def after_updated
12
+ if @last_height != children[0].node.meta.get_prop(:height)
13
+ @last_height = children[0].node.meta.get_prop(:height)
14
+
15
+ node.meta.set_prop(:height, @last_height)
16
+ emit("height_updated", @last_height)
17
+ end
18
+ end
19
+
20
+ def on_mounted
21
+ klass = eval(script)
22
+
23
+ raise Hokusai::Error.new("Class #{klass} is not a Hokusai::Block") unless klass.ancestors.include?(Hokusai::Block)
24
+
25
+ node.meta.set_child(0, klass.mount)
26
+ end
27
+
28
+ def render(canvas)
29
+ if Hokusai.can_render(canvas)
30
+ yield canvas
31
+ end
32
+ end
33
+ end
@@ -15,7 +15,7 @@ class Hokusai::Blocks::Vblock < Hokusai::Block
15
15
  canvas.vertical = true
16
16
  canvas.reverse = reverse
17
17
 
18
- if background.nil?
18
+ if background.nil? && outline.nil?
19
19
  yield canvas
20
20
  else
21
21
  draw do
@@ -26,10 +26,10 @@ module Hokusai
26
26
  def trim_canvas(canvas)
27
27
  x, y, w, h = background_boundary
28
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)
29
+ canvas.x = x + padding.left + outline.left
30
+ canvas.y = y + padding.top + outline.top
31
+ canvas.width = w - (padding.left + padding.right + outline.left + outline.right)
32
+ canvas.height = h - (padding.top + padding.bottom + outline.top + outline.bottom)
33
33
 
34
34
  canvas
35
35
  end
@@ -6,82 +6,84 @@ require_relative "./commands/scissor"
6
6
  require_relative "./commands/text"
7
7
 
8
8
  module Hokusai
9
+ # A proxy class for invoking various UI commands
10
+ #
11
+ # Invocations of commands are immediately sent to the backend
12
+ # for drawing
13
+ #
14
+ # Used as part of the drawing api for Hokusai::Block
9
15
  class Commands
10
16
  attr_reader :queue
11
17
 
12
- def initialize
13
- # @queue = []
14
- end
15
-
16
- # def hash
17
- # [self.class, *@queue.map(&:hash)].hash
18
- # end
18
+ def initialize; end
19
19
 
20
+ # Draw a rectangle
21
+ #
22
+ # @param [Float] the x coordinate
23
+ # @param [Float] the y coordinate
24
+ # @param [Float] the width of the rectangle
25
+ # @param [Float] height of the rectangle
20
26
  def rect(x, y, w, h)
21
27
  command = Commands::Rect.new(x, y, w, h)
28
+
22
29
  yield(command)
23
30
 
24
31
  command.draw
25
- # add(command)
26
32
  end
27
33
 
34
+ # Draw a circle
35
+ #
36
+ # @param [Float] x coordinate
37
+ # @param [Float] y coordinate
38
+ # @param [Float] radius of the circle
28
39
  def circle(x, y, radius)
29
40
  command = Commands::Circle.new(x, y, radius)
41
+
30
42
  yield(command)
31
43
 
32
44
  command.draw
33
- # add(command)
34
45
  end
35
46
 
47
+ # Draws an SVG
48
+ #
49
+ # @param [String] location of the svg
50
+ # @param [Float] x coord
51
+ # @param [Float] y coord
52
+ # @param [Float] width of the svg
53
+ # @param [Float] height of the svg
36
54
  def svg(source, x, y, w, h)
37
55
  command = Commands::SVG.new(source, x, y, w, h)
38
- yield(command)
39
56
 
40
- # add(command)
57
+ yield(command)
41
58
  end
42
59
 
43
60
  # Invokes an image command
44
61
  # from a filename, at position {x,y} with `w`x`h` dimensions
45
62
  def image(source, x, y, w, h)
46
63
  Commands::Image.new(source, x, y, w, h).draw
47
- # add(Commands::Image.new(source, x, y, w, h))
48
64
  end
49
65
 
50
66
  # Invokes a scissor begin command
51
67
  # at position {x,y} with `w`x`h` dimensions
52
68
  def scissor_begin(x, y, w, h)
53
69
  Commands::ScissorBegin.new(x, y, w, h).draw
54
- # add(Commands::ScissorBegin.new(x, y, w, h))
55
70
  end
56
71
 
57
72
  # Invokes a scissor stop command
58
73
  def scissor_end
59
74
  Commands::ScissorEnd.new.draw
60
- # add(Commands::ScissorEnd.new)
61
75
  end
62
76
 
77
+ # Draws text
78
+ #
79
+ # @param [String] the text content
80
+ # @param [Float] x coord
81
+ # @param [Float] y coord
63
82
  def text(content, x, y)
64
83
  command = Commands::Text.new(content, x, y)
65
84
  yield command
66
85
 
67
86
  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
87
  end
86
88
  end
87
89
  end
@@ -1,4 +1,6 @@
1
1
  module Hokusai
2
+ # Represents a patch to move a loop item
3
+ # from one location to another
2
4
  class MovePatch
3
5
  attr_accessor :from, :to, :delete
4
6
 
@@ -9,6 +11,8 @@ module Hokusai
9
11
  end
10
12
  end
11
13
 
14
+ # Represents a patch to insert an item
15
+ # into the loop list
12
16
  class InsertPatch
13
17
  attr_accessor :target, :value, :delete
14
18
 
@@ -19,6 +23,8 @@ module Hokusai
19
23
  end
20
24
  end
21
25
 
26
+ # Represents a patch to update the value
27
+ # of a loop item at an index
22
28
  class UpdatePatch
23
29
  attr_accessor :target, :value
24
30
 
@@ -28,6 +34,7 @@ module Hokusai
28
34
  end
29
35
  end
30
36
 
37
+ # Patch to delete a loop list item
31
38
  class DeletePatch
32
39
  attr_accessor :target
33
40
 
@@ -36,6 +43,10 @@ module Hokusai
36
43
  end
37
44
  end
38
45
 
46
+ # A Differ for comparing one set of values to another
47
+ #
48
+ # When #patch is called, will yield various patches to
49
+ # true up the old values with the new values.
39
50
  class Diff
40
51
  attr_reader :before, :after, :insertions
41
52
 
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hokusai
4
+ # A Basic UI Event
4
5
  class Event
5
- attr_reader :captures, :bubbles
6
+ attr_reader :captures
6
7
  attr_accessor :stopped
7
8
 
9
+ # Sets the name of this event kind
8
10
  def self.name(name)
9
11
  @name = name
10
12
  end
@@ -13,33 +15,45 @@ module Hokusai
13
15
  self.class.instance_variable_get("@name")
14
16
  end
15
17
 
18
+ # Has the event stopped propagation?
19
+ # @return [Bool]
16
20
  def stopped
17
21
  @stopped ||= false
18
22
  end
19
23
 
24
+ # Stop propagation on this event
25
+ # @return [Void]
20
26
  def stop
21
27
  self.stopped = true
22
28
  end
23
29
 
30
+ # @return [Array<Block>] the captured blocks for this event
24
31
  def captures
25
32
  @captures ||= []
26
33
  end
27
34
 
28
- def bubbles
29
- @bubbles ||= []
30
- end
31
-
35
+ # A JSON string representing this event
36
+ #
37
+ # Used in automation
38
+ # @return [String]
32
39
  def to_json
33
40
  raise Hokusai::Error.new("#{self.class} must implement to_json")
34
41
  end
35
42
 
43
+ # Does the event match the provided Hokusai::Block?
44
+ #
45
+ # @param [Hokusai::Block]
46
+ # @return [Bool]
36
47
  def matches(block)
37
48
  return false if block.node.portal.nil?
38
49
 
39
50
  val = block.node.portal.ast.event(name)
51
+
40
52
  !!val
41
53
  end
42
54
 
55
+ # Emit the event to all captured blocks,
56
+ # stopping if any of the blocks stop propagation
43
57
  def bubble
44
58
  while block = captures.pop
45
59
  block.emit(name, self)
@@ -165,8 +165,16 @@ module Hokusai
165
165
  class MouseOutEvent < MouseEvent
166
166
  name "mouseout"
167
167
 
168
- def capture(block, _)
168
+ def capture(block, canvas)
169
169
  captures << block if matches(block)
170
+
171
+ if left[:clicked] && !clicked(canvas)
172
+ block.node.meta.blur
173
+ end
174
+ end
175
+
176
+ def clicked(canvas)
177
+ LibHokusai.hoku_input_is_clicked(input.raw, canvas.to_hoku_rect)
170
178
  end
171
179
  end
172
180
  end
@@ -1,29 +1,54 @@
1
1
  module Hokusai
2
+ # A Backend agnostic font interface
3
+ #
4
+ # Backends are expected to implement the following methods
2
5
  class Font
6
+ # Creates a wrapping of text based on container width and font size
7
+ #
8
+ # @param [String] the text to wrap
9
+ # @param [Integer] the font size
10
+ # @param [Float] the width of the container
11
+ # @param [Float] an initital offset
12
+ # @return [Hokusai::Clamping]
3
13
  def clamp(text, size, width, initial_offset = 0.0)
4
14
  raise Hokusai::Error.new("Font #clamp not implemented")
5
15
  end
6
16
 
17
+ # Creates a wrapping of text based on the container width and font size
18
+ # and parses markdown
19
+ # @param [String] the text to wrap
20
+ # @param [Integer] the font size
21
+ # @param [Float] the width of the container
22
+ # @param [Float] an initital offset
23
+ # @return [Hokusai::Clamping]
7
24
  def clamp_markdown(text, size, width, initial_offset = 0.0)
8
25
  raise Hokusai::Error.new("Font #clamp not implemented")
9
26
  end
10
27
 
28
+ # @return [Integer] the font height
11
29
  def height
12
30
  raise Hokusai::Error.new("Font #height not implemented")
13
31
  end
14
32
  end
15
33
 
34
+ # A class representing wrapped text
35
+ #
36
+ # A clamping has many segments, delimited by a newline
37
+ # A segment has many possible groups, and a group has many possible charss
16
38
  class Clamping
17
39
  class Char
18
40
  attr_reader :raw
41
+
19
42
  def initialize(raw)
20
43
  @raw = raw
21
44
  end
22
45
 
46
+ # @return [Float] the width of the char
23
47
  def width
24
48
  raw[:width]
25
49
  end
26
50
 
51
+ # @return [Integer] the offset of the char relative to the clamping
27
52
  def offset
28
53
  raw[:offset]
29
54
  end
@@ -36,46 +61,57 @@ module Hokusai
36
61
  @raw = raw
37
62
  end
38
63
 
64
+ # @return [Integer] the offset of the group relative to the clamping
39
65
  def offset
40
66
  @offset ||= raw[:offset]
41
67
  end
42
68
 
69
+ # @return [Integer] number of chars in this group
43
70
  def size
44
71
  @size ||= raw[:size]
45
72
  end
46
73
 
74
+ # @return [Float] the total width of chars in this group
47
75
  def width
48
76
  chars.sum(&:width)
49
77
  end
50
78
 
79
+ # @return [UInt] a flag for this group type
51
80
  def type
52
81
  @type ||= raw[:type]
53
82
  end
54
83
 
84
+ # @return [Bool] is this group normal?
55
85
  def normal?
56
86
  @normal ||= type == LibHokusai::GROUP_NORMAL
57
87
  end
58
88
 
89
+ # @return [Bool] is this group bold?
59
90
  def bold?
60
91
  @bold ||= ((type & LibHokusai::GROUP_BOLD) != 0)
61
92
  end
62
93
 
94
+ # @return [Bool] is this group italics?
63
95
  def italics?
64
96
  @italics ||= ((type & LibHokusai::GROUP_ITALICS) != 0)
65
97
  end
66
98
 
99
+ # @return [Bool] does this group represent a hyperlink?
67
100
  def link?
68
101
  @link ||= ((type & LibHokusai::GROUP_LINK) != 0)
69
102
  end
70
103
 
104
+ # @return [Bool] does this group represent a code block?
71
105
  def code?
72
106
  @code ||= type & LibHokusai::GROUP_CODE
73
107
  end
74
108
 
109
+ # @return [String] the hyperlink for this group if there is one
75
110
  def link
76
111
  @href ||= raw[:payload].read_string
77
112
  end
78
113
 
114
+ # @return [Array<Hokusai::Char>] an array of chars
79
115
  def chars
80
116
  return @chars unless @chars.nil?
81
117
 
@@ -106,18 +142,23 @@ module Hokusai
106
142
  @raw = raw
107
143
  end
108
144
 
145
+ # A segment width given a range of offsets
146
+ # NOTE: Defaults to the full segment
109
147
  def width(range = (offset...offset + size))
110
148
  chars[range]&.sum(&:width) || 0.0
111
149
  end
112
150
 
151
+ # @return [Integer] the offset of this segment relative to the clamping
113
152
  def offset
114
153
  raw[:offset]
115
154
  end
116
155
 
156
+ # @return [Integer] the number of chars in this segment
117
157
  def size
118
158
  raw[:size]
119
159
  end
120
160
 
161
+ # @return [Array<Hokusai::Char>] an array of chars
121
162
  def chars
122
163
  return @chars unless @chars.nil?
123
164
 
@@ -140,6 +181,7 @@ module Hokusai
140
181
  end
141
182
  end
142
183
 
184
+ # @return [Array<Hokusai::Group>] an array of clamping groups
143
185
  def groups
144
186
  return @groups unless @groups.nil?
145
187
 
@@ -243,6 +285,7 @@ module Hokusai
243
285
  end
244
286
  end
245
287
 
288
+ # Keeps track of any loaded fonts
246
289
  class FontRegistry
247
290
  attr_reader :fonts, :active_font
248
291
 
@@ -251,28 +294,45 @@ module Hokusai
251
294
  @active_font = nil
252
295
  end
253
296
 
297
+ # Registers a font
298
+ #
299
+ # @param [String] the name of the font
300
+ # @param [Hokusai::Font] a font
254
301
  def register(name, font)
255
302
  raise Hokusai::Error.new("Font #{name} already registered") if fonts[name]
256
303
 
257
304
  fonts[name] = font
258
305
  end
259
306
 
307
+ # Returns the active font's name
308
+ #
309
+ # @return [String]
260
310
  def active_font_name
261
311
  raise Hokusai::Error.new("No active font") if active_font.nil?
262
312
 
263
313
  active_font
264
314
  end
265
315
 
316
+ # Activates a font by name
317
+ #
318
+ # @param [String] the name of the registered font
266
319
  def activate(name)
267
320
  raise Hokusai::Error.new("Font #{name} is not registered") unless fonts[name]
268
321
 
269
322
  @active_font = name
270
323
  end
271
324
 
325
+ # Fetches a font
326
+ #
327
+ # @param [String] the name of the registered font
328
+ # @return [Hokusai::Font]
272
329
  def get(name)
273
330
  fonts[name]
274
331
  end
275
332
 
333
+ # Fetches the active font
334
+ #
335
+ # @return [Hokusai::Font]
276
336
  def active
277
337
  fonts[active_font]
278
338
  end
@@ -105,24 +105,18 @@ module Hokusai
105
105
  end
106
106
 
107
107
  def update(block)
108
- # pp props
109
- # if parent_block = parent
110
- if target_block = target
111
- if updater_block = updater
112
- # Hokusai::Pool.post do
113
- block.public_send(:before_updated) if block.respond_to?(:before_updated)
114
-
115
- updater_block.call(block, target_block, target_block)
116
- block.public_send(:after_updated) if block.respond_to?(:after_updated)
117
-
118
- # end
119
- end
120
- end
121
- # end
108
+ if target_block = target
109
+ if updater_block = updater
110
+ block.public_send(:before_updated) if block.respond_to?(:before_updated)
122
111
 
123
- children?&.each do |child|
124
- child.update
112
+ updater_block.call(block, target_block, target_block)
113
+ block.public_send(:after_updated) if block.respond_to?(:after_updated)
125
114
  end
115
+ end
116
+
117
+ children?&.each do |child|
118
+ child.update
119
+ end
126
120
  end
127
121
 
128
122
  def has_ast?(ast, index)
@@ -83,7 +83,7 @@ module Hokusai
83
83
  elsif context&.table&.[](method)
84
84
  value = context.table[method]
85
85
  else
86
- value = block.public_send(method)
86
+ value = block.instance_eval(method)
87
87
  end
88
88
  else
89
89
  value = method