whirled_peas 0.4.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +38 -0
  4. data/README.md +450 -103
  5. data/Rakefile +28 -3
  6. data/examples/intro.rb +52 -0
  7. data/examples/scrolling.rb +53 -0
  8. data/lib/whirled_peas.rb +4 -8
  9. data/lib/whirled_peas/command_line.rb +44 -55
  10. data/lib/whirled_peas/errors.rb +2 -0
  11. data/lib/whirled_peas/frame.rb +0 -1
  12. data/lib/whirled_peas/frame/consumer.rb +30 -0
  13. data/lib/whirled_peas/frame/debug_consumer.rb +30 -0
  14. data/lib/whirled_peas/frame/event_loop.rb +28 -29
  15. data/lib/whirled_peas/frame/producer.rb +11 -5
  16. data/lib/whirled_peas/graphics.rb +19 -0
  17. data/lib/whirled_peas/graphics/box_painter.rb +101 -0
  18. data/lib/whirled_peas/graphics/canvas.rb +118 -0
  19. data/lib/whirled_peas/graphics/composer.rb +80 -0
  20. data/lib/whirled_peas/graphics/container_coords.rb +72 -0
  21. data/lib/whirled_peas/graphics/container_dimensions.rb +93 -0
  22. data/lib/whirled_peas/graphics/container_painter.rb +272 -0
  23. data/lib/whirled_peas/graphics/debugger.rb +52 -0
  24. data/lib/whirled_peas/graphics/grid_painter.rb +69 -0
  25. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  26. data/lib/whirled_peas/graphics/painter.rb +23 -0
  27. data/lib/whirled_peas/graphics/renderer.rb +26 -0
  28. data/lib/whirled_peas/graphics/screen.rb +70 -0
  29. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  30. data/lib/whirled_peas/graphics/text_painter.rb +40 -0
  31. data/lib/whirled_peas/settings.rb +5 -0
  32. data/lib/whirled_peas/settings/alignment.rb +24 -0
  33. data/lib/whirled_peas/settings/bg_color.rb +24 -0
  34. data/lib/whirled_peas/settings/border.rb +101 -0
  35. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  36. data/lib/whirled_peas/settings/color.rb +68 -0
  37. data/lib/whirled_peas/settings/container_settings.rb +223 -0
  38. data/lib/whirled_peas/settings/debugger.rb +96 -0
  39. data/lib/whirled_peas/settings/display_flow.rb +27 -0
  40. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  41. data/lib/whirled_peas/settings/grid_settings.rb +19 -0
  42. data/lib/whirled_peas/settings/margin.rb +8 -0
  43. data/lib/whirled_peas/settings/padding.rb +8 -0
  44. data/lib/whirled_peas/settings/position.rb +15 -0
  45. data/lib/whirled_peas/settings/scrollbar.rb +15 -0
  46. data/lib/whirled_peas/settings/sizing.rb +19 -0
  47. data/lib/whirled_peas/settings/spacing.rb +58 -0
  48. data/lib/whirled_peas/settings/text_color.rb +21 -0
  49. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  50. data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
  51. data/lib/whirled_peas/utils/ansi.rb +6 -56
  52. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  53. data/lib/whirled_peas/utils/title_font.rb +1 -1
  54. data/lib/whirled_peas/version.rb +1 -1
  55. data/screen_test/elements/box.frame +1 -0
  56. data/screen_test/elements/box.rb +20 -0
  57. data/screen_test/elements/grid.frame +1 -0
  58. data/screen_test/elements/grid.rb +13 -0
  59. data/screen_test/elements/screen_overflow_x.frame +1 -0
  60. data/screen_test/elements/screen_overflow_x.rb +9 -0
  61. data/screen_test/elements/screen_overflow_y.frame +1 -0
  62. data/screen_test/elements/screen_overflow_y.rb +9 -0
  63. data/screen_test/elements/text.frame +1 -0
  64. data/screen_test/elements/text.rb +9 -0
  65. data/screen_test/elements/text_multiline.frame +1 -0
  66. data/screen_test/elements/text_multiline.rb +9 -0
  67. data/screen_test/settings/align/box_around.frame +1 -0
  68. data/screen_test/settings/align/box_around.rb +16 -0
  69. data/screen_test/settings/align/box_between.frame +1 -0
  70. data/screen_test/settings/align/box_between.rb +16 -0
  71. data/screen_test/settings/align/box_center.frame +1 -0
  72. data/screen_test/settings/align/box_center.rb +21 -0
  73. data/screen_test/settings/align/box_default.frame +1 -0
  74. data/screen_test/settings/align/box_default.rb +20 -0
  75. data/screen_test/settings/align/box_evenly.frame +1 -0
  76. data/screen_test/settings/align/box_evenly.rb +16 -0
  77. data/screen_test/settings/align/box_left.frame +1 -0
  78. data/screen_test/settings/align/box_left.rb +21 -0
  79. data/screen_test/settings/align/box_right.frame +1 -0
  80. data/screen_test/settings/align/box_right.rb +21 -0
  81. data/screen_test/settings/align/children_center.frame +1 -0
  82. data/screen_test/settings/align/children_center.rb +15 -0
  83. data/screen_test/settings/align/children_left.frame +1 -0
  84. data/screen_test/settings/align/children_left.rb +15 -0
  85. data/screen_test/settings/align/children_right.frame +1 -0
  86. data/screen_test/settings/align/children_right.rb +15 -0
  87. data/screen_test/settings/align/grid_center.frame +1 -0
  88. data/screen_test/settings/align/grid_center.rb +18 -0
  89. data/screen_test/settings/align/grid_default.frame +1 -0
  90. data/screen_test/settings/align/grid_default.rb +17 -0
  91. data/screen_test/settings/align/grid_left.frame +1 -0
  92. data/screen_test/settings/align/grid_left.rb +18 -0
  93. data/screen_test/settings/align/grid_right.frame +1 -0
  94. data/screen_test/settings/align/grid_right.rb +18 -0
  95. data/screen_test/settings/ansi/bold.frame +1 -0
  96. data/screen_test/settings/ansi/bold.rb +14 -0
  97. data/screen_test/settings/ansi/color.frame +1 -0
  98. data/screen_test/settings/ansi/color.rb +37 -0
  99. data/screen_test/settings/ansi/underline.frame +1 -0
  100. data/screen_test/settings/ansi/underline.rb +14 -0
  101. data/screen_test/settings/border.frame +1 -0
  102. data/screen_test/settings/border.rb +13 -0
  103. data/screen_test/settings/flow/box_b2t.frame +1 -0
  104. data/screen_test/settings/flow/box_b2t.rb +26 -0
  105. data/screen_test/settings/flow/box_l2r.frame +1 -0
  106. data/screen_test/settings/flow/box_l2r.rb +26 -0
  107. data/screen_test/settings/flow/box_r2l.frame +1 -0
  108. data/screen_test/settings/flow/box_r2l.rb +26 -0
  109. data/screen_test/settings/flow/box_t2b.frame +1 -0
  110. data/screen_test/settings/flow/box_t2b.rb +26 -0
  111. data/screen_test/settings/flow/grid_b2t.frame +1 -0
  112. data/screen_test/settings/flow/grid_b2t.rb +14 -0
  113. data/screen_test/settings/flow/grid_l2r.frame +1 -0
  114. data/screen_test/settings/flow/grid_l2r.rb +14 -0
  115. data/screen_test/settings/flow/grid_r2l.frame +1 -0
  116. data/screen_test/settings/flow/grid_r2l.rb +14 -0
  117. data/screen_test/settings/flow/grid_t2b.frame +1 -0
  118. data/screen_test/settings/flow/grid_t2b.rb +14 -0
  119. data/screen_test/settings/height/box.frame +1 -0
  120. data/screen_test/settings/height/box.rb +13 -0
  121. data/screen_test/settings/height/box_border_sizing.frame +1 -0
  122. data/screen_test/settings/height/box_border_sizing.rb +15 -0
  123. data/screen_test/settings/height/grid.frame +1 -0
  124. data/screen_test/settings/height/grid.rb +14 -0
  125. data/screen_test/settings/height/overflow_box.frame +1 -0
  126. data/screen_test/settings/height/overflow_box.rb +13 -0
  127. data/screen_test/settings/height/overflow_box_l2r.frame +1 -0
  128. data/screen_test/settings/height/overflow_box_l2r.rb +17 -0
  129. data/screen_test/settings/height/overflow_box_t2b.frame +1 -0
  130. data/screen_test/settings/height/overflow_box_t2b.rb +16 -0
  131. data/screen_test/settings/height/overflow_grid.frame +1 -0
  132. data/screen_test/settings/height/overflow_grid.rb +16 -0
  133. data/screen_test/settings/margin.frame +1 -0
  134. data/screen_test/settings/margin.rb +16 -0
  135. data/screen_test/settings/padding.frame +1 -0
  136. data/screen_test/settings/padding.rb +13 -0
  137. data/screen_test/settings/position/box_left.frame +1 -0
  138. data/screen_test/settings/position/box_left.rb +17 -0
  139. data/screen_test/settings/position/box_left_negative.frame +1 -0
  140. data/screen_test/settings/position/box_left_negative.rb +17 -0
  141. data/screen_test/settings/position/box_top.frame +1 -0
  142. data/screen_test/settings/position/box_top.rb +17 -0
  143. data/screen_test/settings/position/box_top_negative.frame +1 -0
  144. data/screen_test/settings/position/box_top_negative.rb +17 -0
  145. data/screen_test/settings/position/grid_left.frame +1 -0
  146. data/screen_test/settings/position/grid_left.rb +18 -0
  147. data/screen_test/settings/position/grid_left_negative.frame +1 -0
  148. data/screen_test/settings/position/grid_left_negative.rb +18 -0
  149. data/screen_test/settings/position/grid_top.frame +1 -0
  150. data/screen_test/settings/position/grid_top.rb +18 -0
  151. data/screen_test/settings/position/grid_top_negative.frame +1 -0
  152. data/screen_test/settings/position/grid_top_negative.rb +18 -0
  153. data/screen_test/settings/scroll/horiz_box.frame +1 -0
  154. data/screen_test/settings/scroll/horiz_box.rb +17 -0
  155. data/screen_test/settings/scroll/horiz_box_align_center.rb +18 -0
  156. data/screen_test/settings/scroll/horiz_box_align_right.rb +18 -0
  157. data/screen_test/settings/scroll/vert_box.frame +1 -0
  158. data/screen_test/settings/scroll/vert_box.rb +20 -0
  159. data/screen_test/settings/title_font.frame +1 -0
  160. data/screen_test/settings/title_font.rb +12 -0
  161. data/screen_test/settings/valign/box_around.frame +1 -0
  162. data/screen_test/settings/valign/box_around.rb +17 -0
  163. data/screen_test/settings/valign/box_between.frame +1 -0
  164. data/screen_test/settings/valign/box_between.rb +17 -0
  165. data/screen_test/settings/valign/box_bottom.frame +1 -0
  166. data/screen_test/settings/valign/box_bottom.rb +17 -0
  167. data/screen_test/settings/valign/box_default.frame +1 -0
  168. data/screen_test/settings/valign/box_default.rb +16 -0
  169. data/screen_test/settings/valign/box_evenly.frame +1 -0
  170. data/screen_test/settings/valign/box_evenly.rb +17 -0
  171. data/screen_test/settings/valign/box_middle.frame +1 -0
  172. data/screen_test/settings/valign/box_middle.rb +17 -0
  173. data/screen_test/settings/valign/box_top.frame +1 -0
  174. data/screen_test/settings/valign/box_top.rb +17 -0
  175. data/screen_test/settings/valign/grid_bottom.frame +1 -0
  176. data/screen_test/settings/valign/grid_bottom.rb +15 -0
  177. data/screen_test/settings/valign/grid_default.frame +1 -0
  178. data/screen_test/settings/valign/grid_default.rb +14 -0
  179. data/screen_test/settings/valign/grid_middle.frame +1 -0
  180. data/screen_test/settings/valign/grid_middle.rb +15 -0
  181. data/screen_test/settings/valign/grid_top.frame +1 -0
  182. data/screen_test/settings/valign/grid_top.rb +15 -0
  183. data/screen_test/settings/width/box_border_sizing.frame +1 -0
  184. data/screen_test/settings/width/box_border_sizing.rb +15 -0
  185. data/screen_test/settings/width/box_content.frame +1 -0
  186. data/screen_test/settings/width/box_content.rb +15 -0
  187. data/screen_test/settings/width/box_default.frame +1 -0
  188. data/screen_test/settings/width/box_default.rb +14 -0
  189. data/screen_test/settings/width/grid.frame +1 -0
  190. data/screen_test/settings/width/grid.rb +14 -0
  191. data/screen_test/settings/width/overflow_align_center.frame +1 -0
  192. data/screen_test/settings/width/overflow_align_center.rb +14 -0
  193. data/screen_test/settings/width/overflow_align_right.frame +1 -0
  194. data/screen_test/settings/width/overflow_align_right.rb +14 -0
  195. data/screen_test/settings/width/overflow_box.frame +1 -0
  196. data/screen_test/settings/width/overflow_box.rb +13 -0
  197. data/screen_test/settings/width/overflow_box_l2r.frame +1 -0
  198. data/screen_test/settings/width/overflow_box_l2r.rb +16 -0
  199. data/screen_test/settings/width/overflow_box_t2b.frame +1 -0
  200. data/screen_test/settings/width/overflow_box_t2b.rb +17 -0
  201. data/screen_test/settings/width/overflow_grid.frame +1 -0
  202. data/screen_test/settings/width/overflow_grid.rb +14 -0
  203. data/tools/whirled_peas/tools/screen_tester.rb +233 -0
  204. metadata +191 -12
  205. data/bin/title_fonts +0 -6
  206. data/lib/whirled_peas/frame/print_consumer.rb +0 -33
  207. data/lib/whirled_peas/template.rb +0 -5
  208. data/lib/whirled_peas/template/element.rb +0 -230
  209. data/lib/whirled_peas/template/settings.rb +0 -530
  210. data/lib/whirled_peas/ui.rb +0 -5
  211. data/lib/whirled_peas/ui/canvas.rb +0 -68
  212. data/lib/whirled_peas/ui/painter.rb +0 -287
  213. data/lib/whirled_peas/ui/screen.rb +0 -63
  214. data/lib/whirled_peas/utils/color.rb +0 -101
@@ -1,5 +0,0 @@
1
- module WhirledPeas
2
- module UI
3
- end
4
- private_constant :UI
5
- end
@@ -1,68 +0,0 @@
1
- require_relative '../utils/ansi'
2
-
3
- module WhirledPeas
4
- module UI
5
- # Canvas represent the area of the screen a painter can paint on.
6
- class Canvas
7
- # A Stroke is a single line, formatted string of characters that is painted at
8
- # a given position on a Canvas. This class is not meant to be instantiated
9
- # directly. Instead, use Canvas#stroke to create a new Stroke.
10
- class Stroke
11
- attr_reader :left, :top, :chars
12
-
13
- def initialize(left, top, chars)
14
- @left = left
15
- @top = top
16
- @chars = chars
17
- end
18
-
19
- def hash
20
- [left, top, chars].hash
21
- end
22
-
23
- def ==(other)
24
- other.is_a?(self.class) && self.hash == other.hash
25
- end
26
-
27
- def inspect
28
- "Stroke(left=#{left}, top=#{top}, chars=#{chars})"
29
- end
30
-
31
- alias_method :eq?, :==
32
- end
33
- private_constant :Stroke
34
-
35
- EMPTY_STROKE = Stroke.new(nil, nil, nil)
36
-
37
- attr_reader :left, :top, :width, :height
38
-
39
- def initialize(left, top, width, height)
40
- @left = left
41
- @top = top
42
- @width = width
43
- @height = height
44
- end
45
-
46
- # Return a new Stroke instance, verifying only characters within the canvas
47
- # are included in the stroke.
48
- def stroke(left, top, chars)
49
- if left >= self.left + self.width || left + chars.length <= self.left
50
- EMPTY_STROKE
51
- elsif top < self.top || top >= self.top + self.height
52
- EMPTY_STROKE
53
- else
54
- if left < self.left
55
- chars = chars[self.left - left..-1]
56
- left = self.left
57
- end
58
- num_chars = [self.left + self.width, left + chars.length].min - left
59
- Stroke.new(left, top, Ansi.substring(chars, 0, num_chars))
60
- end
61
- end
62
-
63
- def inspect
64
- "Canvas(left=#{left}, top=#{top}, width=#{width}, height=#{height})"
65
- end
66
- end
67
- end
68
- end
@@ -1,287 +0,0 @@
1
- require_relative '../template/element'
2
- require_relative '../template/settings'
3
- require_relative '../utils/ansi'
4
-
5
- require_relative 'canvas'
6
-
7
- module WhirledPeas
8
- module UI
9
- DEBUG_SPACING = ARGV.include?('--debug-spacing')
10
-
11
- class TextPainter
12
- JUSTIFICATION = DEBUG_SPACING ? 'j' : ' '
13
-
14
- def initialize(text, canvas)
15
- @text = text
16
- @canvas = canvas
17
- end
18
-
19
- def paint(&block)
20
- text.lines.each.with_index do |line, index|
21
- yield canvas.stroke(canvas.left, canvas.top + index, justified(line))
22
- end
23
- end
24
-
25
- private
26
-
27
- attr_reader :text, :canvas
28
-
29
- def visible(line)
30
- if line.length <= text.preferred_width
31
- line
32
- elsif text.settings.align == TextAlign::LEFT
33
- line[0..text.preferred_width - 1]
34
- elsif text.settings.align == TextAlign::CENTER
35
- left_chop = (line.length - text.preferred_width) / 2
36
- right_chop = line.length - text.preferred_width - left_chop
37
- line[left_chop..-right_chop - 1]
38
- else
39
- line[-text.preferred_width..-1]
40
- end
41
- end
42
-
43
- def justified(line)
44
- format_settings = [*text.settings.color, *text.settings.bg_color]
45
- format_settings << Ansi::BOLD if text.settings.bold?
46
- format_settings << Ansi::UNDERLINE if text.settings.underline?
47
-
48
- ljust = case text.settings.align
49
- when TextAlign::LEFT
50
- 0
51
- when TextAlign::CENTER
52
- [0, (text.preferred_width - line.length) / 2].max
53
- when TextAlign::RIGHT
54
- [0, text.preferred_width - line.length].max
55
- end
56
- rjust = [0, text.preferred_width - line.length - ljust].max
57
- Ansi.format(JUSTIFICATION * ljust, [*text.settings.bg_color]) +
58
- Ansi.format(visible(line), format_settings) +
59
- Ansi.format(JUSTIFICATION * rjust, [*text.settings.bg_color])
60
- end
61
- end
62
- private_constant :TextPainter
63
-
64
- class ContainerPainter
65
- PADDING = DEBUG_SPACING ? 'p' : ' '
66
-
67
- def initialize(container, canvas)
68
- @container = container
69
- @settings = container.settings
70
- @canvas = canvas
71
- end
72
-
73
- def paint(&block)
74
- return if container.num_rows == 0 || container.num_cols == 0
75
- top = canvas.top + settings.margin.top
76
- if settings.auto_margin?
77
- left = canvas.left + (canvas.width - container.preferred_width) / 2
78
- else
79
- left = canvas.left + settings.margin.left
80
- end
81
- if settings.border.top?
82
- yield canvas.stroke(left, top, top_border)
83
- top += 1
84
- end
85
- container.num_rows.times do |row_num|
86
- if row_num > 0 && settings.border.inner_horiz?
87
- yield canvas.stroke(left, top, middle_border)
88
- top += 1
89
- end
90
- (settings.padding.top + container.row_height + settings.padding.bottom).times do
91
- yield canvas.stroke(left, top, content_line)
92
- top += 1
93
- end
94
- end
95
- if settings.border.bottom?
96
- yield canvas.stroke(left, top, bottom_border)
97
- top += 1
98
- end
99
- end
100
-
101
- private
102
-
103
- attr_reader :container, :settings, :canvas
104
-
105
- def line_stroke(left_border, horiz_border, junc_border, right_border)
106
- stroke = ''
107
- stroke += left_border if settings.border.left?
108
- container.num_cols.times do |col_num|
109
- stroke += junc_border if col_num > 0 && settings.border.inner_horiz?
110
- stroke += horiz_border * (container.col_width + settings.padding.left + settings.padding.right)
111
- end
112
- stroke += right_border if settings.border.right?
113
- Ansi.format(stroke, [*settings.border.color, *settings.bg_color])
114
- end
115
-
116
- def top_border
117
- line_stroke(
118
- settings.border.style.top_left,
119
- settings.border.style.top_horiz,
120
- settings.border.style.top_junc,
121
- settings.border.style.top_right
122
- )
123
- end
124
-
125
- def content_line
126
- line_stroke(
127
- settings.border.style.left_vert,
128
- PADDING,
129
- settings.border.style.middle_vert,
130
- settings.border.style.right_vert
131
- )
132
- end
133
-
134
- def middle_border
135
- line_stroke(
136
- settings.border.style.left_junc,
137
- settings.border.style.middle_horiz,
138
- settings.border.style.cross_junc,
139
- settings.border.style.right_junc
140
- )
141
- end
142
-
143
- def bottom_border
144
- line_stroke(
145
- settings.border.style.bottom_left,
146
- settings.border.style.bottom_horiz,
147
- settings.border.style.bottom_junc,
148
- settings.border.style.bottom_right
149
- )
150
- end
151
- end
152
-
153
- class BoxContainer
154
- attr_reader :settings, :num_cols, :num_rows, :col_width, :row_height, :preferred_width
155
-
156
- def initialize(box)
157
- @settings = ContainerSettings.merge(box.settings)
158
- @num_cols = 1
159
- @num_rows = 1
160
- @col_width = box.content_width
161
- @row_height = box.content_height
162
- @preferred_width = box.preferred_width
163
- end
164
- end
165
-
166
- class BoxPainter
167
- def initialize(box, canvas)
168
- @box = box
169
- @canvas = canvas
170
- end
171
-
172
- def paint(&block)
173
- container = BoxContainer.new(box)
174
- ContainerPainter.new(container, canvas).paint(&block)
175
- top = canvas.top + box.settings.margin.top + (box.settings.border.top? ? 1 : 0) + box.settings.padding.top
176
- if box.settings.auto_margin?
177
- margin = (canvas.width - box.preferred_width) / 2
178
- else
179
- margin = box.settings.margin.left
180
- end
181
- left = canvas.left + margin + (box.settings.border.left? ? 1 : 0) + box.settings.padding.left
182
- greedy_width = box.settings.vertical_flow? || box.children.length == 1
183
- children = box.children
184
- children = children.reverse if box.settings.reverse_flow?
185
- children.each do |child|
186
- if greedy_width
187
- width = box.content_width
188
- height = child.preferred_height
189
- else
190
- width = child.preferred_width
191
- height = box.content_height
192
- end
193
- child_canvas = Canvas.new(left, top, width, height)
194
- Painter.paint(child, child_canvas, &block)
195
- if box.settings.horizontal_flow?
196
- left += child.preferred_width
197
- else
198
- top += child.preferred_height
199
- end
200
- end
201
- end
202
-
203
- private
204
-
205
- attr_reader :box, :canvas
206
- end
207
-
208
- class GridContainer
209
- attr_reader :settings, :num_cols, :num_rows, :col_width, :row_height, :preferred_width
210
-
211
- def initialize(grid, num_cols, num_rows)
212
- @settings = ContainerSettings.merge(grid.settings)
213
- @num_cols = num_cols
214
- @num_rows = num_rows
215
- @col_width = grid.col_width
216
- @row_height = grid.row_height
217
- @preferred_width = grid.preferred_width
218
- end
219
- end
220
-
221
- class GridPainter
222
- def initialize(grid, canvas)
223
- @grid = grid
224
- @canvas = canvas
225
- available_width = grid.preferred_width - (grid.settings.margin.left || 0) - (grid.settings.margin.right || 0)
226
- @num_cols = grid.settings.num_cols || (available_width - (grid.settings.border.left? ? 1 : 0) - (grid.settings.border.right? ? 1 : 0) + (grid.border.inner_vert ? 1 : 0)) / (col_width + grid.settings.padding.left + grid.settings.right + (grid.border.inner_vert ? 1 : 0))
227
- end
228
-
229
- def paint(&block)
230
- return if grid.children.empty?
231
-
232
- container = GridContainer.new(grid, num_cols, (grid.children.length.to_f / num_cols).ceil)
233
- ContainerPainter.new(container, canvas).paint(&block)
234
-
235
- children = if grid.settings.transpose?
236
- grid.children.length.times.map do |i|
237
- grid.children[(i * num_cols) % grid.children.length + i / (grid.children.length / num_cols)]
238
- end.compact
239
- else
240
- grid.children
241
- end
242
-
243
- top = canvas.top + grid.settings.margin.top + (grid.settings.border.top? ? 1 : 0) + grid.settings.padding.top
244
- if grid.settings.auto_margin?
245
- margin = (canvas.width - grid.preferred_width) / 2
246
- else
247
- margin = grid.settings.margin.left
248
- end
249
- left = canvas.left + margin + (grid.settings.border.left? ? 1 : 0) + grid.settings.padding.left
250
- grid_height = grid.settings.padding.top + grid.row_height + grid.settings.padding.bottom + (grid.settings.border.inner_horiz? ? 1 : 0)
251
- grid_width = grid.settings.padding.left + grid.col_width + grid.settings.padding.right + (grid.settings.border.inner_vert? ? 1 : 0)
252
- children.each_slice(num_cols).each.with_index do |row, row_num|
253
- row_top = top + row_num * grid_height
254
- row.each.with_index do |element, col_num|
255
- col_left = left + col_num * grid_width
256
- child_canvas = Canvas.new(
257
- col_left,
258
- row_top,
259
- element.preferred_width,
260
- element.preferred_height
261
- )
262
- Painter.paint(element, child_canvas, &block)
263
- end
264
- end
265
- end
266
-
267
- private
268
-
269
- attr_reader :grid, :canvas, :num_cols
270
- end
271
-
272
- module Painter
273
- PAINTERS = {
274
- TextElement => TextPainter,
275
- BoxElement => BoxPainter,
276
- GridElement => GridPainter,
277
- }
278
-
279
- def self.paint(element, canvas, &block)
280
- if element.is_a?(Template)
281
- element = BoxElement.from_template(element, canvas.width, canvas.height)
282
- end
283
- PAINTERS[element.class].new(element, canvas).paint(&block)
284
- end
285
- end
286
- end
287
- end
@@ -1,63 +0,0 @@
1
- require 'highline'
2
-
3
- require_relative '../utils/ansi'
4
-
5
- require_relative 'painter'
6
-
7
- module WhirledPeas
8
- module UI
9
- class Screen
10
- def initialize(print_output=true)
11
- @print_output = print_output
12
- @terminal = HighLine.new.terminal
13
- @strokes = []
14
- refresh_size!
15
- Signal.trap('SIGWINCH', proc { self.refresh_size! })
16
- end
17
-
18
- def paint(template)
19
- @template = template
20
- draw
21
- end
22
-
23
- def refresh
24
- # No need to refresh if the screen dimensions have not changed
25
- return if @refreshed_width == width || @refreshed_height == height
26
- draw
27
- end
28
-
29
- def finalize
30
- return unless @print_output
31
- print Ansi.clear
32
- print Ansi.cursor_pos(top: height - 1)
33
- print Ansi.cursor_visible(true)
34
- STDOUT.flush
35
- end
36
-
37
- protected
38
-
39
- def refresh_size!
40
- @width, @height = terminal.terminal_size
41
- end
42
-
43
- private
44
-
45
- attr_reader :cursor, :terminal, :width, :height
46
-
47
- def draw
48
- strokes = [Ansi.cursor_visible(false), Ansi.cursor_pos, Ansi.clear_down]
49
- Painter.paint(@template, Canvas.new(0, 0, width, height)) do |stroke|
50
- unless stroke.chars.nil?
51
- strokes << Ansi.cursor_pos(left: stroke.left, top: stroke.top)
52
- strokes << stroke.chars
53
- end
54
- end
55
- return unless @print_output
56
- strokes.each(&method(:print))
57
- STDOUT.flush
58
- @refreshed_width = width
59
- @refreshed_height = height
60
- end
61
- end
62
- end
63
- end
@@ -1,101 +0,0 @@
1
- require_relative 'ansi'
2
-
3
- module WhirledPeas
4
- module UI
5
- # An abstract class that encapsulates colors for a specific use case
6
- class Color
7
- # The ANSI codes for bright colors are offset by this much from their
8
- # standard versions
9
- BRIGHT_OFFSET = 60
10
- private_constant :BRIGHT_OFFSET
11
-
12
- # Validate the `color` argument is either (1) nil, (2) a valid Color constant in
13
- # this class or (3) a symbol that maps to valid Color constant. E.g. if there
14
- # is a RED constant in an implementing class, then :red or :bright_red are
15
- # valid values for `color`
16
- #
17
- # @param color [Color|Symbol]
18
- # @return [Color|Symbol] the value passed in if valid, otherwise an ArgumentError
19
- # is raised.
20
- def self.validate!(color)
21
- return unless color
22
- if color.is_a?(Symbol)
23
- error_message = "Unsupported #{self.name.split('::').last}: #{color.inspect}"
24
- match = color.to_s.match(/^(bright_)?(\w+)$/)
25
- begin
26
- color = self.const_get(match[2].upcase)
27
- raise ArgumentError, error_message unless color.is_a?(Color)
28
- if match[1]
29
- raise ArgumentError, error_message if color.bright?
30
- color.bright
31
- else
32
- color
33
- end
34
- rescue NameError
35
- raise ArgumentError, error_message
36
- end
37
- else
38
- color
39
- end
40
- end
41
-
42
- def initialize(code, bright=false)
43
- @code = code
44
- @bright = bright
45
- end
46
-
47
- def bright?
48
- @bright
49
- end
50
-
51
- def bright
52
- bright? ? self : self.class.new(@code + BRIGHT_OFFSET, true)
53
- end
54
-
55
- def hash
56
- [@code, @bright].hash
57
- end
58
-
59
- def ==(other)
60
- other.is_a?(self.class) && self.hash == other.hash
61
- end
62
- alias_method :eq?, :==
63
-
64
- def to_s
65
- @code.to_s
66
- end
67
-
68
- def inspect
69
- "#{self.class.name.split('::').last}(code=#{@code}, bright=#{@bright})"
70
- end
71
- end
72
- private_constant :Color
73
-
74
- class BgColor < Color
75
- BG_OFFSET = 10
76
- private_constant :BG_OFFSET
77
-
78
- BLACK = new(Ansi::BLACK + BG_OFFSET)
79
- RED = new(Ansi::RED + BG_OFFSET)
80
- GREEN = new(Ansi::GREEN + BG_OFFSET)
81
- YELLOW = new(Ansi::YELLOW + BG_OFFSET)
82
- BLUE = new(Ansi::BLUE + BG_OFFSET)
83
- MAGENTA = new(Ansi::MAGENTA + BG_OFFSET)
84
- CYAN = new(Ansi::CYAN + BG_OFFSET)
85
- WHITE = new(Ansi::WHITE + BG_OFFSET)
86
- GRAY = BLACK.bright
87
- end
88
-
89
- class TextColor < Color
90
- BLACK = new(Ansi::BLACK)
91
- RED = new(Ansi::RED)
92
- GREEN = new(Ansi::GREEN)
93
- YELLOW = new(Ansi::YELLOW)
94
- BLUE = new(Ansi::BLUE)
95
- MAGENTA = new(Ansi::MAGENTA)
96
- CYAN = new(Ansi::CYAN)
97
- WHITE = new(Ansi::WHITE)
98
- GRAY = BLACK.bright
99
- end
100
- end
101
- end