whirled_peas 0.4.0 → 0.7.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 (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
@@ -0,0 +1,272 @@
1
+ require 'whirled_peas/utils/formatted_string'
2
+
3
+ require_relative 'container_coords'
4
+ require_relative 'painter'
5
+
6
+ module WhirledPeas
7
+ module Graphics
8
+ class ContainerPainter < Painter
9
+ PADDING = ' '
10
+
11
+ def initialize(name, settings)
12
+ super
13
+ @children = []
14
+ end
15
+
16
+ def paint(canvas, left, top, &block)
17
+ return unless canvas.writable?
18
+ return unless needs_printing?
19
+ canvas_coords = coords(left, top)
20
+ stroke_left = canvas_coords.border_left
21
+ stroke_top = canvas_coords.border_top
22
+ formatting = [*settings.border.color, *settings.bg_color]
23
+ if settings.border.top?
24
+ canvas.stroke(stroke_left, stroke_top, top_border_stroke(canvas_coords), formatting, &block)
25
+ stroke_top += 1
26
+ end
27
+ middle_border = dimensions.num_rows > 1 ? middle_border_stroke(canvas_coords) : ''
28
+ dimensions.num_rows.times do |row_num|
29
+ if row_num > 0 && settings.border.inner_horiz?
30
+ canvas.stroke(stroke_left, stroke_top, middle_border, formatting, &block)
31
+ stroke_top += 1
32
+ end
33
+ canvas_coords.inner_grid_height.times do |row_within_cell|
34
+ canvas.stroke(stroke_left, stroke_top, content_line_stroke(canvas_coords, row_within_cell), formatting, &block)
35
+ stroke_top += 1
36
+ end
37
+ if settings.scrollbar.horiz?
38
+ canvas.stroke(stroke_left, stroke_top, bottom_scroll_stroke(canvas_coords), formatting, &block)
39
+ stroke_top += 1
40
+ end
41
+ end
42
+ if settings.border.bottom?
43
+ canvas.stroke(stroke_left, stroke_top, bottom_border_stroke(canvas_coords), formatting, &block)
44
+ stroke_top += 1
45
+ end
46
+ end
47
+
48
+ def add_child(child)
49
+ children << child
50
+ end
51
+
52
+ def num_children
53
+ children.length
54
+ end
55
+
56
+ def children?
57
+ num_children > 0
58
+ end
59
+
60
+ def each_child(&block)
61
+ children.each(&block)
62
+ end
63
+
64
+ private
65
+
66
+ attr_reader :children
67
+
68
+ def needs_printing?
69
+ return true if settings.bg_color
70
+ return true if settings.border.outer?
71
+ return true if dimensions.num_cols > 1 && settings.border.inner_vert?
72
+ return true if dimensions.num_rows > 1 && settings.border.inner_horiz?
73
+ settings.scrollbar.horiz? || settings.scrollbar.vert?
74
+ end
75
+
76
+ def coords(left, top)
77
+ ContainerCoords.new(dimensions, settings, left, top)
78
+ end
79
+
80
+ def horiz_justify_offset(containing_width)
81
+ if settings.align_center?
82
+ [(dimensions.content_width - containing_width) / 2, 0]
83
+ elsif settings.align_right?
84
+ [dimensions.content_width - containing_width, 0]
85
+ elsif settings.align_between?
86
+ return [0, 0] if num_children == 1
87
+ [0, (dimensions.content_width - containing_width) / (num_children - 1)]
88
+ elsif settings.align_around?
89
+ full_spacing = (dimensions.content_width - containing_width) / num_children
90
+ [full_spacing / 2, full_spacing]
91
+ elsif settings.align_evenly?
92
+ spacing = (dimensions.content_width - containing_width) / (num_children + 1)
93
+ [spacing, spacing]
94
+ else
95
+ [0, 0]
96
+ end
97
+ end
98
+
99
+ def vert_justify_offset(containing_height)
100
+ if settings.valign_middle?
101
+ [(dimensions.content_height - containing_height) / 2, 0]
102
+ elsif settings.valign_bottom?
103
+ [dimensions.content_height - containing_height, 0]
104
+ elsif settings.valign_between?
105
+ return [0, 0] if num_children == 1
106
+ [0, (dimensions.content_height - containing_height) / (num_children - 1)]
107
+ elsif settings.valign_around?
108
+ full_spacing = (dimensions.content_height - containing_height) / num_children
109
+ [full_spacing / 2, full_spacing]
110
+ elsif settings.valign_evenly?
111
+ spacing = (dimensions.content_height - containing_height) / (num_children + 1)
112
+ [spacing, spacing]
113
+ else
114
+ [0, 0]
115
+ end
116
+ end
117
+
118
+ def line_stroke(left_border, junc_border, right_border, &block)
119
+ stroke = ''
120
+ stroke += left_border if settings.border.left?
121
+ dimensions.num_cols.times do |col_num|
122
+ stroke += junc_border if col_num > 0 && settings.border.inner_vert?
123
+ stroke += yield
124
+ end
125
+ stroke += right_border if settings.border.right?
126
+ stroke
127
+ end
128
+
129
+ def top_border_stroke(canvas_coords)
130
+ line_stroke(
131
+ settings.border.style.top_left,
132
+ settings.border.style.top_junc,
133
+ settings.border.style.top_right
134
+ ) do
135
+ settings.border.style.top_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
136
+ end
137
+ end
138
+
139
+ def middle_border_stroke(canvas_coords)
140
+ line_stroke(
141
+ settings.border.style.left_junc,
142
+ settings.border.style.cross_junc,
143
+ settings.border.style.right_junc
144
+ ) do
145
+ settings.border.style.middle_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
146
+ end
147
+ end
148
+
149
+ def bottom_border_stroke(canvas_coords)
150
+ line_stroke(
151
+ settings.border.style.bottom_left,
152
+ settings.border.style.bottom_junc,
153
+ settings.border.style.bottom_right
154
+ ) do
155
+ settings.border.style.bottom_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
156
+ end
157
+ end
158
+
159
+ def content_line_stroke(canvas_coords, row_within_cell)
160
+ line_stroke(
161
+ settings.border.style.left_vert,
162
+ settings.border.style.middle_vert,
163
+ settings.border.style.right_vert,
164
+ ) do
165
+ if settings.scrollbar.vert?
166
+ if dimensions.children_height <= canvas_coords.grid_height || children.first.settings.position.top > 0
167
+ scrollbar_char = GUTTER
168
+ else
169
+ scrollbar_char = vert_scroll_char(
170
+ dimensions.children_height + dimensions.padding_height,
171
+ canvas_coords.inner_grid_height,
172
+ -children.first.settings.position.top,
173
+ row_within_cell
174
+ )
175
+ end
176
+ PADDING * canvas_coords.inner_grid_width + scrollbar_char
177
+ else
178
+ PADDING * canvas_coords.inner_grid_width
179
+ end
180
+ end
181
+ end
182
+
183
+ def bottom_scroll_stroke(canvas_coords)
184
+ line_stroke(
185
+ settings.border.style.left_vert,
186
+ settings.border.style.middle_vert,
187
+ settings.border.style.right_vert,
188
+ ) do
189
+ canvas_coords.inner_grid_width.times.map do |col_within_cell|
190
+ horiz_scroll_char(
191
+ dimensions.children_width + dimensions.padding_width,
192
+ canvas_coords.inner_grid_width,
193
+ -children.first.settings.position.left,
194
+ col_within_cell
195
+ )
196
+ end.join
197
+ end
198
+ end
199
+
200
+ GUTTER = ' '
201
+ HORIZONTAL = %w[▗ ▄ ▖]
202
+ VERTICAL = %w[
203
+
204
+
205
+
206
+ ]
207
+
208
+ def horiz_scroll_char(col_count, viewable_col_count, first_visible_col, curr_col)
209
+ scroll_char(col_count, viewable_col_count, first_visible_col, curr_col, HORIZONTAL)
210
+ end
211
+
212
+ def vert_scroll_char(row_count, viewable_row_count, first_visible_row, curr_row)
213
+ scroll_char(row_count, viewable_row_count, first_visible_row, curr_row, VERTICAL)
214
+ end
215
+
216
+ private
217
+
218
+ def scroll_char(total_count, viewable_count, first_visible, curr, chars)
219
+ return GUTTER unless total_count > 0 && viewable_count > 0
220
+ # The scroll handle has the exact same relative size and position in the scroll gutter
221
+ # that the viewable content has in the total content area. For example, a content area
222
+ # that is 50 columns wide with a view port that is 20 columns wide might look like
223
+ #
224
+ # +---------1-----****2*********3******---4---------+
225
+ # | * * |
226
+ # | hidden * viewable * hidden |
227
+ # | * * |
228
+ # +---------1-----****2*********3******---4---------+
229
+ #
230
+ # The scoll gutter, would look like
231
+ #
232
+ # |......********.....|
233
+ #
234
+ # Scrolling all the way to the right results in
235
+ #
236
+ # +---------1---------2---------3*********4*********+
237
+ # | * *
238
+ # | hidden * viewable *
239
+ # | * *
240
+ # +---------1---------2---------3*********4*********+
241
+ # |...........********|
242
+
243
+ # The first task of determining how much of the handle is visible in a row/column is to
244
+ # calculate the range (as a precentage of the total) of viewable items
245
+ viewable_start = first_visible.to_f / total_count
246
+ viewable_end = (first_visible + viewable_count).to_f / total_count
247
+
248
+ # Always use the same length for the scroll bar so it does not give an inchworm effect
249
+ # as it scrolls along.
250
+ #
251
+ # Also, double the value now to get granularity for half width
252
+ # scrollbar characters.
253
+ scrollbar_length = ((2 * viewable_count ** 2).to_f / total_count).ceil
254
+ scrollbar_start = ((2 * first_visible * viewable_count).to_f / total_count).floor
255
+
256
+ first_half = (scrollbar_start...scrollbar_start + scrollbar_length).include?(2 * curr)
257
+ second_half = (scrollbar_start...scrollbar_start + scrollbar_length).include?(2 * curr + 1)
258
+
259
+ if first_half && second_half
260
+ chars[1]
261
+ elsif first_half
262
+ chars[2]
263
+ elsif second_half
264
+ chars[0]
265
+ else
266
+ GUTTER
267
+ end
268
+ end
269
+ end
270
+ private_constant :ContainerPainter
271
+ end
272
+ end
@@ -0,0 +1,52 @@
1
+ require 'whirled_peas/settings/debugger'
2
+
3
+ require_relative 'container_painter'
4
+ require_relative 'text_painter'
5
+
6
+ module WhirledPeas
7
+ module Graphics
8
+ class Debugger
9
+ def initialize(painter)
10
+ @painter = painter
11
+ end
12
+
13
+ def debug(indent='')
14
+ info = [
15
+ "#{indent}* #{painter.class}(#{painter.name})",
16
+ ]
17
+ info << "#{indent + ' '}- Dimensions(#{dimensions})"
18
+ info << "#{indent + ' '}- Settings"
19
+ info << Settings::Debugger.new(painter.settings).debug(indent + ' ')
20
+ if painter.is_a?(TextPainter)
21
+ content = if painter.content.length > 1
22
+ '<multiline>'
23
+ elsif painter.content.first.length > 12
24
+ "#{painter.content.first[0..9]}...".inspect
25
+ else
26
+ painter.content.first.inspect
27
+ end
28
+ info << "#{indent + ' '}- Content(value=#{content})"
29
+ elsif painter.is_a?(ContainerPainter)
30
+ info << "#{indent + ' '}- Children"
31
+ info += painter.each_child.map { |c| Debugger.new(c).debug(indent + ' ') }
32
+ end
33
+ info.join("\n")
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :painter
39
+
40
+ def dimensions
41
+ outer = "#{painter.dimensions.outer_width}x#{painter.dimensions.outer_height}"
42
+ if painter.is_a?(ContainerPainter)
43
+ content = "#{painter.dimensions.content_width}x#{painter.dimensions.content_height}"
44
+ grid = "#{painter.dimensions.num_cols}x#{painter.dimensions.num_rows}"
45
+ "outer=#{outer}, content=#{content}, grid=#{grid}"
46
+ else
47
+ "outer=#{outer}"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,69 @@
1
+ require_relative 'container_dimensions'
2
+ require_relative 'container_painter'
3
+
4
+ module WhirledPeas
5
+ module Graphics
6
+ class GridPainter < ContainerPainter
7
+ def paint(canvas, left, top, &block)
8
+ super
9
+ return unless canvas.writable?
10
+ each_child.with_index do |child, index|
11
+ col_index, row_index = grid_cell(index)
12
+ left_offset, _ = horiz_justify_offset(child.dimensions.outer_width)
13
+ top_offset, _ = vert_justify_offset(child.dimensions.outer_height)
14
+ child_canvas = canvas.child(
15
+ coords(left, top).content_left(col_index) + left_offset,
16
+ coords(left, top).content_top(row_index) + top_offset,
17
+ [dimensions.content_width, child.dimensions.outer_width].min,
18
+ [dimensions.content_height, child.dimensions.outer_height].min
19
+ )
20
+ child.paint(
21
+ child_canvas,
22
+ coords(left, top).content_left(col_index) + left_offset,
23
+ coords(left, top).content_top(row_index) + top_offset,
24
+ &block
25
+ )
26
+ end
27
+ end
28
+
29
+ def dimensions
30
+ @dimensions ||= begin
31
+ if settings.num_cols.nil? || settings.num_cols == 0
32
+ raise SettingsError, "`num_cols` must be set for GridSettings(#{name})"
33
+ end
34
+ num_cols = settings.num_cols
35
+ num_rows = (num_children.to_f / num_cols).ceil
36
+ content_width = 0
37
+ each_child do |child|
38
+ if child.dimensions.outer_width > content_width
39
+ content_width = child.dimensions.outer_width
40
+ end
41
+ end
42
+ content_height = 0
43
+ each_child do |child|
44
+ if child.dimensions.outer_height > content_height
45
+ content_height = child.dimensions.outer_height
46
+ end
47
+ end
48
+ ContainerDimensions.new(
49
+ settings, content_width, content_height, num_cols, num_rows
50
+ )
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def grid_cell(index)
57
+ if settings.horizontal_flow?
58
+ col_index, row_index = [index % dimensions.num_cols, index / dimensions.num_cols]
59
+ col_index = dimensions.num_cols - col_index - 1 if settings.reverse_flow?
60
+ else
61
+ col_index, row_index = [index / dimensions.num_rows, index % dimensions.num_rows]
62
+ row_index = dimensions.num_rows - row_index - 1 if settings.reverse_flow?
63
+ end
64
+ [col_index, row_index]
65
+ end
66
+ end
67
+ private_constant :BoxPainter
68
+ end
69
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'renderer'
2
+
3
+ module WhirledPeas
4
+ module Graphics
5
+ class MockScreen
6
+ def initialize(width:, height:)
7
+ @width = width
8
+ @height = height
9
+ end
10
+
11
+ def paint(template)
12
+ Renderer.new(template, width, height).paint { }
13
+ end
14
+
15
+ def refresh
16
+ end
17
+
18
+ def finalize
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :width, :height
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class Painter
4
+ attr_reader :name, :settings
5
+
6
+ def initialize(name, settings)
7
+ @settings = settings
8
+ @name = name
9
+ end
10
+
11
+ def paint(canvas, left, top, &block)
12
+ end
13
+
14
+ def dimensions
15
+ end
16
+
17
+ def inspect
18
+ "#{self.class.name.split('::').last}(name=#{name.inspect})"
19
+ end
20
+ end
21
+ private_constant :Painter
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'canvas'
2
+
3
+ module WhirledPeas
4
+ module Graphics
5
+ class Renderer
6
+ def initialize(template, width, height)
7
+ @template = template
8
+ @width = width
9
+ @height = height
10
+ end
11
+
12
+ def paint(&block)
13
+ # Modify the template's settings so that it fills the entire screen
14
+ template.settings.width = width
15
+ template.settings.height = height
16
+ template.settings.sizing = :border
17
+ template.settings.set_margin(left: 0, top: 0, right: 0, bottom: 0)
18
+ template.paint(Canvas.new(0, 0, width, height), 0, 0, &block)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :template, :width, :height
24
+ end
25
+ end
26
+ end