whirled_peas 0.1.1 → 0.5.0

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG.md +32 -0
  4. data/Gemfile +1 -0
  5. data/README.md +240 -79
  6. data/Rakefile +37 -3
  7. data/bin/debug +35 -0
  8. data/exe/whirled_peas +7 -0
  9. data/lib/whirled_peas.rb +17 -37
  10. data/lib/whirled_peas/command_line.rb +263 -0
  11. data/lib/whirled_peas/config.rb +21 -0
  12. data/lib/whirled_peas/debugger.rb +80 -0
  13. data/lib/whirled_peas/errors.rb +7 -0
  14. data/lib/whirled_peas/frame.rb +0 -8
  15. data/lib/whirled_peas/frame/consumer.rb +14 -51
  16. data/lib/whirled_peas/frame/debug_consumer.rb +30 -0
  17. data/lib/whirled_peas/frame/event_loop.rb +68 -38
  18. data/lib/whirled_peas/frame/producer.rb +37 -34
  19. data/lib/whirled_peas/graphics.rb +5 -0
  20. data/lib/whirled_peas/graphics/box_painter.rb +100 -0
  21. data/lib/whirled_peas/graphics/canvas.rb +107 -0
  22. data/lib/whirled_peas/graphics/container_coords.rb +61 -0
  23. data/lib/whirled_peas/graphics/container_dimensions.rb +65 -0
  24. data/lib/whirled_peas/graphics/container_painter.rb +116 -0
  25. data/lib/whirled_peas/graphics/debugger.rb +43 -0
  26. data/lib/whirled_peas/graphics/grid_painter.rb +56 -0
  27. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  28. data/lib/whirled_peas/graphics/painter.rb +24 -0
  29. data/lib/whirled_peas/graphics/renderer.rb +49 -0
  30. data/lib/whirled_peas/graphics/screen.rb +65 -0
  31. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  32. data/lib/whirled_peas/graphics/text_painter.rb +38 -0
  33. data/lib/whirled_peas/settings.rb +5 -0
  34. data/lib/whirled_peas/settings/bg_color.rb +22 -0
  35. data/lib/whirled_peas/settings/border.rb +101 -0
  36. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  37. data/lib/whirled_peas/settings/color.rb +69 -0
  38. data/lib/whirled_peas/settings/container_settings.rb +128 -0
  39. data/lib/whirled_peas/settings/debugger.rb +87 -0
  40. data/lib/whirled_peas/settings/display_flow.rb +25 -0
  41. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  42. data/lib/whirled_peas/settings/grid_settings.rb +11 -0
  43. data/lib/whirled_peas/settings/margin.rb +8 -0
  44. data/lib/whirled_peas/settings/padding.rb +8 -0
  45. data/lib/whirled_peas/settings/position.rb +15 -0
  46. data/lib/whirled_peas/settings/spacing.rb +24 -0
  47. data/lib/whirled_peas/settings/text_align.rb +19 -0
  48. data/lib/whirled_peas/settings/text_color.rb +19 -0
  49. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  50. data/lib/whirled_peas/template.rb +5 -0
  51. data/lib/whirled_peas/template/box_element.rb +8 -0
  52. data/lib/whirled_peas/template/composer.rb +68 -0
  53. data/lib/whirled_peas/template/container.rb +28 -0
  54. data/lib/whirled_peas/template/debugger.rb +34 -0
  55. data/lib/whirled_peas/template/element.rb +13 -0
  56. data/lib/whirled_peas/template/grid_element.rb +8 -0
  57. data/lib/whirled_peas/template/text_element.rb +24 -0
  58. data/lib/whirled_peas/utils.rb +5 -0
  59. data/lib/whirled_peas/utils/ansi.rb +53 -0
  60. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  61. data/lib/whirled_peas/utils/title_font.rb +75 -0
  62. data/lib/whirled_peas/version.rb +1 -1
  63. data/screen_test/rendered/elements/box.frame +1 -0
  64. data/screen_test/rendered/elements/box.rb +20 -0
  65. data/screen_test/rendered/elements/grid.frame +1 -0
  66. data/screen_test/rendered/elements/grid.rb +13 -0
  67. data/screen_test/rendered/elements/screen_overflow.rb +9 -0
  68. data/screen_test/rendered/elements/text.frame +1 -0
  69. data/screen_test/rendered/elements/text.rb +9 -0
  70. data/screen_test/rendered/elements/text_multiline.frame +1 -0
  71. data/screen_test/rendered/elements/text_multiline.rb +9 -0
  72. data/screen_test/rendered/settings/align/box.frame +1 -0
  73. data/screen_test/rendered/settings/align/box.rb +20 -0
  74. data/screen_test/rendered/settings/align/children_center.frame +1 -0
  75. data/screen_test/rendered/settings/align/children_center.rb +13 -0
  76. data/screen_test/rendered/settings/align/children_left.frame +1 -0
  77. data/screen_test/rendered/settings/align/children_left.rb +13 -0
  78. data/screen_test/rendered/settings/align/children_right.frame +1 -0
  79. data/screen_test/rendered/settings/align/children_right.rb +13 -0
  80. data/screen_test/rendered/settings/align/grid.frame +1 -0
  81. data/screen_test/rendered/settings/align/grid.rb +20 -0
  82. data/screen_test/rendered/settings/ansi/bold.frame +1 -0
  83. data/screen_test/rendered/settings/ansi/bold.rb +15 -0
  84. data/screen_test/rendered/settings/ansi/color.frame +1 -0
  85. data/screen_test/rendered/settings/ansi/color.rb +37 -0
  86. data/screen_test/rendered/settings/ansi/underline.frame +1 -0
  87. data/screen_test/rendered/settings/ansi/underline.rb +15 -0
  88. data/screen_test/rendered/settings/border.frame +1 -0
  89. data/screen_test/rendered/settings/border.rb +13 -0
  90. data/screen_test/rendered/settings/flow/l2r.frame +1 -0
  91. data/screen_test/rendered/settings/flow/l2r.rb +24 -0
  92. data/screen_test/rendered/settings/flow/t2b.frame +1 -0
  93. data/screen_test/rendered/settings/flow/t2b.rb +24 -0
  94. data/screen_test/rendered/settings/height/box.frame +1 -0
  95. data/screen_test/rendered/settings/height/box.rb +13 -0
  96. data/screen_test/rendered/settings/height/grid.frame +1 -0
  97. data/screen_test/rendered/settings/height/grid.rb +14 -0
  98. data/screen_test/rendered/settings/height/overflow_box.frame +1 -0
  99. data/screen_test/rendered/settings/height/overflow_box.rb +13 -0
  100. data/screen_test/rendered/settings/height/overflow_box_l2r.frame +1 -0
  101. data/screen_test/rendered/settings/height/overflow_box_l2r.rb +15 -0
  102. data/screen_test/rendered/settings/height/overflow_box_t2b.frame +1 -0
  103. data/screen_test/rendered/settings/height/overflow_box_t2b.rb +14 -0
  104. data/screen_test/rendered/settings/height/overflow_grid.frame +1 -0
  105. data/screen_test/rendered/settings/height/overflow_grid.rb +16 -0
  106. data/screen_test/rendered/settings/margin.frame +1 -0
  107. data/screen_test/rendered/settings/margin.rb +14 -0
  108. data/screen_test/rendered/settings/padding.frame +1 -0
  109. data/screen_test/rendered/settings/padding.rb +11 -0
  110. data/screen_test/rendered/settings/position/box_left.frame +1 -0
  111. data/screen_test/rendered/settings/position/box_left.rb +17 -0
  112. data/screen_test/rendered/settings/position/box_left_negative.frame +1 -0
  113. data/screen_test/rendered/settings/position/box_left_negative.rb +17 -0
  114. data/screen_test/rendered/settings/position/box_top.frame +1 -0
  115. data/screen_test/rendered/settings/position/box_top.rb +17 -0
  116. data/screen_test/rendered/settings/position/box_top_negative.frame +1 -0
  117. data/screen_test/rendered/settings/position/box_top_negative.rb +17 -0
  118. data/screen_test/rendered/settings/position/grid_left.frame +1 -0
  119. data/screen_test/rendered/settings/position/grid_left.rb +18 -0
  120. data/screen_test/rendered/settings/position/grid_left_negative.frame +1 -0
  121. data/screen_test/rendered/settings/position/grid_left_negative.rb +18 -0
  122. data/screen_test/rendered/settings/position/grid_top.frame +1 -0
  123. data/screen_test/rendered/settings/position/grid_top.rb +18 -0
  124. data/screen_test/rendered/settings/position/grid_top_negative.frame +1 -0
  125. data/screen_test/rendered/settings/position/grid_top_negative.rb +18 -0
  126. data/screen_test/rendered/settings/title_font.frame +1 -0
  127. data/screen_test/rendered/settings/title_font.rb +12 -0
  128. data/screen_test/rendered/settings/width/box.frame +1 -0
  129. data/screen_test/rendered/settings/width/box.rb +13 -0
  130. data/screen_test/rendered/settings/width/grid.frame +1 -0
  131. data/screen_test/rendered/settings/width/grid.rb +14 -0
  132. data/screen_test/rendered/settings/width/overflow_box.frame +1 -0
  133. data/screen_test/rendered/settings/width/overflow_box.rb +11 -0
  134. data/screen_test/rendered/settings/width/overflow_box_l2r.frame +1 -0
  135. data/screen_test/rendered/settings/width/overflow_box_l2r.rb +14 -0
  136. data/screen_test/rendered/settings/width/overflow_box_t2b.frame +1 -0
  137. data/screen_test/rendered/settings/width/overflow_box_t2b.rb +15 -0
  138. data/screen_test/rendered/settings/width/overflow_grid.frame +1 -0
  139. data/screen_test/rendered/settings/width/overflow_grid.rb +14 -0
  140. data/screen_test/screen_tester.rb +191 -0
  141. data/whirled_peas.gemspec +4 -2
  142. metadata +136 -20
  143. data/lib/whirled_peas/ui.rb +0 -7
  144. data/lib/whirled_peas/ui/ansi.rb +0 -154
  145. data/lib/whirled_peas/ui/canvas.rb +0 -35
  146. data/lib/whirled_peas/ui/element.rb +0 -199
  147. data/lib/whirled_peas/ui/painter.rb +0 -283
  148. data/lib/whirled_peas/ui/screen.rb +0 -62
  149. data/lib/whirled_peas/ui/settings.rb +0 -512
  150. data/lib/whirled_peas/ui/stroke.rb +0 -29
  151. data/sandbox/auto.rb +0 -13
  152. data/sandbox/box.rb +0 -19
  153. data/sandbox/grid.rb +0 -13
  154. data/sandbox/sandbox.rb +0 -17
  155. data/sandbox/text.rb +0 -33
@@ -0,0 +1,87 @@
1
+ require_relative 'border'
2
+ require_relative 'container_settings'
3
+ require_relative 'margin'
4
+ require_relative 'padding'
5
+ require_relative 'position'
6
+
7
+ module WhirledPeas
8
+ module Settings
9
+ class Debugger
10
+ def initialize(settings)
11
+ @settings = settings
12
+ end
13
+
14
+ def debug(indent='')
15
+ values = non_default_values(indent + ' ')
16
+ details = values.length > 0 ? values.join("\n") : "#{indent} <default>"
17
+ "#{indent}#{settings.class}\n#{details}"
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :settings
23
+
24
+ def non_default_values(indent)
25
+ settings.instance_variables.map do |ivar, values|
26
+ value = settings.instance_variable_get(ivar)
27
+ next if value.nil?
28
+ printable_value = debug_value(value)
29
+ "#{indent}#{ivar.to_s.sub(/^@?_?/, '')}: #{printable_value}" unless printable_value.nil?
30
+ end.compact
31
+ end
32
+
33
+ def debug_value(value)
34
+ return if value.nil?
35
+ case value
36
+ when Position
37
+ position_value(value)
38
+ when Margin
39
+ margin_value(value)
40
+ when Border
41
+ border_value(value)
42
+ when Padding
43
+ padding_value(value)
44
+ else
45
+ value.inspect
46
+ end
47
+ end
48
+
49
+ def non_defaults(object, default_object, keys)
50
+ values = keys.map.with_object({}) do |key, hash|
51
+ unless object.send(key) == default_object.send(key)
52
+ hash[key] = object.send(key)
53
+ end
54
+ end
55
+ values.keys.map { |k| "#{k}: #{values[k]}"}.join(', ')
56
+ end
57
+
58
+ def position_value(position)
59
+ values = non_defaults(position, Position.new, %i[left top])
60
+ return if values == ''
61
+ "Position(#{values})"
62
+ end
63
+
64
+ def margin_value(margin)
65
+ values = non_defaults(margin, Margin.new, %i[left top right bottom])
66
+ return if values == ''
67
+ "Margin(#{values})"
68
+ end
69
+
70
+ def border_value(border)
71
+ values = non_defaults(
72
+ border,
73
+ Border.new,
74
+ %i[left? top? right? bottom? inner_horiz? inner_vert? style color]
75
+ )
76
+ return if values == ''
77
+ "Border(#{values})"
78
+ end
79
+
80
+ def padding_value(padding)
81
+ values = non_defaults(padding, Padding.new, %i[left top right bottom])
82
+ return if values == ''
83
+ "Padding(#{values})"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,25 @@
1
+ module WhirledPeas
2
+ module Settings
3
+ module DisplayFlow
4
+ LEFT_TO_RIGHT = :l2r
5
+ RIGHT_TO_LEFT = :r2l
6
+ TOP_TO_BOTTOM = :t2b
7
+ BOTTOM_TO_TOP = :b2t
8
+
9
+ def self.validate!(flow)
10
+ return unless flow
11
+ if [
12
+ DisplayFlow::LEFT_TO_RIGHT,
13
+ DisplayFlow::RIGHT_TO_LEFT,
14
+ DisplayFlow::TOP_TO_BOTTOM,
15
+ DisplayFlow::BOTTOM_TO_TOP
16
+ ].include?(flow)
17
+ flow
18
+ else
19
+ raise ArgumentError, "Unsupported display flow: #{flow}"
20
+ end
21
+ end
22
+ end
23
+ private_constant :DisplayFlow
24
+ end
25
+ end
@@ -0,0 +1,61 @@
1
+ require 'json'
2
+
3
+ require_relative 'bg_color'
4
+ require_relative 'text_align'
5
+ require_relative 'text_color'
6
+
7
+ module WhirledPeas
8
+ module Settings
9
+ class ElementSettings
10
+ def self.inherit(parent)
11
+ child = self.new
12
+ child.inherit(parent)
13
+ child
14
+ end
15
+
16
+ def bg_color
17
+ @_bg_color
18
+ end
19
+
20
+ def bg_color=(color)
21
+ @_bg_color = BgColor.validate!(color)
22
+ end
23
+
24
+ def bold?
25
+ @_bold || false
26
+ end
27
+
28
+ def bold=(val)
29
+ @_bold = val
30
+ end
31
+
32
+ def color
33
+ @_color
34
+ end
35
+
36
+ def color=(color)
37
+ @_color = TextColor.validate!(color)
38
+ end
39
+
40
+ def underline?
41
+ @_underline || false
42
+ end
43
+
44
+ def underline=(val)
45
+ @_underline = val
46
+ end
47
+
48
+ def inherit(parent)
49
+ @_bg_color = parent._bg_color
50
+ @_bold = parent._bold
51
+ @_color = parent._color
52
+ @_underline = parent._underline
53
+ end
54
+
55
+ protected
56
+
57
+ attr_accessor :_bg_color, :_bold, :_color, :_underline
58
+ end
59
+ private_constant :ElementSettings
60
+ end
61
+ end
@@ -0,0 +1,11 @@
1
+ require 'whirled_peas/utils/title_font'
2
+
3
+ require_relative 'container_settings'
4
+
5
+ module WhirledPeas
6
+ module Settings
7
+ class GridSettings < ContainerSettings
8
+ attr_accessor :num_cols
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'spacing'
2
+
3
+ module WhirledPeas
4
+ module Settings
5
+ class Margin < Spacing
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'spacing'
2
+
3
+ module WhirledPeas
4
+ module Settings
5
+ class Padding < Spacing
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module WhirledPeas
2
+ module Settings
3
+ class Position
4
+ attr_writer :left, :top
5
+
6
+ def left
7
+ @left || 0
8
+ end
9
+
10
+ def top
11
+ @top || 0
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module WhirledPeas
2
+ module Settings
3
+ class Spacing
4
+ attr_writer :left, :top, :right, :bottom
5
+
6
+ def left
7
+ @left || 0
8
+ end
9
+
10
+ def top
11
+ @top || 0
12
+ end
13
+
14
+ def right
15
+ @right || 0
16
+ end
17
+
18
+ def bottom
19
+ @bottom || 0
20
+ end
21
+ end
22
+ private_constant :Spacing
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module WhirledPeas
2
+ module Settings
3
+ module TextAlign
4
+ LEFT = :left
5
+ CENTER = :center
6
+ RIGHT = :right
7
+
8
+ def self.validate!(align)
9
+ return unless align
10
+ if [TextAlign::LEFT, TextAlign::CENTER, TextAlign::RIGHT].include?(align)
11
+ align
12
+ else
13
+ raise ArgumentError, "Unsupported text alignment: #{align}"
14
+ end
15
+ end
16
+ end
17
+ private_constant :TextAlign
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require 'whirled_peas/utils/ansi'
2
+
3
+ require_relative 'color'
4
+
5
+ module WhirledPeas
6
+ module Settings
7
+ class TextColor < Color
8
+ BLACK = new(Utils::Ansi::BLACK)
9
+ RED = new(Utils::Ansi::RED)
10
+ GREEN = new(Utils::Ansi::GREEN)
11
+ YELLOW = new(Utils::Ansi::YELLOW)
12
+ BLUE = new(Utils::Ansi::BLUE)
13
+ MAGENTA = new(Utils::Ansi::MAGENTA)
14
+ CYAN = new(Utils::Ansi::CYAN)
15
+ WHITE = new(Utils::Ansi::WHITE)
16
+ GRAY = BLACK.bright
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ require 'whirled_peas/utils/title_font'
2
+
3
+ require_relative 'element_settings'
4
+
5
+ module WhirledPeas
6
+ module Settings
7
+ class TextSettings < ElementSettings
8
+ attr_reader :title_font
9
+
10
+ def title_font=(font)
11
+ @title_font = Utils::TitleFont.validate!(font)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module WhirledPeas
2
+ module Template
3
+ end
4
+ private_constant :Template
5
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'container'
2
+
3
+ module WhirledPeas
4
+ module Template
5
+ class BoxElement < Container
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,68 @@
1
+ require 'whirled_peas/settings/box_settings'
2
+ require 'whirled_peas/settings/grid_settings'
3
+ require 'whirled_peas/settings/text_settings'
4
+
5
+ require_relative 'box_element'
6
+ require_relative 'grid_element'
7
+ require_relative 'text_element'
8
+
9
+ module WhirledPeas
10
+ module Template
11
+ class Composer
12
+ def self.next_name
13
+ @counter ||= 0
14
+ @counter += 1
15
+ "Element-#{@counter}"
16
+ end
17
+
18
+ def self.build
19
+ settings = Settings::BoxSettings.new
20
+ template = BoxElement.new('TEMPLATE', settings)
21
+ composer = Template::Composer.new(template)
22
+ value = yield composer, settings
23
+ if !template.children? && TextElement.stringable?(value)
24
+ composer.add_text { value.to_s }
25
+ end
26
+ template
27
+ end
28
+
29
+ attr_reader :element
30
+
31
+ def initialize(element)
32
+ @element = element
33
+ end
34
+
35
+ def add_text(name=self.class.next_name, &block)
36
+ child_settings = Settings::TextSettings.inherit(element.settings)
37
+ child = TextElement.new(name, child_settings)
38
+ # TextElements are not composable, so yield nil
39
+ child.content = yield nil, child_settings
40
+ element.add_child(child)
41
+ end
42
+
43
+ def add_box(name=self.class.next_name, &block)
44
+ child_settings = Settings::BoxSettings.inherit(element.settings)
45
+ child = BoxElement.new(name, child_settings)
46
+ composer = self.class.new(child)
47
+ value = yield composer, child.settings
48
+ element.add_child(child)
49
+ if !child.children? && TextElement.stringable?(value)
50
+ composer.add_text("#{name}-Text") { value.to_s }
51
+ end
52
+ end
53
+
54
+ def add_grid(name=self.class.next_name, &block)
55
+ child_settings = Settings::GridSettings.inherit(element.settings)
56
+ child = GridElement.new(name, child_settings)
57
+ composer = self.class.new(child)
58
+ values = yield composer, child.settings
59
+ element.add_child(child)
60
+ if !child.children? && values.is_a?(Array)
61
+ values.each.with_index do |value, index|
62
+ composer.add_text("#{name}-Text-#{index}") { value.to_s }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'element'
2
+
3
+ module WhirledPeas
4
+ module Template
5
+ class Container < Element
6
+ def initialize(name, settings)
7
+ super
8
+ @children = []
9
+ end
10
+
11
+ def add_child(child)
12
+ @children << child
13
+ end
14
+
15
+ def children?
16
+ @children.length > 0
17
+ end
18
+
19
+ def num_children
20
+ @children.length
21
+ end
22
+
23
+ def each_child(&block)
24
+ @children.each(&block)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ require 'whirled_peas/settings/debugger'
2
+
3
+ require_relative 'container'
4
+ require_relative 'text_element'
5
+
6
+ module WhirledPeas
7
+ module Template
8
+ class Debugger
9
+ def initialize(element)
10
+ @element = element
11
+ end
12
+
13
+ def debug(indent='')
14
+ info = [
15
+ "#{indent}* #{element.class}(#{element.name})",
16
+ ]
17
+ if element.is_a?(TextElement)
18
+ info << "#{indent + ' '}- Content: #{element.content}"
19
+ end
20
+ info << "#{indent + ' '}- Settings"
21
+ info << Settings::Debugger.new(element.settings).debug(indent + ' ')
22
+ if element.is_a?(Container)
23
+ info << "#{indent + ' '}- Children"
24
+ info += element.each_child.map { |c| Debugger.new(c).debug(indent + ' ') }
25
+ end
26
+ info.join("\n")
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :element
32
+ end
33
+ end
34
+ end