whirled_peas 0.4.1 → 0.8.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 (238) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +37 -0
  5. data/README.md +529 -156
  6. data/Rakefile +9 -3
  7. data/bin/reset_cursor +11 -0
  8. data/bin/screen_test +68 -0
  9. data/examples/intro.rb +52 -0
  10. data/examples/scrolling.rb +54 -0
  11. data/lib/whirled_peas.rb +6 -12
  12. data/lib/whirled_peas/animator.rb +5 -0
  13. data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
  14. data/lib/whirled_peas/animator/easing.rb +72 -0
  15. data/lib/whirled_peas/animator/frame.rb +5 -0
  16. data/lib/whirled_peas/animator/frameset.rb +33 -0
  17. data/lib/whirled_peas/animator/producer.rb +35 -0
  18. data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
  19. data/lib/whirled_peas/command.rb +5 -0
  20. data/lib/whirled_peas/command/base.rb +86 -0
  21. data/lib/whirled_peas/command/config_command.rb +44 -0
  22. data/lib/whirled_peas/command/debug.rb +21 -0
  23. data/lib/whirled_peas/command/fonts.rb +22 -0
  24. data/lib/whirled_peas/command/frame_command.rb +34 -0
  25. data/lib/whirled_peas/command/frames.rb +24 -0
  26. data/lib/whirled_peas/command/help.rb +38 -0
  27. data/lib/whirled_peas/command/play.rb +108 -0
  28. data/lib/whirled_peas/command/record.rb +57 -0
  29. data/lib/whirled_peas/command/still.rb +29 -0
  30. data/lib/whirled_peas/command_line.rb +23 -228
  31. data/lib/whirled_peas/config.rb +56 -6
  32. data/lib/whirled_peas/device.rb +5 -0
  33. data/lib/whirled_peas/device/null_device.rb +8 -0
  34. data/lib/whirled_peas/device/output_file.rb +19 -0
  35. data/lib/whirled_peas/device/screen.rb +26 -0
  36. data/lib/whirled_peas/errors.rb +2 -0
  37. data/lib/whirled_peas/graphics.rb +19 -0
  38. data/lib/whirled_peas/graphics/box_painter.rb +101 -0
  39. data/lib/whirled_peas/graphics/canvas.rb +118 -0
  40. data/lib/whirled_peas/graphics/composer.rb +80 -0
  41. data/lib/whirled_peas/graphics/container_coords.rb +72 -0
  42. data/lib/whirled_peas/graphics/container_dimensions.rb +93 -0
  43. data/lib/whirled_peas/graphics/container_painter.rb +363 -0
  44. data/lib/whirled_peas/graphics/debugger.rb +52 -0
  45. data/lib/whirled_peas/graphics/grid_painter.rb +69 -0
  46. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  47. data/lib/whirled_peas/graphics/painter.rb +33 -0
  48. data/lib/whirled_peas/graphics/renderer.rb +32 -0
  49. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  50. data/lib/whirled_peas/graphics/text_painter.rb +40 -0
  51. data/lib/whirled_peas/settings.rb +5 -0
  52. data/lib/whirled_peas/settings/alignment.rb +24 -0
  53. data/lib/whirled_peas/settings/bg_color.rb +24 -0
  54. data/lib/whirled_peas/settings/border.rb +101 -0
  55. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  56. data/lib/whirled_peas/settings/color.rb +68 -0
  57. data/lib/whirled_peas/settings/container_settings.rb +223 -0
  58. data/lib/whirled_peas/settings/debugger.rb +96 -0
  59. data/lib/whirled_peas/settings/display_flow.rb +27 -0
  60. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  61. data/lib/whirled_peas/settings/grid_settings.rb +19 -0
  62. data/lib/whirled_peas/settings/margin.rb +8 -0
  63. data/lib/whirled_peas/settings/padding.rb +8 -0
  64. data/lib/whirled_peas/settings/position.rb +15 -0
  65. data/lib/whirled_peas/settings/scrollbar.rb +15 -0
  66. data/lib/whirled_peas/settings/sizing.rb +19 -0
  67. data/lib/whirled_peas/settings/spacing.rb +58 -0
  68. data/lib/whirled_peas/settings/text_color.rb +21 -0
  69. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  70. data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
  71. data/lib/whirled_peas/utils/ansi.rb +19 -56
  72. data/lib/whirled_peas/utils/file_handler.rb +57 -0
  73. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  74. data/lib/whirled_peas/utils/title_font.rb +1 -1
  75. data/lib/whirled_peas/version.rb +1 -1
  76. data/screen_test/elements/box.frame +1 -0
  77. data/screen_test/elements/box.rb +20 -0
  78. data/screen_test/elements/grid.frame +1 -0
  79. data/screen_test/elements/grid.rb +13 -0
  80. data/screen_test/elements/screen_overflow_x.frame +1 -0
  81. data/screen_test/elements/screen_overflow_x.rb +9 -0
  82. data/screen_test/elements/screen_overflow_y.frame +1 -0
  83. data/screen_test/elements/screen_overflow_y.rb +9 -0
  84. data/screen_test/elements/text.frame +1 -0
  85. data/screen_test/elements/text.rb +9 -0
  86. data/screen_test/elements/text_multiline.frame +1 -0
  87. data/screen_test/elements/text_multiline.rb +9 -0
  88. data/screen_test/settings/align/box_around.frame +1 -0
  89. data/screen_test/settings/align/box_around.rb +16 -0
  90. data/screen_test/settings/align/box_between.frame +1 -0
  91. data/screen_test/settings/align/box_between.rb +16 -0
  92. data/screen_test/settings/align/box_center.frame +1 -0
  93. data/screen_test/settings/align/box_center.rb +21 -0
  94. data/screen_test/settings/align/box_default.frame +1 -0
  95. data/screen_test/settings/align/box_default.rb +20 -0
  96. data/screen_test/settings/align/box_evenly.frame +1 -0
  97. data/screen_test/settings/align/box_evenly.rb +16 -0
  98. data/screen_test/settings/align/box_left.frame +1 -0
  99. data/screen_test/settings/align/box_left.rb +21 -0
  100. data/screen_test/settings/align/box_right.frame +1 -0
  101. data/screen_test/settings/align/box_right.rb +21 -0
  102. data/screen_test/settings/align/children_center.frame +1 -0
  103. data/screen_test/settings/align/children_center.rb +15 -0
  104. data/screen_test/settings/align/children_left.frame +1 -0
  105. data/screen_test/settings/align/children_left.rb +15 -0
  106. data/screen_test/settings/align/children_right.frame +1 -0
  107. data/screen_test/settings/align/children_right.rb +15 -0
  108. data/screen_test/settings/align/grid_center.frame +1 -0
  109. data/screen_test/settings/align/grid_center.rb +18 -0
  110. data/screen_test/settings/align/grid_default.frame +1 -0
  111. data/screen_test/settings/align/grid_default.rb +17 -0
  112. data/screen_test/settings/align/grid_left.frame +1 -0
  113. data/screen_test/settings/align/grid_left.rb +18 -0
  114. data/screen_test/settings/align/grid_right.frame +1 -0
  115. data/screen_test/settings/align/grid_right.rb +18 -0
  116. data/screen_test/settings/ansi/bold.frame +1 -0
  117. data/screen_test/settings/ansi/bold.rb +14 -0
  118. data/screen_test/settings/ansi/color.frame +1 -0
  119. data/screen_test/settings/ansi/color.rb +37 -0
  120. data/screen_test/settings/ansi/underline.frame +1 -0
  121. data/screen_test/settings/ansi/underline.rb +14 -0
  122. data/screen_test/settings/border.frame +1 -0
  123. data/screen_test/settings/border.rb +13 -0
  124. data/screen_test/settings/flow/box_b2t.frame +1 -0
  125. data/screen_test/settings/flow/box_b2t.rb +26 -0
  126. data/screen_test/settings/flow/box_l2r.frame +1 -0
  127. data/screen_test/settings/flow/box_l2r.rb +26 -0
  128. data/screen_test/settings/flow/box_r2l.frame +1 -0
  129. data/screen_test/settings/flow/box_r2l.rb +26 -0
  130. data/screen_test/settings/flow/box_t2b.frame +1 -0
  131. data/screen_test/settings/flow/box_t2b.rb +26 -0
  132. data/screen_test/settings/flow/grid_b2t.frame +1 -0
  133. data/screen_test/settings/flow/grid_b2t.rb +14 -0
  134. data/screen_test/settings/flow/grid_l2r.frame +1 -0
  135. data/screen_test/settings/flow/grid_l2r.rb +14 -0
  136. data/screen_test/settings/flow/grid_r2l.frame +1 -0
  137. data/screen_test/settings/flow/grid_r2l.rb +14 -0
  138. data/screen_test/settings/flow/grid_t2b.frame +1 -0
  139. data/screen_test/settings/flow/grid_t2b.rb +14 -0
  140. data/screen_test/settings/height/box.frame +1 -0
  141. data/screen_test/settings/height/box.rb +13 -0
  142. data/screen_test/settings/height/box_border_sizing.frame +1 -0
  143. data/screen_test/settings/height/box_border_sizing.rb +15 -0
  144. data/screen_test/settings/height/grid.frame +1 -0
  145. data/screen_test/settings/height/grid.rb +14 -0
  146. data/screen_test/settings/height/overflow_box.frame +1 -0
  147. data/screen_test/settings/height/overflow_box.rb +13 -0
  148. data/screen_test/settings/height/overflow_box_l2r.frame +1 -0
  149. data/screen_test/settings/height/overflow_box_l2r.rb +17 -0
  150. data/screen_test/settings/height/overflow_box_t2b.frame +1 -0
  151. data/screen_test/settings/height/overflow_box_t2b.rb +16 -0
  152. data/screen_test/settings/height/overflow_grid.frame +1 -0
  153. data/screen_test/settings/height/overflow_grid.rb +16 -0
  154. data/screen_test/settings/margin.frame +1 -0
  155. data/screen_test/settings/margin.rb +16 -0
  156. data/screen_test/settings/padding.frame +1 -0
  157. data/screen_test/settings/padding.rb +13 -0
  158. data/screen_test/settings/position/box_left.frame +1 -0
  159. data/screen_test/settings/position/box_left.rb +17 -0
  160. data/screen_test/settings/position/box_left_negative.frame +1 -0
  161. data/screen_test/settings/position/box_left_negative.rb +17 -0
  162. data/screen_test/settings/position/box_top.frame +1 -0
  163. data/screen_test/settings/position/box_top.rb +17 -0
  164. data/screen_test/settings/position/box_top_negative.frame +1 -0
  165. data/screen_test/settings/position/box_top_negative.rb +17 -0
  166. data/screen_test/settings/position/grid_left.frame +1 -0
  167. data/screen_test/settings/position/grid_left.rb +18 -0
  168. data/screen_test/settings/position/grid_left_negative.frame +1 -0
  169. data/screen_test/settings/position/grid_left_negative.rb +18 -0
  170. data/screen_test/settings/position/grid_top.frame +1 -0
  171. data/screen_test/settings/position/grid_top.rb +18 -0
  172. data/screen_test/settings/position/grid_top_negative.frame +1 -0
  173. data/screen_test/settings/position/grid_top_negative.rb +18 -0
  174. data/screen_test/settings/scroll/horiz_box.frame +1 -0
  175. data/screen_test/settings/scroll/horiz_box.rb +17 -0
  176. data/screen_test/settings/scroll/horiz_box_align_center.rb +18 -0
  177. data/screen_test/settings/scroll/horiz_box_align_right.rb +18 -0
  178. data/screen_test/settings/scroll/vert_box.frame +1 -0
  179. data/screen_test/settings/scroll/vert_box.rb +20 -0
  180. data/screen_test/settings/title_font.frame +1 -0
  181. data/screen_test/settings/title_font.rb +12 -0
  182. data/screen_test/settings/valign/box_around.frame +1 -0
  183. data/screen_test/settings/valign/box_around.rb +17 -0
  184. data/screen_test/settings/valign/box_between.frame +1 -0
  185. data/screen_test/settings/valign/box_between.rb +17 -0
  186. data/screen_test/settings/valign/box_bottom.frame +1 -0
  187. data/screen_test/settings/valign/box_bottom.rb +17 -0
  188. data/screen_test/settings/valign/box_default.frame +1 -0
  189. data/screen_test/settings/valign/box_default.rb +16 -0
  190. data/screen_test/settings/valign/box_evenly.frame +1 -0
  191. data/screen_test/settings/valign/box_evenly.rb +17 -0
  192. data/screen_test/settings/valign/box_middle.frame +1 -0
  193. data/screen_test/settings/valign/box_middle.rb +17 -0
  194. data/screen_test/settings/valign/box_top.frame +1 -0
  195. data/screen_test/settings/valign/box_top.rb +17 -0
  196. data/screen_test/settings/valign/grid_bottom.frame +1 -0
  197. data/screen_test/settings/valign/grid_bottom.rb +15 -0
  198. data/screen_test/settings/valign/grid_default.frame +1 -0
  199. data/screen_test/settings/valign/grid_default.rb +14 -0
  200. data/screen_test/settings/valign/grid_middle.frame +1 -0
  201. data/screen_test/settings/valign/grid_middle.rb +15 -0
  202. data/screen_test/settings/valign/grid_top.frame +1 -0
  203. data/screen_test/settings/valign/grid_top.rb +15 -0
  204. data/screen_test/settings/width/box_border_sizing.frame +1 -0
  205. data/screen_test/settings/width/box_border_sizing.rb +15 -0
  206. data/screen_test/settings/width/box_content.frame +1 -0
  207. data/screen_test/settings/width/box_content.rb +15 -0
  208. data/screen_test/settings/width/box_default.frame +1 -0
  209. data/screen_test/settings/width/box_default.rb +14 -0
  210. data/screen_test/settings/width/grid.frame +1 -0
  211. data/screen_test/settings/width/grid.rb +14 -0
  212. data/screen_test/settings/width/overflow_align_center.frame +1 -0
  213. data/screen_test/settings/width/overflow_align_center.rb +14 -0
  214. data/screen_test/settings/width/overflow_align_right.frame +1 -0
  215. data/screen_test/settings/width/overflow_align_right.rb +14 -0
  216. data/screen_test/settings/width/overflow_box.frame +1 -0
  217. data/screen_test/settings/width/overflow_box.rb +13 -0
  218. data/screen_test/settings/width/overflow_box_l2r.frame +1 -0
  219. data/screen_test/settings/width/overflow_box_l2r.rb +16 -0
  220. data/screen_test/settings/width/overflow_box_t2b.frame +1 -0
  221. data/screen_test/settings/width/overflow_box_t2b.rb +17 -0
  222. data/screen_test/settings/width/overflow_grid.frame +1 -0
  223. data/screen_test/settings/width/overflow_grid.rb +14 -0
  224. data/tools/whirled_peas/tools/screen_tester.rb +285 -0
  225. metadata +213 -15
  226. data/bin/title_fonts +0 -6
  227. data/lib/whirled_peas/frame.rb +0 -7
  228. data/lib/whirled_peas/frame/event_loop.rb +0 -91
  229. data/lib/whirled_peas/frame/print_consumer.rb +0 -33
  230. data/lib/whirled_peas/frame/producer.rb +0 -61
  231. data/lib/whirled_peas/template.rb +0 -5
  232. data/lib/whirled_peas/template/element.rb +0 -230
  233. data/lib/whirled_peas/template/settings.rb +0 -530
  234. data/lib/whirled_peas/ui.rb +0 -5
  235. data/lib/whirled_peas/ui/canvas.rb +0 -68
  236. data/lib/whirled_peas/ui/painter.rb +0 -287
  237. data/lib/whirled_peas/ui/screen.rb +0 -63
  238. data/lib/whirled_peas/utils/color.rb +0 -101
@@ -1,5 +0,0 @@
1
- module WhirledPeas
2
- module Template
3
- end
4
- private_constant :Template
5
- end
@@ -1,230 +0,0 @@
1
- require_relative '../utils/title_font'
2
-
3
- require_relative 'settings'
4
-
5
- module WhirledPeas
6
- module UI
7
- STRINGALBE_CLASSES = [FalseClass, Float, Integer, NilClass, String, Symbol, TrueClass]
8
-
9
- class Element
10
- attr_accessor :preferred_width, :preferred_height
11
- attr_reader :name, :settings
12
-
13
- def initialize(name, settings)
14
- @name = name
15
- @settings = settings
16
- end
17
- end
18
- private_constant :Element
19
-
20
- class TextElement < Element
21
- attr_reader :lines
22
-
23
- def initialize(name, settings)
24
- super(name, TextSettings.merge(settings))
25
- end
26
-
27
- def lines=(val)
28
- unless STRINGALBE_CLASSES.include?(val.class)
29
- raise ArgmentError, "Unsupported type for TextElement: #{val.class}"
30
- end
31
- if settings.title_font
32
- @lines = Utils::TitleFont.to_s(val.to_s, settings.title_font).split("\n")
33
- else
34
- @lines = [val.to_s]
35
- end
36
- @preferred_width = settings.width || @lines.first.length
37
- @preferred_height = @lines.length
38
- end
39
-
40
- def inspect(indent='')
41
- dims = unless preferred_width.nil?
42
- "#{indent + ' '}- Dimensions: #{preferred_width}x#{preferred_height}"
43
- end
44
- [
45
- "#{indent}+ #{name} [#{self.class.name}]",
46
- dims,
47
- "#{indent + ' '}- Settings",
48
- settings.inspect(indent + ' ')
49
- ].compact.join("\n")
50
- end
51
- end
52
-
53
- class ComposableElement < Element
54
- class << self
55
- def next_name
56
- @counter ||= 0
57
- @counter += 1
58
- "Element-#{@counter}"
59
- end
60
- end
61
-
62
- def initialize(name, settings)
63
- super
64
- end
65
-
66
- def children
67
- @children ||= []
68
- end
69
-
70
- def add_text(name=self.class.next_name, &block)
71
- element = TextElement.new(name, settings)
72
- element.lines = yield nil, element.settings
73
- children << element
74
- end
75
-
76
- def add_box(name=self.class.next_name, &block)
77
- element = BoxElement.new(name, settings)
78
- value = yield element, element.settings
79
- children << element
80
- if element.children.empty? && STRINGALBE_CLASSES.include?(value.class)
81
- element.add_text { value.to_s }
82
- end
83
- end
84
-
85
- def add_grid(name=self.class.next_name, &block)
86
- element = GridElement.new(name, settings)
87
- values = yield element, element.settings
88
- children << element
89
- if element.children.empty? && values.is_a?(Array)
90
- values.each { |v| element.add_text { v.to_s } }
91
- end
92
- end
93
-
94
- def inspect(indent='')
95
- kids = children.map { |c| c.inspect(indent + ' ') }.join("\n")
96
- dims = unless preferred_width.nil?
97
- "#{indent + ' '}- Dimensions: #{preferred_width}x#{preferred_height}"
98
- end
99
- [
100
- "#{indent}+ #{name} [#{self.class.name}]",
101
- dims,
102
- "#{indent + ' '}- Settings",
103
- settings.inspect(indent + ' '),
104
- "#{indent + ' '}- Children",
105
- kids
106
- ].compact.join("\n")
107
- end
108
-
109
- private
110
-
111
- def margin_width
112
- settings.margin.left + settings.margin.right
113
- end
114
-
115
- def margin_height
116
- settings.margin.top + settings.margin.bottom
117
- end
118
-
119
- def outer_border_width
120
- (settings.border.left? ? 1 : 0) + (settings.border.right? ? 1 : 0)
121
- end
122
-
123
- def outer_border_height
124
- (settings.border.top? ? 1 : 0) + (settings.border.bottom? ? 1 : 0)
125
- end
126
-
127
- def inner_border_width
128
- settings.border.inner_vert? ? 1 : 0
129
- end
130
-
131
- def inner_border_height
132
- settings.border.inner_horiz? ? 1 : 0
133
- end
134
-
135
- def padding_width
136
- settings.padding.left + settings.padding.right
137
- end
138
-
139
- def padding_height
140
- settings.padding.top + settings.padding.bottom
141
- end
142
- end
143
-
144
- class Template < ComposableElement
145
- def initialize(settings=TemplateSettings.new)
146
- super('TEMPLATE', settings)
147
- end
148
- end
149
-
150
- class BoxElement < ComposableElement
151
- attr_writer :content_width, :content_height
152
-
153
- def initialize(name, settings)
154
- super(name, BoxSettings.merge(settings))
155
- end
156
-
157
- def self.from_template(template, width, height)
158
- box = new(template.name, template.settings)
159
- template.children.each { |c| box.children << c }
160
- box.content_width = box.preferred_width = width
161
- box.content_height = box.preferred_height = height
162
- box
163
- end
164
-
165
- def content_width
166
- @content_width ||= begin
167
- child_widths = children.map(&:preferred_width)
168
- width = settings.horizontal_flow? ? child_widths.sum : (child_widths.max || 0)
169
- [width, *settings.width].max
170
- end
171
- end
172
-
173
- def preferred_width
174
- @preferred_width ||=
175
- margin_width + outer_border_width + padding_width + content_width
176
- end
177
-
178
- def content_height
179
- @content_height ||= begin
180
- child_heights = children.map(&:preferred_height)
181
- settings.vertical_flow? ? child_heights.sum : (child_heights.max || 0)
182
- end
183
- end
184
-
185
- def preferred_height
186
- @preferred_height ||=
187
- margin_height + outer_border_height + padding_height + content_height
188
- end
189
- end
190
-
191
- class GridElement < ComposableElement
192
- def initialize(name, settings)
193
- super(name, GridSettings.merge(settings))
194
- end
195
-
196
- def col_width
197
- return @col_width if @col_width
198
- @col_width = 0
199
- children.each do |child|
200
- @col_width = child.preferred_width if child.preferred_width > @col_width
201
- end
202
- @col_width
203
- end
204
-
205
- def row_height
206
- return @row_height if @row_height
207
- @row_height = 0
208
- children.each do |child|
209
- @row_height = child.preferred_height if child.preferred_height > @row_height
210
- end
211
- @row_height
212
- end
213
-
214
- def preferred_width
215
- margin_width +
216
- outer_border_width +
217
- settings.num_cols * (padding_width + col_width) +
218
- (settings.num_cols - 1) * inner_border_width
219
- end
220
-
221
- def preferred_height
222
- num_rows = (children.length / settings.num_cols).ceil
223
- margin_height +
224
- outer_border_height +
225
- num_rows * (padding_height + row_height) +
226
- (num_rows - 1) * inner_border_height
227
- end
228
- end
229
- end
230
- end
@@ -1,530 +0,0 @@
1
- require 'json'
2
-
3
- require_relative '../utils/color'
4
- require_relative '../utils/title_font'
5
-
6
- module WhirledPeas
7
- module UI
8
- module TextAlign
9
- LEFT = :left
10
- CENTER = :center
11
- RIGHT = :right
12
-
13
- def self.validate!(align)
14
- return unless align
15
- if [TextAlign::LEFT, TextAlign::CENTER, TextAlign::RIGHT].include?(align)
16
- align
17
- else
18
- raise ArgumentError, "Invalid alignment: #{align}"
19
- end
20
- end
21
- end
22
-
23
- module DisplayFlow
24
- LEFT_TO_RIGHT = :l2r
25
- RIGHT_TO_LEFT = :r2l
26
- TOP_TO_BOTTOM = :t2b
27
- BOTTOM_TO_TOP = :b2t
28
-
29
- def self.validate!(flow)
30
- return unless flow
31
- if [
32
- DisplayFlow::LEFT_TO_RIGHT,
33
- DisplayFlow::RIGHT_TO_LEFT,
34
- DisplayFlow::TOP_TO_BOTTOM,
35
- DisplayFlow::BOTTOM_TO_TOP
36
- ].include?(flow)
37
- flow
38
- else
39
- raise ArgumentError, "Invalid flow: #{flow}"
40
- end
41
- end
42
- end
43
-
44
- class Spacing
45
- def left
46
- @_left || 0
47
- end
48
-
49
- def left=(val)
50
- @_left = val
51
- end
52
-
53
- def top
54
- @_top || 0
55
- end
56
-
57
- def top=(val)
58
- @_top = val
59
- end
60
-
61
- def right
62
- @_right || 0
63
- end
64
-
65
- def right=(val)
66
- @_right = val
67
- end
68
-
69
- def bottom
70
- @_bottom || 0
71
- end
72
-
73
- def bottom=(val)
74
- @_bottom = val
75
- end
76
-
77
- def merge(parent)
78
- merged = Spacing.new
79
- merged._left = @_left || parent._left
80
- merged._top = @_top || parent._top
81
- merged._right = @_right || parent._right
82
- merged._bottom = @_bottom || parent._bottom
83
- merged
84
- end
85
-
86
- def inspect
87
- vals = {}
88
- vals[:left] = @_left if @_left
89
- vals[:top] = @_top if @_top
90
- vals[:right] = @_right if @_right
91
- vals[:bottom] = @_bottom if @_bottom
92
- JSON.generate(vals) if vals.any?
93
- end
94
-
95
- protected
96
-
97
- attr_accessor :_left, :_top, :_right, :_bottom
98
- end
99
- private_constant :Spacing
100
-
101
- class Padding < Spacing
102
- end
103
-
104
- class Margin < Spacing
105
- end
106
-
107
- BorderStyle = Struct.new(
108
- :top_left, :top_horiz, :top_junc, :top_right,
109
- :left_vert, :left_junc,
110
- :middle_vert, :cross_junc, :middle_horiz,
111
- :right_vert, :right_junc,
112
- :bottom_left, :bottom_horiz, :bottom_junc, :bottom_right
113
- )
114
-
115
- module BorderStyles
116
- BOLD = BorderStyle.new(
117
- '┏', '━', '┳', '┓',
118
- '┃', '┣',
119
- '┃', '╋', '━',
120
- '┃', '┫',
121
- '┗', '━', '┻', '┛'
122
- )
123
- SOFT = BorderStyle.new(
124
- '╭', '─', '┬', '╮',
125
- '│', '├',
126
- '│', '┼', '─',
127
- '│', '┤',
128
- '╰', '─', '┴', '╯'
129
- )
130
- DOUBLE = BorderStyle.new(
131
- '╔', '═', '╦', '╗',
132
- '║', '╠',
133
- '║', '╬', '═',
134
- '║', '╣',
135
- '╚', '═', '╩', '╝'
136
- )
137
-
138
- def self.validate!(style)
139
- return unless style
140
- if style.is_a?(Symbol)
141
- error_message = "Unsupported border style: #{style}"
142
- begin
143
- style = self.const_get(style.upcase)
144
- raise ArgumentError, error_message unless style.is_a?(BorderStyle)
145
- style
146
- rescue NameError
147
- raise ArgumentError, error_message
148
- end
149
- else
150
- style
151
- end
152
- end
153
- end
154
-
155
- class Border
156
- def left?
157
- @_left == true
158
- end
159
-
160
- def left=(val)
161
- @_left = val
162
- end
163
-
164
- def top?
165
- @_top == true
166
- end
167
-
168
- def top=(val)
169
- @_top = val
170
- end
171
-
172
- def right?
173
- @_right == true
174
- end
175
-
176
- def right=(val)
177
- @_right = val
178
- end
179
-
180
- def bottom?
181
- @_bottom == true
182
- end
183
-
184
- def bottom=(val)
185
- @_bottom = val
186
- end
187
-
188
- def inner_horiz?
189
- @_inner_horiz == true
190
- end
191
-
192
- def inner_horiz=(val)
193
- @_inner_horiz = val
194
- end
195
-
196
- def inner_vert?
197
- @_inner_vert == true
198
- end
199
-
200
- def inner_vert=(val)
201
- @_inner_vert = val
202
- end
203
-
204
- def style
205
- @_style || BorderStyles::BOLD
206
- end
207
-
208
- def style=(val)
209
- @_style = BorderStyles.validate!(val)
210
- end
211
-
212
- def color
213
- @_color || TextColor::WHITE
214
- end
215
-
216
- def color=(val)
217
- @_color = TextColor.validate!(val)
218
- end
219
-
220
- def merge(parent)
221
- merged = Border.new
222
- merged._left = @_left.nil? ? parent._left : @_left
223
- merged._top = @_top.nil? ? parent._top : @_top
224
- merged._right = @_right.nil? ? parent._right : @_right
225
- merged._bottom = @_bottom.nil? ? parent._bottom : @_bottom
226
- merged._inner_horiz = @_inner_horiz.nil? ? parent._inner_horiz : @_inner_horiz
227
- merged._inner_vert = @_inner_vert.nil? ? parent._inner_vert : @_inner_vert
228
- merged._style = @_style || parent._style
229
- merged._color = @_color || parent._color
230
- merged
231
- end
232
-
233
- def inspect
234
- vals = {}
235
- vals[:left] = @_left unless @_left.nil?
236
- vals[:top] = @_top unless @_top.nil?
237
- vals[:right] = @_right unless @_right.nil?
238
- vals[:bottom] = @_bottom unless @_bottom.nil?
239
- vals[:inner_horiz] = @_inner_horiz unless @_inner_horiz.nil?
240
- vals[:inner_vert] = @_inner_vert unless @_inner_vert.nil?
241
- JSON.generate(vals) if vals.any?
242
- end
243
-
244
- protected
245
-
246
- attr_accessor :_left, :_top, :_right, :_bottom, :_inner_horiz, :_inner_vert, :_style, :_color
247
- end
248
-
249
- class ElementSettings
250
- def self.merge(parent)
251
- self.new.merge(parent)
252
- end
253
-
254
- def color
255
- @_color
256
- end
257
-
258
- def color=(color)
259
- @_color = TextColor.validate!(color)
260
- end
261
-
262
- def bg_color
263
- @_bg_color
264
- end
265
-
266
- def bg_color=(color)
267
- @_bg_color = BgColor.validate!(color)
268
- end
269
-
270
- def bold?
271
- @_bold || false
272
- end
273
-
274
- def bold=(val)
275
- @_bold = val
276
- end
277
-
278
- def underline?
279
- @_underline || false
280
- end
281
-
282
- def underline=(val)
283
- @_underline = val
284
- end
285
-
286
- def merge(parent)
287
- merged = self.class.new
288
- merged.color = @color || parent.color
289
- merged.bg_color = @bg_color || parent.bg_color
290
- merged._bold = @_bold.nil? ? parent._bold : @_bold
291
- merged._underline = @_underline.nil? ? parent._underline : @_underline
292
- merged
293
- end
294
-
295
- def inspect(indent='')
296
- values = self.instance_variables.map do |v|
297
- unless instance_variable_get(v).nil? || instance_variable_get(v).inspect.nil?
298
- "#{indent} #{v} = #{instance_variable_get(v).inspect}"
299
- end
300
- end.compact
301
- details = values.length > 0 ? values.join("\n") : "#{indent} <default>"
302
- "#{indent}#{self.class.name}\n#{details}"
303
- end
304
-
305
- protected
306
-
307
- attr_accessor :_bold, :_underline
308
- end
309
- private_constant :ElementSettings
310
-
311
- module WidthSettings
312
- attr_accessor :width
313
- end
314
-
315
- module AlignSettings
316
- def align
317
- @_align || TextAlign::LEFT
318
- end
319
-
320
- def align=(align)
321
- @_align = TextAlign.validate!(align)
322
- end
323
-
324
- def merge(parent)
325
- merged = super
326
- merged._align = if @_align
327
- @_align
328
- elsif parent.is_a?(AlignSettings)
329
- parent._align
330
- end
331
- merged
332
- end
333
-
334
- protected
335
-
336
- attr_accessor :_align
337
- end
338
-
339
- module MarginSettings
340
- def set_margin(left: nil, top: nil, right: nil, bottom: nil)
341
- @_margin = Margin.new unless @_margin
342
- @_margin.left = left if left
343
- @_margin.top = top if top
344
- @_margin.right = right if right
345
- @_margin.bottom = bottom if bottom
346
- end
347
-
348
- def clear_margin
349
- set_margin(left: 0, top: 0, right: 0, bottom: 0)
350
- @_auto_margin = nil
351
- end
352
-
353
- def margin
354
- @_margin || Margin.new
355
- end
356
-
357
- def auto_margin=(val)
358
- @_auto_margin = val
359
- end
360
-
361
- def auto_margin?
362
- @_auto_margin || false
363
- end
364
-
365
- def merge(parent)
366
- merged = super
367
- if parent.is_a?(MarginSettings)
368
- merged._margin = margin.merge(parent.margin)
369
- merged._auto_margin = @_auto_margin.nil? ? parent._auto_margin : @_auto_margin
370
- else
371
- merged._margin = _margin
372
- merged._auto_margin = @_auto_margin
373
- end
374
- merged
375
- end
376
-
377
- protected
378
-
379
- attr_accessor :_margin, :_auto_margin
380
- end
381
- private_constant :MarginSettings
382
-
383
- module BorderSettings
384
- def set_border(
385
- left: nil, top: nil, right: nil, bottom: nil, inner_horiz: nil, inner_vert: nil, style: nil, color: nil
386
- )
387
- @_border = Border.new unless @_border
388
- @_border.left = left unless left.nil?
389
- @_border.top = top unless top.nil?
390
- @_border.right = right unless right.nil?
391
- @_border.bottom = bottom unless bottom.nil?
392
- @_border.inner_horiz = inner_horiz unless inner_horiz.nil?
393
- @_border.inner_vert = inner_vert unless inner_vert.nil?
394
- @_border.style = style unless style.nil?
395
- @_border.color = color unless color.nil?
396
- end
397
-
398
- def clear_border
399
- set_border(
400
- left: false, top: false, right: false, bottom: false, inner_horiz: false, inner_vert: false
401
- )
402
- end
403
-
404
- def full_border(style: nil, color: nil)
405
- set_border(
406
- left: true, top: true, right: true, bottom: true, inner_horiz: true, inner_vert: true, style: style, color: color
407
- )
408
- end
409
-
410
- def border
411
- @_border || Border.new
412
- end
413
-
414
- def merge(parent)
415
- merged = super
416
- merged._border = parent.is_a?(BorderSettings) ? border.merge(parent.border) : _border
417
- merged
418
- end
419
-
420
- protected
421
-
422
- attr_accessor :_border
423
- end
424
- private_constant :BorderSettings
425
-
426
- module PaddingSettings
427
- def set_padding(left: nil, top: nil, right: nil, bottom: nil)
428
- @_padding = Padding.new unless @_padding
429
- @_padding.left = left if left
430
- @_padding.top = top if top
431
- @_padding.right = right if right
432
- @_padding.bottom = bottom if bottom
433
- end
434
-
435
- def clear_padding
436
- set_padding(left: 0, top: 0, right: 0, bottom: 0)
437
- end
438
-
439
- def padding
440
- @_padding || Padding.new
441
- end
442
-
443
- def merge(parent)
444
- merged = super
445
- merged._padding = parent.is_a?(PaddingSettings) ? padding.merge(parent.padding) : _padding
446
- merged
447
- end
448
-
449
- protected
450
-
451
- attr_accessor :_padding
452
- end
453
- private_constant :PaddingSettings
454
-
455
- class TemplateSettings < ElementSettings
456
- end
457
-
458
- class TextSettings < ElementSettings
459
- include WidthSettings
460
- include AlignSettings
461
-
462
- attr_reader :title_font
463
-
464
- def title_font=(font)
465
- @title_font = Utils::TitleFont.validate!(font)
466
- end
467
- end
468
-
469
- class ContainerSettings < ElementSettings
470
- include MarginSettings
471
- include BorderSettings
472
- include PaddingSettings
473
- end
474
-
475
- class BoxSettings < ContainerSettings
476
- include WidthSettings
477
- include AlignSettings
478
-
479
- def flow=(flow)
480
- @_flow = DisplayFlow.validate!(flow)
481
- end
482
-
483
- def flow
484
- @_flow || DisplayFlow::LEFT_TO_RIGHT
485
- end
486
-
487
- def horizontal_flow?
488
- %i[l2r r2l].include?(flow)
489
- end
490
-
491
- def vertical_flow?
492
- !horizontal_flow?
493
- end
494
-
495
- def forward_flow?
496
- %i[l2r t2b].include?(flow)
497
- end
498
-
499
- def reverse_flow?
500
- !forward_flow?
501
- end
502
-
503
- def merge(parent)
504
- merged = super
505
- merged._flow = if @_flow
506
- @_flow
507
- elsif parent.is_a?(BoxSettings)
508
- parent._flow
509
- end
510
- merged
511
- end
512
-
513
- protected
514
-
515
- attr_accessor :_flow
516
- end
517
-
518
- class GridSettings < ContainerSettings
519
- include WidthSettings
520
- include AlignSettings
521
-
522
- attr_accessor :num_cols
523
- attr_writer :transpose
524
-
525
- def transpose?
526
- @transpose || false
527
- end
528
- end
529
- end
530
- end