whirled_peas 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +38 -0
  4. data/Gemfile +1 -0
  5. data/README.md +275 -81
  6. data/Rakefile +38 -3
  7. data/examples/intro.rb +52 -0
  8. data/examples/scrolling.rb +53 -0
  9. data/exe/whirled_peas +7 -0
  10. data/lib/whirled_peas.rb +12 -42
  11. data/lib/whirled_peas/command_line.rb +255 -0
  12. data/lib/whirled_peas/config.rb +21 -0
  13. data/lib/whirled_peas/errors.rb +7 -0
  14. data/lib/whirled_peas/frame.rb +0 -8
  15. data/lib/whirled_peas/frame/consumer.rb +14 -50
  16. data/lib/whirled_peas/frame/debug_consumer.rb +30 -0
  17. data/lib/whirled_peas/frame/event_loop.rb +68 -38
  18. data/lib/whirled_peas/frame/producer.rb +36 -32
  19. data/lib/whirled_peas/graphics.rb +5 -0
  20. data/lib/whirled_peas/graphics/box_painter.rb +108 -0
  21. data/lib/whirled_peas/graphics/canvas.rb +107 -0
  22. data/lib/whirled_peas/graphics/composer.rb +80 -0
  23. data/lib/whirled_peas/graphics/container_coords.rb +71 -0
  24. data/lib/whirled_peas/graphics/container_dimensions.rb +75 -0
  25. data/lib/whirled_peas/graphics/container_painter.rb +234 -0
  26. data/lib/whirled_peas/graphics/debugger.rb +52 -0
  27. data/lib/whirled_peas/graphics/grid_painter.rb +68 -0
  28. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  29. data/lib/whirled_peas/graphics/painter.rb +19 -0
  30. data/lib/whirled_peas/graphics/renderer.rb +21 -0
  31. data/lib/whirled_peas/graphics/screen.rb +70 -0
  32. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  33. data/lib/whirled_peas/graphics/text_painter.rb +40 -0
  34. data/lib/whirled_peas/settings.rb +5 -0
  35. data/lib/whirled_peas/settings/bg_color.rb +22 -0
  36. data/lib/whirled_peas/settings/border.rb +101 -0
  37. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  38. data/lib/whirled_peas/settings/color.rb +69 -0
  39. data/lib/whirled_peas/settings/container_settings.rb +139 -0
  40. data/lib/whirled_peas/settings/debugger.rb +96 -0
  41. data/lib/whirled_peas/settings/display_flow.rb +25 -0
  42. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  43. data/lib/whirled_peas/settings/grid_settings.rb +15 -0
  44. data/lib/whirled_peas/settings/margin.rb +8 -0
  45. data/lib/whirled_peas/settings/padding.rb +8 -0
  46. data/lib/whirled_peas/settings/position.rb +15 -0
  47. data/lib/whirled_peas/settings/scrollbar.rb +15 -0
  48. data/lib/whirled_peas/settings/spacing.rb +24 -0
  49. data/lib/whirled_peas/settings/text_align.rb +19 -0
  50. data/lib/whirled_peas/settings/text_color.rb +19 -0
  51. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  52. data/lib/whirled_peas/utils.rb +5 -0
  53. data/lib/whirled_peas/utils/ansi.rb +53 -0
  54. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  55. data/lib/whirled_peas/utils/title_font.rb +75 -0
  56. data/lib/whirled_peas/version.rb +1 -1
  57. data/screen_test/rendered/elements/box.frame +1 -0
  58. data/screen_test/rendered/elements/box.rb +20 -0
  59. data/screen_test/rendered/elements/grid.frame +1 -0
  60. data/screen_test/rendered/elements/grid.rb +13 -0
  61. data/screen_test/rendered/elements/screen_overflow.frame +1 -0
  62. data/screen_test/rendered/elements/screen_overflow.rb +9 -0
  63. data/screen_test/rendered/elements/text.frame +1 -0
  64. data/screen_test/rendered/elements/text.rb +9 -0
  65. data/screen_test/rendered/elements/text_multiline.frame +1 -0
  66. data/screen_test/rendered/elements/text_multiline.rb +9 -0
  67. data/screen_test/rendered/settings/align/box.frame +1 -0
  68. data/screen_test/rendered/settings/align/box.rb +24 -0
  69. data/screen_test/rendered/settings/align/children_center.frame +1 -0
  70. data/screen_test/rendered/settings/align/children_center.rb +13 -0
  71. data/screen_test/rendered/settings/align/children_left.frame +1 -0
  72. data/screen_test/rendered/settings/align/children_left.rb +13 -0
  73. data/screen_test/rendered/settings/align/children_right.frame +1 -0
  74. data/screen_test/rendered/settings/align/children_right.rb +13 -0
  75. data/screen_test/rendered/settings/align/grid.frame +1 -0
  76. data/screen_test/rendered/settings/align/grid.rb +20 -0
  77. data/screen_test/rendered/settings/ansi/bold.frame +1 -0
  78. data/screen_test/rendered/settings/ansi/bold.rb +15 -0
  79. data/screen_test/rendered/settings/ansi/color.frame +1 -0
  80. data/screen_test/rendered/settings/ansi/color.rb +37 -0
  81. data/screen_test/rendered/settings/ansi/underline.frame +1 -0
  82. data/screen_test/rendered/settings/ansi/underline.rb +15 -0
  83. data/screen_test/rendered/settings/border.frame +1 -0
  84. data/screen_test/rendered/settings/border.rb +13 -0
  85. data/screen_test/rendered/settings/flow/box_b2t.frame +1 -0
  86. data/screen_test/rendered/settings/flow/box_b2t.rb +24 -0
  87. data/screen_test/rendered/settings/flow/box_l2r.frame +1 -0
  88. data/screen_test/rendered/settings/flow/box_l2r.rb +24 -0
  89. data/screen_test/rendered/settings/flow/box_r2l.frame +1 -0
  90. data/screen_test/rendered/settings/flow/box_r2l.rb +24 -0
  91. data/screen_test/rendered/settings/flow/box_t2b.frame +1 -0
  92. data/screen_test/rendered/settings/flow/box_t2b.rb +24 -0
  93. data/screen_test/rendered/settings/flow/grid_b2t.frame +1 -0
  94. data/screen_test/rendered/settings/flow/grid_b2t.rb +14 -0
  95. data/screen_test/rendered/settings/flow/grid_l2r.frame +1 -0
  96. data/screen_test/rendered/settings/flow/grid_l2r.rb +14 -0
  97. data/screen_test/rendered/settings/flow/grid_r2l.frame +1 -0
  98. data/screen_test/rendered/settings/flow/grid_r2l.rb +14 -0
  99. data/screen_test/rendered/settings/flow/grid_t2b.frame +1 -0
  100. data/screen_test/rendered/settings/flow/grid_t2b.rb +14 -0
  101. data/screen_test/rendered/settings/height/box.frame +1 -0
  102. data/screen_test/rendered/settings/height/box.rb +13 -0
  103. data/screen_test/rendered/settings/height/grid.frame +1 -0
  104. data/screen_test/rendered/settings/height/grid.rb +14 -0
  105. data/screen_test/rendered/settings/height/overflow_box.frame +1 -0
  106. data/screen_test/rendered/settings/height/overflow_box.rb +13 -0
  107. data/screen_test/rendered/settings/height/overflow_box_l2r.frame +1 -0
  108. data/screen_test/rendered/settings/height/overflow_box_l2r.rb +15 -0
  109. data/screen_test/rendered/settings/height/overflow_box_t2b.frame +1 -0
  110. data/screen_test/rendered/settings/height/overflow_box_t2b.rb +14 -0
  111. data/screen_test/rendered/settings/height/overflow_grid.frame +1 -0
  112. data/screen_test/rendered/settings/height/overflow_grid.rb +16 -0
  113. data/screen_test/rendered/settings/margin.frame +1 -0
  114. data/screen_test/rendered/settings/margin.rb +14 -0
  115. data/screen_test/rendered/settings/padding.frame +1 -0
  116. data/screen_test/rendered/settings/padding.rb +11 -0
  117. data/screen_test/rendered/settings/position/box_left.frame +1 -0
  118. data/screen_test/rendered/settings/position/box_left.rb +17 -0
  119. data/screen_test/rendered/settings/position/box_left_negative.frame +1 -0
  120. data/screen_test/rendered/settings/position/box_left_negative.rb +17 -0
  121. data/screen_test/rendered/settings/position/box_top.frame +1 -0
  122. data/screen_test/rendered/settings/position/box_top.rb +17 -0
  123. data/screen_test/rendered/settings/position/box_top_negative.frame +1 -0
  124. data/screen_test/rendered/settings/position/box_top_negative.rb +17 -0
  125. data/screen_test/rendered/settings/position/grid_left.frame +1 -0
  126. data/screen_test/rendered/settings/position/grid_left.rb +18 -0
  127. data/screen_test/rendered/settings/position/grid_left_negative.frame +1 -0
  128. data/screen_test/rendered/settings/position/grid_left_negative.rb +18 -0
  129. data/screen_test/rendered/settings/position/grid_top.frame +1 -0
  130. data/screen_test/rendered/settings/position/grid_top.rb +18 -0
  131. data/screen_test/rendered/settings/position/grid_top_negative.frame +1 -0
  132. data/screen_test/rendered/settings/position/grid_top_negative.rb +18 -0
  133. data/screen_test/rendered/settings/scroll/horiz_box.frame +1 -0
  134. data/screen_test/rendered/settings/scroll/horiz_box.rb +15 -0
  135. data/screen_test/rendered/settings/scroll/vert_box.frame +1 -0
  136. data/screen_test/rendered/settings/scroll/vert_box.rb +18 -0
  137. data/screen_test/rendered/settings/title_font.frame +1 -0
  138. data/screen_test/rendered/settings/title_font.rb +12 -0
  139. data/screen_test/rendered/settings/width/box.frame +1 -0
  140. data/screen_test/rendered/settings/width/box.rb +13 -0
  141. data/screen_test/rendered/settings/width/grid.frame +1 -0
  142. data/screen_test/rendered/settings/width/grid.rb +14 -0
  143. data/screen_test/rendered/settings/width/overflow_box.frame +1 -0
  144. data/screen_test/rendered/settings/width/overflow_box.rb +11 -0
  145. data/screen_test/rendered/settings/width/overflow_box_l2r.frame +1 -0
  146. data/screen_test/rendered/settings/width/overflow_box_l2r.rb +14 -0
  147. data/screen_test/rendered/settings/width/overflow_box_t2b.frame +1 -0
  148. data/screen_test/rendered/settings/width/overflow_box_t2b.rb +15 -0
  149. data/screen_test/rendered/settings/width/overflow_grid.frame +1 -0
  150. data/screen_test/rendered/settings/width/overflow_grid.rb +14 -0
  151. data/screen_test/screen_tester.rb +201 -0
  152. data/whirled_peas.gemspec +4 -2
  153. metadata +147 -20
  154. data/lib/whirled_peas/ui.rb +0 -7
  155. data/lib/whirled_peas/ui/ansi.rb +0 -154
  156. data/lib/whirled_peas/ui/canvas.rb +0 -35
  157. data/lib/whirled_peas/ui/element.rb +0 -225
  158. data/lib/whirled_peas/ui/painter.rb +0 -283
  159. data/lib/whirled_peas/ui/screen.rb +0 -62
  160. data/lib/whirled_peas/ui/settings.rb +0 -521
  161. data/lib/whirled_peas/ui/stroke.rb +0 -29
  162. data/sandbox/auto.rb +0 -13
  163. data/sandbox/box.rb +0 -19
  164. data/sandbox/grid.rb +0 -13
  165. data/sandbox/sandbox.rb +0 -17
  166. data/sandbox/text.rb +0 -33
@@ -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,68 @@
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, &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
+ content_left = coords(canvas).content_left(col_index)
13
+ if settings.align_center?
14
+ content_left += (dimensions.content_width - child.dimensions.outer_width) / 2
15
+ elsif settings.align_right?
16
+ content_left += dimensions.content_width - child.dimensions.outer_width
17
+ end
18
+ child_canvas = canvas.child(
19
+ content_left,
20
+ coords(canvas).content_top(row_index),
21
+ [dimensions.content_width, child.dimensions.outer_width].min,
22
+ [dimensions.content_height, child.dimensions.outer_height].min
23
+ )
24
+ child.paint(child_canvas, &block)
25
+ end
26
+ end
27
+
28
+ def dimensions
29
+ @dimensions ||= begin
30
+ if settings.num_cols.nil? || settings.num_cols == 0
31
+ raise SettingsError, "`num_cols` must be set for GridSettings(#{name})"
32
+ end
33
+ num_cols = settings.num_cols
34
+ num_rows = (num_children.to_f / num_cols).ceil
35
+ content_width = 0
36
+ each_child do |child|
37
+ if child.dimensions.outer_width > content_width
38
+ content_width = child.dimensions.outer_width
39
+ end
40
+ end
41
+ content_height = 0
42
+ each_child do |child|
43
+ if child.dimensions.outer_height > content_height
44
+ content_height = child.dimensions.outer_height
45
+ end
46
+ end
47
+ ContainerDimensions.new(
48
+ settings, content_width, content_height, num_cols, num_rows
49
+ )
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def grid_cell(index)
56
+ if settings.horizontal_flow?
57
+ col_index, row_index = [index % dimensions.num_cols, index / dimensions.num_cols]
58
+ col_index = dimensions.num_cols - col_index - 1 if settings.reverse_flow?
59
+ else
60
+ col_index, row_index = [index / dimensions.num_cols, index % dimensions.num_cols]
61
+ row_index = dimensions.num_rows - row_index - 1 if settings.reverse_flow?
62
+ end
63
+ [col_index, row_index]
64
+ end
65
+ end
66
+ private_constant :BoxPainter
67
+ end
68
+ 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,19 @@
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, &block)
12
+ end
13
+
14
+ def dimensions
15
+ end
16
+ end
17
+ private_constant :Painter
18
+ end
19
+ end
@@ -0,0 +1,21 @@
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
+ template.paint(Canvas.new(0, 0, width, height, 0, 0), &block)
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :template, :width, :height
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,70 @@
1
+ require 'highline'
2
+
3
+ require 'whirled_peas/utils/ansi'
4
+
5
+ require_relative 'renderer'
6
+
7
+ module WhirledPeas
8
+ module Graphics
9
+ class Screen
10
+ def self.current_dimensions
11
+ width, height = HighLine.new.terminal.terminal_size
12
+ [width || 0, height || 0]
13
+ end
14
+
15
+ def initialize(width: nil, height: nil, output: STDOUT)
16
+ @output = output
17
+ @terminal = HighLine.new.terminal
18
+ @strokes = []
19
+ if width && height
20
+ @width = width
21
+ @height = height
22
+ else
23
+ refresh_size!
24
+ Signal.trap('SIGWINCH', proc { self.refresh_size! })
25
+ end
26
+ end
27
+
28
+ def paint(template)
29
+ @template = template
30
+ draw
31
+ end
32
+
33
+ def refresh
34
+ # No need to refresh if the screen dimensions have not changed
35
+ return if @refreshed_width == width || @refreshed_height == height
36
+ draw
37
+ end
38
+
39
+ def finalize
40
+ output.print Utils::Ansi.clear
41
+ output.print Utils::Ansi.cursor_pos(top: height - 1)
42
+ output.print Utils::Ansi.cursor_visible(true)
43
+ output.flush
44
+ end
45
+
46
+ protected
47
+
48
+ def refresh_size!
49
+ @width, @height = self.class.current_dimensions
50
+ end
51
+
52
+ private
53
+
54
+ attr_reader :output, :cursor, :terminal, :width, :height
55
+
56
+ def draw
57
+ strokes = [Utils::Ansi.cursor_visible(false), Utils::Ansi.cursor_pos, Utils::Ansi.clear_down]
58
+ Renderer.new(@template, width, height).paint do |left, top, fstring|
59
+ next unless fstring.length > 0
60
+ strokes << Utils::Ansi.cursor_pos(left: left, top: top)
61
+ strokes << fstring
62
+ end
63
+ strokes.each { |stroke| output.print(stroke) }
64
+ output.flush
65
+ @refreshed_width = width
66
+ @refreshed_height = height
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,15 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class TextDimensions
4
+ attr_reader :outer_width, :outer_height
5
+
6
+ def initialize(content)
7
+ @outer_width = 0
8
+ content.each do |line|
9
+ @outer_width = line.length if line.length > @outer_width
10
+ end
11
+ @outer_height = content.length
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ require 'whirled_peas/utils/formatted_string'
2
+ require 'whirled_peas/utils/title_font'
3
+
4
+ require_relative 'painter'
5
+ require_relative 'text_dimensions'
6
+
7
+ module WhirledPeas
8
+ module Graphics
9
+ class TextPainter < Painter
10
+ attr_reader :content
11
+
12
+ def paint(canvas, &block)
13
+ return unless canvas.writable?
14
+ formatting = [*settings.color, *settings.bg_color]
15
+ formatting << Utils::Ansi::BOLD if settings.bold?
16
+ if settings.underline? && settings.title_font.nil?
17
+ formatting << Utils::Ansi::UNDERLINE
18
+ end
19
+ content.each.with_index do |line, index|
20
+ canvas.stroke(canvas.start_left, canvas.start_top + index, line, formatting, &block)
21
+ end
22
+ end
23
+
24
+ def dimensions
25
+ TextDimensions.new(content)
26
+ end
27
+
28
+ def content=(content)
29
+ @content = if settings.title_font
30
+ Utils::TitleFont.to_s(
31
+ content, settings.title_font
32
+ ).split("\n")
33
+ else
34
+ content.split("\n")
35
+ end
36
+ end
37
+ end
38
+ private_constant :TextPainter
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ module WhirledPeas
2
+ module Settings
3
+ end
4
+ private_constant :Settings
5
+ end
@@ -0,0 +1,22 @@
1
+ require 'whirled_peas/utils/ansi'
2
+
3
+ require_relative 'color'
4
+
5
+ module WhirledPeas
6
+ module Settings
7
+ class BgColor < Color
8
+ BG_OFFSET = 10
9
+ private_constant :BG_OFFSET
10
+
11
+ BLACK = new(Utils::Ansi::BLACK + BG_OFFSET)
12
+ RED = new(Utils::Ansi::RED + BG_OFFSET)
13
+ GREEN = new(Utils::Ansi::GREEN + BG_OFFSET)
14
+ YELLOW = new(Utils::Ansi::YELLOW + BG_OFFSET)
15
+ BLUE = new(Utils::Ansi::BLUE + BG_OFFSET)
16
+ MAGENTA = new(Utils::Ansi::MAGENTA + BG_OFFSET)
17
+ CYAN = new(Utils::Ansi::CYAN + BG_OFFSET)
18
+ WHITE = new(Utils::Ansi::WHITE + BG_OFFSET)
19
+ GRAY = BLACK.bright
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,101 @@
1
+ require_relative 'text_color'
2
+
3
+ module WhirledPeas
4
+ module Settings
5
+ class Border
6
+ Style = Struct.new(
7
+ :top_left, :top_horiz, :top_junc, :top_right,
8
+ :left_vert, :left_junc,
9
+ :middle_vert, :cross_junc, :middle_horiz,
10
+ :right_vert, :right_junc,
11
+ :bottom_left, :bottom_horiz, :bottom_junc, :bottom_right
12
+ )
13
+
14
+ module Styles
15
+ BOLD = Style.new(
16
+ '┏', '━', '┳', '┓',
17
+ '┃', '┣',
18
+ '┃', '╋', '━',
19
+ '┃', '┫',
20
+ '┗', '━', '┻', '┛'
21
+ )
22
+ SOFT = Style.new(
23
+ '╭', '─', '┬', '╮',
24
+ '│', '├',
25
+ '│', '┼', '─',
26
+ '│', '┤',
27
+ '╰', '─', '┴', '╯'
28
+ )
29
+ DOUBLE = Style.new(
30
+ '╔', '═', '╦', '╗',
31
+ '║', '╠',
32
+ '║', '╬', '═',
33
+ '║', '╣',
34
+ '╚', '═', '╩', '╝'
35
+ )
36
+
37
+ def self.validate!(style)
38
+ return unless style
39
+ if style.is_a?(Symbol)
40
+ error_message = "Unsupported border style: #{style}"
41
+ begin
42
+ style = self.const_get(style.upcase)
43
+ raise ArgumentError, error_message unless style.is_a?(Style)
44
+ style
45
+ rescue NameError
46
+ raise ArgumentError, error_message
47
+ end
48
+ else
49
+ style
50
+ end
51
+ end
52
+ end
53
+
54
+ attr_writer :left, :top, :right, :bottom, :inner_horiz, :inner_vert
55
+
56
+ def left?
57
+ @left == true
58
+ end
59
+
60
+ def top?
61
+ @top == true
62
+ end
63
+
64
+ def right?
65
+ @right == true
66
+ end
67
+
68
+ def bottom?
69
+ @bottom == true
70
+ end
71
+
72
+ def inner_horiz?
73
+ @inner_horiz == true
74
+ end
75
+
76
+ def inner_vert?
77
+ @inner_vert == true
78
+ end
79
+
80
+ def outer?
81
+ left? || top? || right? || bottom?
82
+ end
83
+
84
+ def style
85
+ @style || Styles::BOLD
86
+ end
87
+
88
+ def style=(val)
89
+ @style = Styles.validate!(val)
90
+ end
91
+
92
+ def color
93
+ @color || TextColor::WHITE
94
+ end
95
+
96
+ def color=(val)
97
+ @color = TextColor.validate!(val)
98
+ end
99
+ end
100
+ end
101
+ end