whirled_peas 0.8.0 → 0.11.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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/CHANGELOG.md +23 -0
  4. data/README.md +9 -777
  5. data/doc/application.md +92 -0
  6. data/doc/cli.md +115 -0
  7. data/doc/components.md +55 -0
  8. data/doc/easing.md +257 -0
  9. data/doc/elements.md +69 -0
  10. data/doc/screen_test.md +23 -0
  11. data/doc/settings.md +466 -0
  12. data/doc/template_factory.md +53 -0
  13. data/doc/themes.md +15 -0
  14. data/examples/components.rb +35 -0
  15. data/examples/graph.rb +54 -0
  16. data/examples/scrolling.rb +9 -20
  17. data/lib/data/themes.yaml +17 -0
  18. data/lib/whirled_peas.rb +31 -9
  19. data/lib/whirled_peas/animator/frameset.rb +1 -1
  20. data/lib/whirled_peas/animator/producer.rb +1 -1
  21. data/lib/whirled_peas/command/config_command.rb +3 -3
  22. data/lib/whirled_peas/command/play.rb +4 -0
  23. data/lib/whirled_peas/command/themes.rb +77 -0
  24. data/lib/whirled_peas/command_line.rb +3 -1
  25. data/lib/whirled_peas/component.rb +35 -0
  26. data/lib/whirled_peas/component/list_with_active.rb +88 -0
  27. data/lib/whirled_peas/graphics/box_painter.rb +4 -4
  28. data/lib/whirled_peas/graphics/composer.rb +27 -2
  29. data/lib/whirled_peas/graphics/container_coords.rb +23 -25
  30. data/lib/whirled_peas/graphics/container_dimensions.rb +24 -0
  31. data/lib/whirled_peas/graphics/container_painter.rb +27 -139
  32. data/lib/whirled_peas/graphics/content_dimensions.rb +19 -0
  33. data/lib/whirled_peas/graphics/content_painter.rb +20 -0
  34. data/lib/whirled_peas/graphics/debugger.rb +2 -7
  35. data/lib/whirled_peas/graphics/graph_dimensions.rb +12 -0
  36. data/lib/whirled_peas/graphics/graph_painter.rb +104 -0
  37. data/lib/whirled_peas/graphics/grid_painter.rb +0 -3
  38. data/lib/whirled_peas/graphics/scrollbar_helper.rb +126 -0
  39. data/lib/whirled_peas/graphics/text_painter.rb +7 -11
  40. data/lib/whirled_peas/null_logger.rb +0 -1
  41. data/lib/whirled_peas/settings/bg_color.rb +5 -1
  42. data/lib/whirled_peas/settings/border.rb +25 -4
  43. data/lib/whirled_peas/settings/color.rb +8 -4
  44. data/lib/whirled_peas/settings/container_settings.rb +14 -9
  45. data/lib/whirled_peas/settings/debugger.rb +3 -1
  46. data/lib/whirled_peas/settings/element_settings.rb +22 -5
  47. data/lib/whirled_peas/settings/graph_settings.rb +21 -0
  48. data/lib/whirled_peas/settings/grid_settings.rb +7 -0
  49. data/lib/whirled_peas/settings/position.rb +17 -5
  50. data/lib/whirled_peas/settings/text_color.rb +5 -0
  51. data/lib/whirled_peas/settings/text_settings.rb +1 -0
  52. data/lib/whirled_peas/settings/theme.rb +61 -0
  53. data/lib/whirled_peas/settings/theme_library.rb +69 -0
  54. data/lib/whirled_peas/utils/ansi.rb +1 -1
  55. data/lib/whirled_peas/version.rb +1 -1
  56. data/screen_test/components/list_with_active/l2r_active_color.frame +1 -0
  57. data/screen_test/components/list_with_active/l2r_active_color.rb +18 -0
  58. data/screen_test/components/list_with_active/l2r_position_end.frame +1 -0
  59. data/screen_test/components/list_with_active/l2r_position_end.rb +16 -0
  60. data/screen_test/components/list_with_active/l2r_position_middle.frame +1 -0
  61. data/screen_test/components/list_with_active/l2r_position_middle.rb +16 -0
  62. data/screen_test/components/list_with_active/l2r_position_start.frame +1 -0
  63. data/screen_test/components/list_with_active/l2r_position_start.rb +16 -0
  64. data/screen_test/components/list_with_active/l2r_separator.frame +1 -0
  65. data/screen_test/components/list_with_active/l2r_separator.rb +17 -0
  66. data/screen_test/components/list_with_active/t2b_active_color.frame +1 -0
  67. data/screen_test/components/list_with_active/t2b_active_color.rb +19 -0
  68. data/screen_test/components/list_with_active/t2b_position_end.frame +1 -0
  69. data/screen_test/components/list_with_active/t2b_position_end.rb +17 -0
  70. data/screen_test/components/list_with_active/t2b_position_middle.frame +1 -0
  71. data/screen_test/components/list_with_active/t2b_position_middle.rb +17 -0
  72. data/screen_test/components/list_with_active/t2b_position_start.frame +1 -0
  73. data/screen_test/components/list_with_active/t2b_position_start.rb +17 -0
  74. data/screen_test/components/list_with_active/t2b_separator.frame +1 -0
  75. data/screen_test/components/list_with_active/t2b_separator.rb +18 -0
  76. data/screen_test/elements/box.frame +1 -1
  77. data/screen_test/elements/box.rb +1 -1
  78. data/screen_test/elements/graph_asc.frame +1 -0
  79. data/screen_test/elements/graph_asc.rb +12 -0
  80. data/screen_test/elements/graph_desc.frame +1 -0
  81. data/screen_test/elements/graph_desc.rb +12 -0
  82. data/screen_test/elements/graph_horiz.frame +1 -0
  83. data/screen_test/elements/graph_horiz.rb +12 -0
  84. data/screen_test/elements/graph_sin.frame +1 -0
  85. data/screen_test/elements/graph_sin.rb +12 -0
  86. data/screen_test/elements/grid.frame +1 -1
  87. data/screen_test/elements/grid.rb +1 -1
  88. data/screen_test/elements/screen_overflow_x.frame +1 -1
  89. data/screen_test/elements/screen_overflow_x.rb +1 -1
  90. data/screen_test/elements/screen_overflow_y.frame +1 -1
  91. data/screen_test/elements/screen_overflow_y.rb +1 -1
  92. data/screen_test/elements/text.frame +1 -1
  93. data/screen_test/elements/text.rb +1 -1
  94. data/screen_test/elements/text_multiline.frame +1 -1
  95. data/screen_test/elements/text_multiline.rb +1 -1
  96. data/screen_test/elements/theme.frame +1 -0
  97. data/screen_test/elements/theme.rb +26 -0
  98. data/screen_test/settings/align/box_around.frame +1 -1
  99. data/screen_test/settings/align/box_around.rb +1 -1
  100. data/screen_test/settings/align/box_between.frame +1 -1
  101. data/screen_test/settings/align/box_between.rb +1 -1
  102. data/screen_test/settings/align/box_center.frame +1 -1
  103. data/screen_test/settings/align/box_center.rb +1 -1
  104. data/screen_test/settings/align/box_default.frame +1 -1
  105. data/screen_test/settings/align/box_default.rb +1 -1
  106. data/screen_test/settings/align/box_evenly.frame +1 -1
  107. data/screen_test/settings/align/box_evenly.rb +1 -1
  108. data/screen_test/settings/align/box_left.frame +1 -1
  109. data/screen_test/settings/align/box_left.rb +1 -1
  110. data/screen_test/settings/align/box_right.frame +1 -1
  111. data/screen_test/settings/align/box_right.rb +1 -1
  112. data/screen_test/settings/align/children_center.frame +1 -1
  113. data/screen_test/settings/align/children_center.rb +1 -1
  114. data/screen_test/settings/align/children_left.frame +1 -1
  115. data/screen_test/settings/align/children_left.rb +1 -1
  116. data/screen_test/settings/align/children_right.frame +1 -1
  117. data/screen_test/settings/align/children_right.rb +1 -1
  118. data/screen_test/settings/align/grid_center.frame +1 -1
  119. data/screen_test/settings/align/grid_center.rb +1 -1
  120. data/screen_test/settings/align/grid_default.frame +1 -1
  121. data/screen_test/settings/align/grid_default.rb +1 -1
  122. data/screen_test/settings/align/grid_left.frame +1 -1
  123. data/screen_test/settings/align/grid_left.rb +1 -1
  124. data/screen_test/settings/align/grid_right.frame +1 -1
  125. data/screen_test/settings/align/grid_right.rb +1 -1
  126. data/screen_test/settings/ansi/bold.frame +1 -1
  127. data/screen_test/settings/ansi/bold.rb +1 -1
  128. data/screen_test/settings/ansi/color.frame +1 -1
  129. data/screen_test/settings/ansi/color.rb +1 -1
  130. data/screen_test/settings/ansi/underline.frame +1 -1
  131. data/screen_test/settings/ansi/underline.rb +1 -1
  132. data/screen_test/settings/border.frame +1 -1
  133. data/screen_test/settings/border.rb +1 -1
  134. data/screen_test/settings/content_start/box_bottom.frame +1 -0
  135. data/screen_test/settings/content_start/box_bottom.rb +17 -0
  136. data/screen_test/settings/content_start/box_left.frame +1 -0
  137. data/screen_test/settings/{position → content_start}/box_left.rb +2 -2
  138. data/screen_test/settings/content_start/box_left_negative.frame +1 -0
  139. data/screen_test/settings/{position → content_start}/box_left_negative.rb +2 -2
  140. data/screen_test/settings/content_start/box_right.frame +1 -0
  141. data/screen_test/settings/content_start/box_right.rb +17 -0
  142. data/screen_test/settings/content_start/box_top.frame +1 -0
  143. data/screen_test/settings/{position → content_start}/box_top.rb +2 -2
  144. data/screen_test/settings/content_start/box_top_negative.frame +1 -0
  145. data/screen_test/settings/{position → content_start}/box_top_negative.rb +2 -2
  146. data/screen_test/settings/content_start/grid_left.frame +1 -0
  147. data/screen_test/settings/{position → content_start}/grid_left.rb +2 -2
  148. data/screen_test/settings/content_start/grid_left_negative.frame +1 -0
  149. data/screen_test/settings/{position → content_start}/grid_left_negative.rb +2 -2
  150. data/screen_test/settings/content_start/grid_top.frame +1 -0
  151. data/screen_test/settings/{position → content_start}/grid_top.rb +2 -2
  152. data/screen_test/settings/content_start/grid_top_negative.frame +1 -0
  153. data/screen_test/settings/{position → content_start}/grid_top_negative.rb +2 -2
  154. data/screen_test/settings/flow/box_b2t.frame +1 -1
  155. data/screen_test/settings/flow/box_b2t.rb +1 -1
  156. data/screen_test/settings/flow/box_l2r.frame +1 -1
  157. data/screen_test/settings/flow/box_l2r.rb +1 -1
  158. data/screen_test/settings/flow/box_r2l.frame +1 -1
  159. data/screen_test/settings/flow/box_r2l.rb +1 -1
  160. data/screen_test/settings/flow/box_t2b.frame +1 -1
  161. data/screen_test/settings/flow/box_t2b.rb +1 -1
  162. data/screen_test/settings/flow/grid_b2t.frame +1 -1
  163. data/screen_test/settings/flow/grid_b2t.rb +1 -1
  164. data/screen_test/settings/flow/grid_l2r.frame +1 -1
  165. data/screen_test/settings/flow/grid_l2r.rb +1 -1
  166. data/screen_test/settings/flow/grid_r2l.frame +1 -1
  167. data/screen_test/settings/flow/grid_r2l.rb +1 -1
  168. data/screen_test/settings/flow/grid_t2b.frame +1 -1
  169. data/screen_test/settings/flow/grid_t2b.rb +1 -1
  170. data/screen_test/settings/height/box.frame +1 -1
  171. data/screen_test/settings/height/box.rb +1 -1
  172. data/screen_test/settings/height/box_border_sizing.frame +1 -1
  173. data/screen_test/settings/height/box_border_sizing.rb +1 -1
  174. data/screen_test/settings/height/grid.frame +1 -1
  175. data/screen_test/settings/height/grid.rb +1 -1
  176. data/screen_test/settings/height/overflow_box.frame +1 -1
  177. data/screen_test/settings/height/overflow_box.rb +1 -1
  178. data/screen_test/settings/height/overflow_box_l2r.frame +1 -1
  179. data/screen_test/settings/height/overflow_box_l2r.rb +1 -1
  180. data/screen_test/settings/height/overflow_box_t2b.frame +1 -1
  181. data/screen_test/settings/height/overflow_box_t2b.rb +1 -1
  182. data/screen_test/settings/height/overflow_grid.frame +1 -1
  183. data/screen_test/settings/height/overflow_grid.rb +1 -1
  184. data/screen_test/settings/margin.frame +1 -1
  185. data/screen_test/settings/margin.rb +1 -1
  186. data/screen_test/settings/padding.frame +1 -1
  187. data/screen_test/settings/padding.rb +1 -1
  188. data/screen_test/settings/scroll/horiz_box.frame +1 -1
  189. data/screen_test/settings/scroll/horiz_box.rb +3 -5
  190. data/screen_test/settings/scroll/horiz_box_align_center.rb +3 -5
  191. data/screen_test/settings/scroll/horiz_box_align_right.rb +3 -5
  192. data/screen_test/settings/scroll/vert_box.frame +1 -1
  193. data/screen_test/settings/scroll/vert_box.rb +6 -8
  194. data/screen_test/settings/title_font.frame +1 -1
  195. data/screen_test/settings/title_font.rb +1 -1
  196. data/screen_test/settings/valign/box_around.frame +1 -1
  197. data/screen_test/settings/valign/box_around.rb +1 -1
  198. data/screen_test/settings/valign/box_between.frame +1 -1
  199. data/screen_test/settings/valign/box_between.rb +1 -1
  200. data/screen_test/settings/valign/box_bottom.frame +1 -1
  201. data/screen_test/settings/valign/box_bottom.rb +1 -1
  202. data/screen_test/settings/valign/box_default.frame +1 -1
  203. data/screen_test/settings/valign/box_default.rb +1 -1
  204. data/screen_test/settings/valign/box_evenly.frame +1 -1
  205. data/screen_test/settings/valign/box_evenly.rb +1 -1
  206. data/screen_test/settings/valign/box_middle.frame +1 -1
  207. data/screen_test/settings/valign/box_middle.rb +1 -1
  208. data/screen_test/settings/valign/box_top.frame +1 -1
  209. data/screen_test/settings/valign/box_top.rb +1 -1
  210. data/screen_test/settings/valign/grid_bottom.frame +1 -1
  211. data/screen_test/settings/valign/grid_bottom.rb +1 -1
  212. data/screen_test/settings/valign/grid_default.frame +1 -1
  213. data/screen_test/settings/valign/grid_default.rb +1 -1
  214. data/screen_test/settings/valign/grid_middle.frame +1 -1
  215. data/screen_test/settings/valign/grid_middle.rb +1 -1
  216. data/screen_test/settings/valign/grid_top.frame +1 -1
  217. data/screen_test/settings/valign/grid_top.rb +1 -1
  218. data/screen_test/settings/width/box_border_sizing.frame +1 -1
  219. data/screen_test/settings/width/box_border_sizing.rb +1 -1
  220. data/screen_test/settings/width/box_content.frame +1 -1
  221. data/screen_test/settings/width/box_content.rb +1 -1
  222. data/screen_test/settings/width/box_default.frame +1 -1
  223. data/screen_test/settings/width/box_default.rb +1 -1
  224. data/screen_test/settings/width/grid.frame +1 -1
  225. data/screen_test/settings/width/grid.rb +1 -1
  226. data/screen_test/settings/width/overflow_align_center.frame +1 -1
  227. data/screen_test/settings/width/overflow_align_center.rb +1 -1
  228. data/screen_test/settings/width/overflow_align_right.frame +1 -1
  229. data/screen_test/settings/width/overflow_align_right.rb +1 -1
  230. data/screen_test/settings/width/overflow_box.frame +1 -1
  231. data/screen_test/settings/width/overflow_box.rb +1 -1
  232. data/screen_test/settings/width/overflow_box_l2r.frame +1 -1
  233. data/screen_test/settings/width/overflow_box_l2r.rb +1 -1
  234. data/screen_test/settings/width/overflow_box_t2b.frame +1 -1
  235. data/screen_test/settings/width/overflow_box_t2b.rb +1 -1
  236. data/screen_test/settings/width/overflow_grid.frame +1 -1
  237. data/screen_test/settings/width/overflow_grid.rb +1 -1
  238. data/tools/whirled_peas/tools/screen_tester.rb +15 -1
  239. metadata +75 -20
  240. data/lib/whirled_peas/graphics/mock_screen.rb +0 -26
  241. data/lib/whirled_peas/graphics/text_dimensions.rb +0 -15
  242. data/screen_test/settings/position/box_left.frame +0 -1
  243. data/screen_test/settings/position/box_left_negative.frame +0 -1
  244. data/screen_test/settings/position/box_top.frame +0 -1
  245. data/screen_test/settings/position/box_top_negative.frame +0 -1
  246. data/screen_test/settings/position/grid_left.frame +0 -1
  247. data/screen_test/settings/position/grid_left_negative.frame +0 -1
  248. data/screen_test/settings/position/grid_top.frame +0 -1
  249. data/screen_test/settings/position/grid_top_negative.frame +0 -1
@@ -0,0 +1,88 @@
1
+ module WhirledPeas
2
+ module Component
3
+ class ListWithActive
4
+ attr_accessor :active_index, :separator
5
+
6
+ attr_reader :items
7
+
8
+ attr_writer :flow, :viewport_size
9
+
10
+ def items=(value)
11
+ @items = value.map(&:to_s)
12
+ end
13
+
14
+ def flow
15
+ @flow || :l2r
16
+ end
17
+
18
+ def total_size
19
+ @total_size ||= if flow == :l2r
20
+ size = separator.nil? ? 0 : (items.count - 1) * separator.length
21
+ items.each { |item| size += item.length }
22
+ size
23
+ else
24
+ items.count + (separator.nil? ? 0 : items.count - 1)
25
+ end
26
+ end
27
+
28
+ def viewport_size
29
+ @viewport_size || total_size
30
+ end
31
+
32
+ def compose(composer, settings)
33
+ %i[items active_index].each do |required_attr|
34
+ if send(required_attr).nil?
35
+ raise ArugmentError, "Require field #{required_attr} missing"
36
+ end
37
+ end
38
+
39
+ composer.add_box('ListWithActive') do |composer, settings|
40
+ settings.flow = flow
41
+ settings.align = :left
42
+ settings.full_border
43
+ if flow == :l2r
44
+ settings.width = viewport_size
45
+ active_start = separator.nil? ? 0 : active_index * separator.length
46
+ items.first(active_index).each do |item|
47
+ active_start += item.length
48
+ end
49
+ active_size = items[active_index].length
50
+ else
51
+ settings.height = viewport_size
52
+ active_start = active_index + (separator.nil? ? 0 : active_index)
53
+ active_size = 1
54
+ end
55
+
56
+ if viewport_size < total_size
57
+ front_padding = (viewport_size - active_size) * 0.667
58
+ offset = (active_start - front_padding).round
59
+ if offset < 0
60
+ offset = 0
61
+ elsif offset > total_size - viewport_size
62
+ offset = total_size - viewport_size
63
+ end
64
+
65
+ if flow == :l2r
66
+ settings.content_start.left = -offset
67
+ settings.scrollbar.horiz = true
68
+ else
69
+ settings.content_start.top = -offset
70
+ settings.scrollbar.vert = true
71
+ end
72
+ end
73
+
74
+ items.each.with_index do |item, index|
75
+ composer.add_text { separator } if !separator.nil? && index > 0
76
+ composer.add_text do |_, settings|
77
+ if index == active_index
78
+ settings.bg_color = :highlight
79
+ settings.color = :highlight
80
+ end
81
+ item
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -55,8 +55,8 @@ module WhirledPeas
55
55
  private
56
56
 
57
57
  def paint_horizontally(canvas, canvas_coords, &block)
58
- stroke_left = canvas_coords.content_left
59
- stroke_top = canvas_coords.content_top
58
+ stroke_left = canvas_coords.offset_content_left
59
+ stroke_top = canvas_coords.offset_content_top
60
60
  children_width = 0
61
61
  each_child { |c| children_width += c.dimensions.outer_width }
62
62
  left_offset, spacing_offset = horiz_justify_offset(children_width)
@@ -76,8 +76,8 @@ module WhirledPeas
76
76
  end
77
77
 
78
78
  def paint_vertically(canvas, canvas_coords, &block)
79
- stroke_left = canvas_coords.content_left
80
- stroke_top = canvas_coords.content_top
79
+ stroke_left = canvas_coords.offset_content_left
80
+ stroke_top = canvas_coords.offset_content_top
81
81
  children_height = 0
82
82
  each_child { |c| children_height += c.dimensions.outer_height }
83
83
  top_offset, spacing_offset = vert_justify_offset(children_height)
@@ -1,8 +1,11 @@
1
1
  require 'whirled_peas/settings/box_settings'
2
+ require 'whirled_peas/settings/graph_settings'
2
3
  require 'whirled_peas/settings/grid_settings'
3
4
  require 'whirled_peas/settings/text_settings'
5
+ require 'whirled_peas/settings/theme_library'
4
6
 
5
7
  require_relative 'box_painter'
8
+ require_relative 'graph_painter'
6
9
  require_relative 'grid_painter'
7
10
  require_relative 'text_painter'
8
11
 
@@ -23,8 +26,10 @@ module WhirledPeas
23
26
  "Element-#{@counter}"
24
27
  end
25
28
 
26
- def self.build
27
- settings = Settings::BoxSettings.new
29
+ def self.build(theme_name=nil, &block)
30
+ theme_name ||= Settings::ThemeLibrary.default_name
31
+ theme = Settings::ThemeLibrary.get(theme_name)
32
+ settings = Settings::BoxSettings.new(theme)
28
33
  template = BoxPainter.new('TEMPLATE', settings)
29
34
  composer = Composer.new(template)
30
35
  value = yield composer, settings
@@ -45,6 +50,7 @@ module WhirledPeas
45
50
  child = TextPainter.new(name, child_settings)
46
51
  # TextPainters are not composable, so yield nil
47
52
  content = yield nil, child_settings
53
+ child_settings.validate!
48
54
  unless self.class.stringable?(content)
49
55
  raise ArgumentError, "Unsupported type for text: #{content.class}"
50
56
  end
@@ -52,11 +58,29 @@ module WhirledPeas
52
58
  painter.add_child(child)
53
59
  end
54
60
 
61
+ def add_graph(name=self.class.next_name, &block)
62
+ child_settings = Settings::GraphSettings.inherit(painter.settings)
63
+ child = GraphPainter.new(name, child_settings)
64
+ # GraphPainters are not composable, so yield nil
65
+ content = yield nil, child_settings
66
+ child_settings.validate!
67
+ unless content.is_a?(Array) && content.length > 0
68
+ raise ArgumentError, 'Graphs require a non-empty array as the content'
69
+ end
70
+ child.content = content
71
+ painter.add_child(child)
72
+ end
73
+
74
+ def add_component(component)
75
+ component.compose(self, settings)
76
+ end
77
+
55
78
  def add_box(name=self.class.next_name, &block)
56
79
  child_settings = Settings::BoxSettings.inherit(painter.settings)
57
80
  child = BoxPainter.new(name, child_settings)
58
81
  composer = self.class.new(child)
59
82
  value = yield composer, child.settings
83
+ child_settings.validate!
60
84
  painter.add_child(child)
61
85
  if !child.children? && self.class.stringable?(value)
62
86
  composer.add_text("#{name}-Text") { value.to_s }
@@ -68,6 +92,7 @@ module WhirledPeas
68
92
  child = GridPainter.new(name, child_settings)
69
93
  composer = self.class.new(child)
70
94
  values = yield composer, child.settings
95
+ child_settings.validate!
71
96
  painter.add_child(child)
72
97
  if !child.children? && values.is_a?(Array)
73
98
  values.each.with_index do |value, index|
@@ -9,11 +9,11 @@ module WhirledPeas
9
9
  end
10
10
 
11
11
  def left
12
- start_left + settings.position.left
12
+ start_left
13
13
  end
14
14
 
15
15
  def top
16
- start_top + settings.position.top
16
+ start_top
17
17
  end
18
18
 
19
19
  def border_left
@@ -33,35 +33,33 @@ module WhirledPeas
33
33
  end
34
34
 
35
35
  def content_left(col_index=0)
36
- padding_left + settings.padding.left + col_index * grid_width
36
+ padding_left + settings.padding.left + col_index * dimensions.grid_width
37
37
  end
38
38
 
39
- def content_top(row_index=0)
40
- padding_top + settings.padding.top + row_index * grid_height
41
- end
42
-
43
- def inner_grid_width
44
- settings.padding.left +
45
- dimensions.content_width +
46
- settings.padding.right
39
+ def offset_content_left(col_index=0)
40
+ if settings.content_start.left
41
+ content_left(col_index) + settings.content_start.left
42
+ elsif settings.content_start.right
43
+ left_offset = dimensions.content_width - dimensions.children_width
44
+ content_left(col_index) + left_offset - settings.content_start.right
45
+ else
46
+ content_left(col_index)
47
+ end
47
48
  end
48
49
 
49
- def grid_width
50
- (settings.border.inner_vert? ? 1 : 0) +
51
- inner_grid_width +
52
- (settings.scrollbar.vert? ? 1 : 0)
53
- end
54
-
55
- def inner_grid_height
56
- settings.padding.top +
57
- dimensions.content_height +
58
- settings.padding.bottom
50
+ def content_top(row_index=0)
51
+ padding_top + settings.padding.top + row_index * dimensions.grid_height
59
52
  end
60
53
 
61
- def grid_height
62
- (settings.border.inner_horiz? ? 1 : 0) +
63
- inner_grid_height +
64
- (settings.scrollbar.horiz? ? 1 : 0)
54
+ def offset_content_top(row_index=0)
55
+ if settings.content_start.top
56
+ content_top(row_index) + settings.content_start.top
57
+ elsif settings.content_start.bottom
58
+ top_offset = dimensions.content_height - dimensions.children_height
59
+ content_top(row_index) + top_offset - settings.content_start.bottom
60
+ else
61
+ content_top(row_index)
62
+ end
65
63
  end
66
64
 
67
65
  private
@@ -31,6 +31,30 @@ module WhirledPeas
31
31
  end
32
32
  end
33
33
 
34
+ def inner_grid_width
35
+ settings.padding.left +
36
+ content_width +
37
+ settings.padding.right
38
+ end
39
+
40
+ def grid_width
41
+ (settings.border.inner_vert? ? 1 : 0) +
42
+ inner_grid_width +
43
+ (settings.scrollbar.vert? ? 1 : 0)
44
+ end
45
+
46
+ def inner_grid_height
47
+ settings.padding.top +
48
+ content_height +
49
+ settings.padding.bottom
50
+ end
51
+
52
+ def grid_height
53
+ (settings.border.inner_horiz? ? 1 : 0) +
54
+ inner_grid_height +
55
+ (settings.scrollbar.horiz? ? 1 : 0)
56
+ end
57
+
34
58
  def outer_width
35
59
  @outer_width ||= margin_width +
36
60
  outer_border_width +
@@ -2,6 +2,7 @@ require 'whirled_peas/utils/formatted_string'
2
2
 
3
3
  require_relative 'container_coords'
4
4
  require_relative 'painter'
5
+ require_relative 'scrollbar_helper'
5
6
 
6
7
  module WhirledPeas
7
8
  module Graphics
@@ -34,11 +35,17 @@ module WhirledPeas
34
35
 
35
36
  # Paint the top border if the settings call for it
36
37
  if settings.border.top?
37
- canvas.stroke(stroke_left, stroke_top, top_border_stroke(canvas_coords), formatting, &block)
38
+ canvas.stroke(stroke_left, stroke_top, top_border_stroke, formatting, &block)
38
39
  stroke_top += 1
39
40
  end
40
41
  # Precalculate the middle border container grids with more than 1 row
41
- middle_border = dimensions.num_rows > 1 ? middle_border_stroke(canvas_coords) : ''
42
+ middle_border = dimensions.num_rows > 1 ? middle_border_stroke : ''
43
+
44
+ vert_scrollbar = settings.scrollbar.vert? ? ScrollbarHelper.vert(
45
+ dimensions.children_height + dimensions.padding_height,
46
+ dimensions.inner_grid_height,
47
+ canvas_coords.content_top - canvas_coords.offset_content_top,
48
+ ) : []
42
49
 
43
50
  # Paint each grid row by row
44
51
  dimensions.num_rows.times do |row_num|
@@ -52,8 +59,9 @@ module WhirledPeas
52
59
 
53
60
  # Paint the interior of each row (horizontal borders, veritical scroll bar and
54
61
  # background color for the padding and content area)
55
- canvas_coords.inner_grid_height.times do |row_within_cell|
56
- canvas.stroke(stroke_left, stroke_top, content_line_stroke(canvas_coords, row_within_cell), formatting, &block)
62
+ dimensions.inner_grid_height.times do |row_within_cell|
63
+ content_line = content_line_stroke(row_within_cell, vert_scrollbar)
64
+ canvas.stroke(stroke_left, stroke_top, content_line, formatting, &block)
57
65
  stroke_top += 1
58
66
  end
59
67
 
@@ -66,7 +74,7 @@ module WhirledPeas
66
74
 
67
75
  # Paint the bottom border if the settings call for it
68
76
  if settings.border.bottom?
69
- canvas.stroke(stroke_left, stroke_top, bottom_border_stroke(canvas_coords), formatting, &block)
77
+ canvas.stroke(stroke_left, stroke_top, bottom_border_stroke, formatting, &block)
70
78
  stroke_top += 1
71
79
  end
72
80
  end
@@ -176,59 +184,49 @@ module WhirledPeas
176
184
  end
177
185
 
178
186
  # Return the stroke for the top border
179
- def top_border_stroke(canvas_coords)
187
+ def top_border_stroke
180
188
  line_stroke(
181
189
  settings.border.style.top_left,
182
190
  settings.border.style.top_junc,
183
191
  settings.border.style.top_right
184
192
  ) do
185
- settings.border.style.top_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
193
+ settings.border.style.top_horiz * (dimensions.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
186
194
  end
187
195
  end
188
196
 
189
197
  # Return the stroke for an inner horizontal border
190
- def middle_border_stroke(canvas_coords)
198
+ def middle_border_stroke
191
199
  line_stroke(
192
200
  settings.border.style.left_junc,
193
201
  settings.border.style.cross_junc,
194
202
  settings.border.style.right_junc
195
203
  ) do
196
- settings.border.style.middle_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
204
+ settings.border.style.middle_horiz * (dimensions.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
197
205
  end
198
206
  end
199
207
 
200
208
  # Return the stroke for the bottom border
201
- def bottom_border_stroke(canvas_coords)
209
+ def bottom_border_stroke
202
210
  line_stroke(
203
211
  settings.border.style.bottom_left,
204
212
  settings.border.style.bottom_junc,
205
213
  settings.border.style.bottom_right
206
214
  ) do
207
- settings.border.style.bottom_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
215
+ settings.border.style.bottom_horiz * (dimensions.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
208
216
  end
209
217
  end
210
218
 
211
219
  # Return the stroke for a grid row between any borders
212
- def content_line_stroke(canvas_coords, row_within_cell)
220
+ def content_line_stroke(row_within_cell, vert_scrollbar)
213
221
  line_stroke(
214
222
  settings.border.style.left_vert,
215
223
  settings.border.style.middle_vert,
216
224
  settings.border.style.right_vert,
217
225
  ) do
218
226
  if settings.scrollbar.vert?
219
- if dimensions.children_height <= canvas_coords.grid_height || children.first.settings.position.top > 0
220
- scrollbar_char = GUTTER
221
- else
222
- scrollbar_char = vert_scroll_char(
223
- dimensions.children_height + dimensions.padding_height,
224
- canvas_coords.inner_grid_height,
225
- -children.first.settings.position.top,
226
- row_within_cell
227
- )
228
- end
229
- PADDING * canvas_coords.inner_grid_width + scrollbar_char
227
+ PADDING * dimensions.inner_grid_width + vert_scrollbar[row_within_cell]
230
228
  else
231
- PADDING * canvas_coords.inner_grid_width
229
+ PADDING * dimensions.inner_grid_width
232
230
  end
233
231
  end
234
232
  end
@@ -240,121 +238,11 @@ module WhirledPeas
240
238
  settings.border.style.middle_vert,
241
239
  settings.border.style.right_vert,
242
240
  ) do
243
- canvas_coords.inner_grid_width.times.map do |col_within_cell|
244
- horiz_scroll_char(
245
- dimensions.children_width + dimensions.padding_width,
246
- canvas_coords.inner_grid_width,
247
- -children.first.settings.position.left,
248
- col_within_cell
249
- )
250
- end.join
251
- end
252
- end
253
-
254
- # Contants to paint scrollbars
255
- GUTTER = ' '
256
- HORIZONTAL = %w[▗ ▄ ▖]
257
- VERTICAL = %w[
258
-
259
-
260
-
261
- ]
262
-
263
- # Determine the character to paint the horizontal scroll bar with for the given column
264
- #
265
- # @see #scroll_char for more details
266
- def horiz_scroll_char(col_count, viewable_col_count, first_visible_col, curr_col)
267
- scroll_char(col_count, viewable_col_count, first_visible_col, curr_col, HORIZONTAL)
268
- end
269
-
270
- # Determine the character to paint the vertical scroll bar with for the given row
271
- #
272
- # @see #scroll_char for more details
273
- def vert_scroll_char(row_count, viewable_row_count, first_visible_row, curr_row)
274
- scroll_char(row_count, viewable_row_count, first_visible_row, curr_row, VERTICAL)
275
- end
276
-
277
- private
278
-
279
- # Determine which character to paint a for a scrollbar
280
- #
281
- # @param total_count [Integer] total number of rows/columns in the content
282
- # @param viewable_count [Integer] number of rows/columns visible in the viewport
283
- # @param first_visible [Integer] zero-based index of the first row/column that is visible
284
- # in the viewport
285
- # @param curr [Integer] zero-based index of the row/column (relative to the first visible
286
- # row/column) that the painted character is being requested for
287
- # @param chars [Array<String>] an array with three 1-character strings, the frist is the
288
- # "second half" scrollbar character, the second is the "full" scrollbar character, and
289
- # the third is the "first half" scrollbar character.
290
- def scroll_char(total_count, viewable_count, first_visible, curr, chars)
291
- return GUTTER unless total_count > 0 && viewable_count > 0
292
- # The scroll handle has the exact same relative size and position in the scroll gutter
293
- # that the viewable content has in the total content area. For example, a content area
294
- # that is 50 columns wide with a view port that is 20 columns wide might look like
295
- #
296
- # +---------1-----****2*********3******---4---------+
297
- # | * * |
298
- # | hidden * viewable * hidden |
299
- # | * * |
300
- # +---------1-----****2*********3******---4---------+
301
- #
302
- # The scoll gutter, would look like
303
- #
304
- # |......********.....|
305
- #
306
- # Scrolling all the way to the right results in
307
- #
308
- # +---------1---------2---------3*********4*********+
309
- # | * *
310
- # | hidden * viewable *
311
- # | * *
312
- # +---------1---------2---------3*********4*********+
313
- # |...........********|
314
- #
315
- # Returning to the first example, we can match up the arguments to this method to the
316
- # diagram
317
- #
318
- # total_count = 50
319
- # |<----------------------------------------------->|
320
- # | |
321
- # | veiwable_count = 20 |
322
- # | |<----------------->| |
323
- # ↓ ↓ ↓ ↓
324
- # +---------1-----****2*********3******---4---------+
325
- # | * * |
326
- # | hidden * viewable * hidden |
327
- # | * * |
328
- # +---------1-----****2*********3******---4---------+
329
- # |......****?***.....|
330
- # ↑ ↑
331
- # first_visible = 16 |
332
- # curr = 11
333
-
334
- # The first task of determining how much of the handle is visible in a row/column is to
335
- # calculate the range (as a precentage of the total) of viewable items
336
- viewable_start = first_visible.to_f / total_count
337
- viewable_end = (first_visible + viewable_count).to_f / total_count
338
-
339
- # Always use the same length for the scroll bar so it does not give an inchworm effect
340
- # as it scrolls along.
341
- #
342
- # Also, double the value now to get granularity for half width
343
- # scrollbar characters.
344
- scrollbar_length = ((2 * viewable_count ** 2).to_f / total_count).ceil
345
- scrollbar_start = ((2 * first_visible * viewable_count).to_f / total_count).floor
346
-
347
- first_half = (scrollbar_start...scrollbar_start + scrollbar_length).include?(2 * curr)
348
- second_half = (scrollbar_start...scrollbar_start + scrollbar_length).include?(2 * curr + 1)
349
-
350
- if first_half && second_half
351
- chars[1]
352
- elsif first_half
353
- chars[2]
354
- elsif second_half
355
- chars[0]
356
- else
357
- GUTTER
241
+ ScrollbarHelper.horiz(
242
+ dimensions.children_width + dimensions.padding_width,
243
+ dimensions.inner_grid_width,
244
+ canvas_coords.content_left - canvas_coords.offset_content_left
245
+ ).join
358
246
  end
359
247
  end
360
248
  end