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.
- checksums.yaml +4 -4
- data/.gitignore +0 -2
- data/CHANGELOG.md +23 -0
- data/README.md +9 -777
- data/doc/application.md +92 -0
- data/doc/cli.md +115 -0
- data/doc/components.md +55 -0
- data/doc/easing.md +257 -0
- data/doc/elements.md +69 -0
- data/doc/screen_test.md +23 -0
- data/doc/settings.md +466 -0
- data/doc/template_factory.md +53 -0
- data/doc/themes.md +15 -0
- data/examples/components.rb +35 -0
- data/examples/graph.rb +54 -0
- data/examples/scrolling.rb +9 -20
- data/lib/data/themes.yaml +17 -0
- data/lib/whirled_peas.rb +31 -9
- data/lib/whirled_peas/animator/frameset.rb +1 -1
- data/lib/whirled_peas/animator/producer.rb +1 -1
- data/lib/whirled_peas/command/config_command.rb +3 -3
- data/lib/whirled_peas/command/play.rb +4 -0
- data/lib/whirled_peas/command/themes.rb +77 -0
- data/lib/whirled_peas/command_line.rb +3 -1
- data/lib/whirled_peas/component.rb +35 -0
- data/lib/whirled_peas/component/list_with_active.rb +88 -0
- data/lib/whirled_peas/graphics/box_painter.rb +4 -4
- data/lib/whirled_peas/graphics/composer.rb +27 -2
- data/lib/whirled_peas/graphics/container_coords.rb +23 -25
- data/lib/whirled_peas/graphics/container_dimensions.rb +24 -0
- data/lib/whirled_peas/graphics/container_painter.rb +27 -139
- data/lib/whirled_peas/graphics/content_dimensions.rb +19 -0
- data/lib/whirled_peas/graphics/content_painter.rb +20 -0
- data/lib/whirled_peas/graphics/debugger.rb +2 -7
- data/lib/whirled_peas/graphics/graph_dimensions.rb +12 -0
- data/lib/whirled_peas/graphics/graph_painter.rb +104 -0
- data/lib/whirled_peas/graphics/grid_painter.rb +0 -3
- data/lib/whirled_peas/graphics/scrollbar_helper.rb +126 -0
- data/lib/whirled_peas/graphics/text_painter.rb +7 -11
- data/lib/whirled_peas/null_logger.rb +0 -1
- data/lib/whirled_peas/settings/bg_color.rb +5 -1
- data/lib/whirled_peas/settings/border.rb +25 -4
- data/lib/whirled_peas/settings/color.rb +8 -4
- data/lib/whirled_peas/settings/container_settings.rb +14 -9
- data/lib/whirled_peas/settings/debugger.rb +3 -1
- data/lib/whirled_peas/settings/element_settings.rb +22 -5
- data/lib/whirled_peas/settings/graph_settings.rb +21 -0
- data/lib/whirled_peas/settings/grid_settings.rb +7 -0
- data/lib/whirled_peas/settings/position.rb +17 -5
- data/lib/whirled_peas/settings/text_color.rb +5 -0
- data/lib/whirled_peas/settings/text_settings.rb +1 -0
- data/lib/whirled_peas/settings/theme.rb +61 -0
- data/lib/whirled_peas/settings/theme_library.rb +69 -0
- data/lib/whirled_peas/utils/ansi.rb +1 -1
- data/lib/whirled_peas/version.rb +1 -1
- data/screen_test/components/list_with_active/l2r_active_color.frame +1 -0
- data/screen_test/components/list_with_active/l2r_active_color.rb +18 -0
- data/screen_test/components/list_with_active/l2r_position_end.frame +1 -0
- data/screen_test/components/list_with_active/l2r_position_end.rb +16 -0
- data/screen_test/components/list_with_active/l2r_position_middle.frame +1 -0
- data/screen_test/components/list_with_active/l2r_position_middle.rb +16 -0
- data/screen_test/components/list_with_active/l2r_position_start.frame +1 -0
- data/screen_test/components/list_with_active/l2r_position_start.rb +16 -0
- data/screen_test/components/list_with_active/l2r_separator.frame +1 -0
- data/screen_test/components/list_with_active/l2r_separator.rb +17 -0
- data/screen_test/components/list_with_active/t2b_active_color.frame +1 -0
- data/screen_test/components/list_with_active/t2b_active_color.rb +19 -0
- data/screen_test/components/list_with_active/t2b_position_end.frame +1 -0
- data/screen_test/components/list_with_active/t2b_position_end.rb +17 -0
- data/screen_test/components/list_with_active/t2b_position_middle.frame +1 -0
- data/screen_test/components/list_with_active/t2b_position_middle.rb +17 -0
- data/screen_test/components/list_with_active/t2b_position_start.frame +1 -0
- data/screen_test/components/list_with_active/t2b_position_start.rb +17 -0
- data/screen_test/components/list_with_active/t2b_separator.frame +1 -0
- data/screen_test/components/list_with_active/t2b_separator.rb +18 -0
- data/screen_test/elements/box.frame +1 -1
- data/screen_test/elements/box.rb +1 -1
- data/screen_test/elements/graph_asc.frame +1 -0
- data/screen_test/elements/graph_asc.rb +12 -0
- data/screen_test/elements/graph_desc.frame +1 -0
- data/screen_test/elements/graph_desc.rb +12 -0
- data/screen_test/elements/graph_horiz.frame +1 -0
- data/screen_test/elements/graph_horiz.rb +12 -0
- data/screen_test/elements/graph_sin.frame +1 -0
- data/screen_test/elements/graph_sin.rb +12 -0
- data/screen_test/elements/grid.frame +1 -1
- data/screen_test/elements/grid.rb +1 -1
- data/screen_test/elements/screen_overflow_x.frame +1 -1
- data/screen_test/elements/screen_overflow_x.rb +1 -1
- data/screen_test/elements/screen_overflow_y.frame +1 -1
- data/screen_test/elements/screen_overflow_y.rb +1 -1
- data/screen_test/elements/text.frame +1 -1
- data/screen_test/elements/text.rb +1 -1
- data/screen_test/elements/text_multiline.frame +1 -1
- data/screen_test/elements/text_multiline.rb +1 -1
- data/screen_test/elements/theme.frame +1 -0
- data/screen_test/elements/theme.rb +26 -0
- data/screen_test/settings/align/box_around.frame +1 -1
- data/screen_test/settings/align/box_around.rb +1 -1
- data/screen_test/settings/align/box_between.frame +1 -1
- data/screen_test/settings/align/box_between.rb +1 -1
- data/screen_test/settings/align/box_center.frame +1 -1
- data/screen_test/settings/align/box_center.rb +1 -1
- data/screen_test/settings/align/box_default.frame +1 -1
- data/screen_test/settings/align/box_default.rb +1 -1
- data/screen_test/settings/align/box_evenly.frame +1 -1
- data/screen_test/settings/align/box_evenly.rb +1 -1
- data/screen_test/settings/align/box_left.frame +1 -1
- data/screen_test/settings/align/box_left.rb +1 -1
- data/screen_test/settings/align/box_right.frame +1 -1
- data/screen_test/settings/align/box_right.rb +1 -1
- data/screen_test/settings/align/children_center.frame +1 -1
- data/screen_test/settings/align/children_center.rb +1 -1
- data/screen_test/settings/align/children_left.frame +1 -1
- data/screen_test/settings/align/children_left.rb +1 -1
- data/screen_test/settings/align/children_right.frame +1 -1
- data/screen_test/settings/align/children_right.rb +1 -1
- data/screen_test/settings/align/grid_center.frame +1 -1
- data/screen_test/settings/align/grid_center.rb +1 -1
- data/screen_test/settings/align/grid_default.frame +1 -1
- data/screen_test/settings/align/grid_default.rb +1 -1
- data/screen_test/settings/align/grid_left.frame +1 -1
- data/screen_test/settings/align/grid_left.rb +1 -1
- data/screen_test/settings/align/grid_right.frame +1 -1
- data/screen_test/settings/align/grid_right.rb +1 -1
- data/screen_test/settings/ansi/bold.frame +1 -1
- data/screen_test/settings/ansi/bold.rb +1 -1
- data/screen_test/settings/ansi/color.frame +1 -1
- data/screen_test/settings/ansi/color.rb +1 -1
- data/screen_test/settings/ansi/underline.frame +1 -1
- data/screen_test/settings/ansi/underline.rb +1 -1
- data/screen_test/settings/border.frame +1 -1
- data/screen_test/settings/border.rb +1 -1
- data/screen_test/settings/content_start/box_bottom.frame +1 -0
- data/screen_test/settings/content_start/box_bottom.rb +17 -0
- data/screen_test/settings/content_start/box_left.frame +1 -0
- data/screen_test/settings/{position → content_start}/box_left.rb +2 -2
- data/screen_test/settings/content_start/box_left_negative.frame +1 -0
- data/screen_test/settings/{position → content_start}/box_left_negative.rb +2 -2
- data/screen_test/settings/content_start/box_right.frame +1 -0
- data/screen_test/settings/content_start/box_right.rb +17 -0
- data/screen_test/settings/content_start/box_top.frame +1 -0
- data/screen_test/settings/{position → content_start}/box_top.rb +2 -2
- data/screen_test/settings/content_start/box_top_negative.frame +1 -0
- data/screen_test/settings/{position → content_start}/box_top_negative.rb +2 -2
- data/screen_test/settings/content_start/grid_left.frame +1 -0
- data/screen_test/settings/{position → content_start}/grid_left.rb +2 -2
- data/screen_test/settings/content_start/grid_left_negative.frame +1 -0
- data/screen_test/settings/{position → content_start}/grid_left_negative.rb +2 -2
- data/screen_test/settings/content_start/grid_top.frame +1 -0
- data/screen_test/settings/{position → content_start}/grid_top.rb +2 -2
- data/screen_test/settings/content_start/grid_top_negative.frame +1 -0
- data/screen_test/settings/{position → content_start}/grid_top_negative.rb +2 -2
- data/screen_test/settings/flow/box_b2t.frame +1 -1
- data/screen_test/settings/flow/box_b2t.rb +1 -1
- data/screen_test/settings/flow/box_l2r.frame +1 -1
- data/screen_test/settings/flow/box_l2r.rb +1 -1
- data/screen_test/settings/flow/box_r2l.frame +1 -1
- data/screen_test/settings/flow/box_r2l.rb +1 -1
- data/screen_test/settings/flow/box_t2b.frame +1 -1
- data/screen_test/settings/flow/box_t2b.rb +1 -1
- data/screen_test/settings/flow/grid_b2t.frame +1 -1
- data/screen_test/settings/flow/grid_b2t.rb +1 -1
- data/screen_test/settings/flow/grid_l2r.frame +1 -1
- data/screen_test/settings/flow/grid_l2r.rb +1 -1
- data/screen_test/settings/flow/grid_r2l.frame +1 -1
- data/screen_test/settings/flow/grid_r2l.rb +1 -1
- data/screen_test/settings/flow/grid_t2b.frame +1 -1
- data/screen_test/settings/flow/grid_t2b.rb +1 -1
- data/screen_test/settings/height/box.frame +1 -1
- data/screen_test/settings/height/box.rb +1 -1
- data/screen_test/settings/height/box_border_sizing.frame +1 -1
- data/screen_test/settings/height/box_border_sizing.rb +1 -1
- data/screen_test/settings/height/grid.frame +1 -1
- data/screen_test/settings/height/grid.rb +1 -1
- data/screen_test/settings/height/overflow_box.frame +1 -1
- data/screen_test/settings/height/overflow_box.rb +1 -1
- data/screen_test/settings/height/overflow_box_l2r.frame +1 -1
- data/screen_test/settings/height/overflow_box_l2r.rb +1 -1
- data/screen_test/settings/height/overflow_box_t2b.frame +1 -1
- data/screen_test/settings/height/overflow_box_t2b.rb +1 -1
- data/screen_test/settings/height/overflow_grid.frame +1 -1
- data/screen_test/settings/height/overflow_grid.rb +1 -1
- data/screen_test/settings/margin.frame +1 -1
- data/screen_test/settings/margin.rb +1 -1
- data/screen_test/settings/padding.frame +1 -1
- data/screen_test/settings/padding.rb +1 -1
- data/screen_test/settings/scroll/horiz_box.frame +1 -1
- data/screen_test/settings/scroll/horiz_box.rb +3 -5
- data/screen_test/settings/scroll/horiz_box_align_center.rb +3 -5
- data/screen_test/settings/scroll/horiz_box_align_right.rb +3 -5
- data/screen_test/settings/scroll/vert_box.frame +1 -1
- data/screen_test/settings/scroll/vert_box.rb +6 -8
- data/screen_test/settings/title_font.frame +1 -1
- data/screen_test/settings/title_font.rb +1 -1
- data/screen_test/settings/valign/box_around.frame +1 -1
- data/screen_test/settings/valign/box_around.rb +1 -1
- data/screen_test/settings/valign/box_between.frame +1 -1
- data/screen_test/settings/valign/box_between.rb +1 -1
- data/screen_test/settings/valign/box_bottom.frame +1 -1
- data/screen_test/settings/valign/box_bottom.rb +1 -1
- data/screen_test/settings/valign/box_default.frame +1 -1
- data/screen_test/settings/valign/box_default.rb +1 -1
- data/screen_test/settings/valign/box_evenly.frame +1 -1
- data/screen_test/settings/valign/box_evenly.rb +1 -1
- data/screen_test/settings/valign/box_middle.frame +1 -1
- data/screen_test/settings/valign/box_middle.rb +1 -1
- data/screen_test/settings/valign/box_top.frame +1 -1
- data/screen_test/settings/valign/box_top.rb +1 -1
- data/screen_test/settings/valign/grid_bottom.frame +1 -1
- data/screen_test/settings/valign/grid_bottom.rb +1 -1
- data/screen_test/settings/valign/grid_default.frame +1 -1
- data/screen_test/settings/valign/grid_default.rb +1 -1
- data/screen_test/settings/valign/grid_middle.frame +1 -1
- data/screen_test/settings/valign/grid_middle.rb +1 -1
- data/screen_test/settings/valign/grid_top.frame +1 -1
- data/screen_test/settings/valign/grid_top.rb +1 -1
- data/screen_test/settings/width/box_border_sizing.frame +1 -1
- data/screen_test/settings/width/box_border_sizing.rb +1 -1
- data/screen_test/settings/width/box_content.frame +1 -1
- data/screen_test/settings/width/box_content.rb +1 -1
- data/screen_test/settings/width/box_default.frame +1 -1
- data/screen_test/settings/width/box_default.rb +1 -1
- data/screen_test/settings/width/grid.frame +1 -1
- data/screen_test/settings/width/grid.rb +1 -1
- data/screen_test/settings/width/overflow_align_center.frame +1 -1
- data/screen_test/settings/width/overflow_align_center.rb +1 -1
- data/screen_test/settings/width/overflow_align_right.frame +1 -1
- data/screen_test/settings/width/overflow_align_right.rb +1 -1
- data/screen_test/settings/width/overflow_box.frame +1 -1
- data/screen_test/settings/width/overflow_box.rb +1 -1
- data/screen_test/settings/width/overflow_box_l2r.frame +1 -1
- data/screen_test/settings/width/overflow_box_l2r.rb +1 -1
- data/screen_test/settings/width/overflow_box_t2b.frame +1 -1
- data/screen_test/settings/width/overflow_box_t2b.rb +1 -1
- data/screen_test/settings/width/overflow_grid.frame +1 -1
- data/screen_test/settings/width/overflow_grid.rb +1 -1
- data/tools/whirled_peas/tools/screen_tester.rb +15 -1
- metadata +75 -20
- data/lib/whirled_peas/graphics/mock_screen.rb +0 -26
- data/lib/whirled_peas/graphics/text_dimensions.rb +0 -15
- data/screen_test/settings/position/box_left.frame +0 -1
- data/screen_test/settings/position/box_left_negative.frame +0 -1
- data/screen_test/settings/position/box_top.frame +0 -1
- data/screen_test/settings/position/box_top_negative.frame +0 -1
- data/screen_test/settings/position/grid_left.frame +0 -1
- data/screen_test/settings/position/grid_left_negative.frame +0 -1
- data/screen_test/settings/position/grid_top.frame +0 -1
- data/screen_test/settings/position/grid_top_negative.frame +0 -1
@@ -0,0 +1,19 @@
|
|
1
|
+
module WhirledPeas
|
2
|
+
module Graphics
|
3
|
+
class ContentDimensions
|
4
|
+
attr_reader :outer_width, :outer_height
|
5
|
+
|
6
|
+
def initialize(settings, content)
|
7
|
+
if settings.width
|
8
|
+
@outer_width = settings.width
|
9
|
+
else
|
10
|
+
@outer_width = 0
|
11
|
+
content.each do |line|
|
12
|
+
@outer_width = line.length if line.length > @outer_width
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@outer_height = settings.height || content.length
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'painter'
|
2
|
+
|
3
|
+
module WhirledPeas
|
4
|
+
module Graphics
|
5
|
+
class ContentPainter < Painter
|
6
|
+
attr_accessor :content
|
7
|
+
|
8
|
+
def dimensions
|
9
|
+
ContentDimensions.new(settings, content_lines)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def content_lines
|
15
|
+
raise NotImplementedError, "#{self.class} must implement #content_lines"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
private_constant :ContentPainter
|
19
|
+
end
|
20
|
+
end
|
@@ -15,16 +15,11 @@ module WhirledPeas
|
|
15
15
|
"#{indent}* #{painter.class}(#{painter.name})",
|
16
16
|
]
|
17
17
|
info << "#{indent + ' '}- Dimensions(#{dimensions})"
|
18
|
+
info << "#{indent + ' '}- Theme=#{painter.settings.theme.inspect}" if indent == ''
|
18
19
|
info << "#{indent + ' '}- Settings"
|
19
20
|
info << Settings::Debugger.new(painter.settings).debug(indent + ' ')
|
20
21
|
if painter.is_a?(TextPainter)
|
21
|
-
content =
|
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
|
22
|
+
content = painter.content
|
28
23
|
info << "#{indent + ' '}- Content(value=#{content})"
|
29
24
|
elsif painter.is_a?(ContainerPainter)
|
30
25
|
info << "#{indent + ' '}- Children"
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'whirled_peas/utils/ansi'
|
2
|
+
|
3
|
+
require_relative 'content_painter'
|
4
|
+
|
5
|
+
module WhirledPeas
|
6
|
+
module Graphics
|
7
|
+
class GraphPainter < ContentPainter
|
8
|
+
# The number of units along the Y-axis a single character can be divided into
|
9
|
+
Y_AXIS_SCALE = 2
|
10
|
+
private_constant :Y_AXIS_SCALE
|
11
|
+
|
12
|
+
def paint(canvas, left, top, &block)
|
13
|
+
axis_formatting = [*settings.axis_color, *settings.bg_color]
|
14
|
+
plot_formatting = [*settings.color, *settings.bg_color]
|
15
|
+
axes_lines.each.with_index do |axis_line, row_index|
|
16
|
+
canvas.stroke(left, top + row_index, axis_line, axis_formatting, &block)
|
17
|
+
next if row_index >= plot_lines.length
|
18
|
+
canvas.stroke(left + 1, top + row_index, plot_lines[row_index], plot_formatting, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def content_lines
|
25
|
+
axes_lines
|
26
|
+
end
|
27
|
+
|
28
|
+
def plot_lines
|
29
|
+
return @plot_lines if @plot_lines
|
30
|
+
min_y = 1.0 / 0
|
31
|
+
max_y = -1.0 / 0
|
32
|
+
if settings.width
|
33
|
+
interpolated = (inner_width + 1).times.map do |i|
|
34
|
+
x = (i * (content.length - 1).to_f / (inner_width + 1)).floor
|
35
|
+
max_y = content[x] if content[x] > max_y
|
36
|
+
min_y = content[x] if content[x] < min_y
|
37
|
+
content[x]
|
38
|
+
end
|
39
|
+
else
|
40
|
+
interpolated = content
|
41
|
+
content.each do |y|
|
42
|
+
max_y = y if y > max_y
|
43
|
+
min_y = y if y < min_y
|
44
|
+
end
|
45
|
+
end
|
46
|
+
if min_y == max_y
|
47
|
+
min_y -= 1
|
48
|
+
max_y += 1
|
49
|
+
end
|
50
|
+
scaled = interpolated.map do |y|
|
51
|
+
inner_height * (y - min_y).to_f / (max_y - min_y)
|
52
|
+
end
|
53
|
+
|
54
|
+
@plot_lines = Array.new(inner_height) { '' }
|
55
|
+
scaled[0..-2].each.with_index do |y, x_index|
|
56
|
+
last = x_index == scaled.length - 2
|
57
|
+
|
58
|
+
prev_y = x_index == 0 ? y : scaled[x_index - 1]
|
59
|
+
next_y = scaled[x_index + 1]
|
60
|
+
|
61
|
+
min_y = [(prev_y + y) / 2, y, last ? next_y : (y + next_y) / 2].min
|
62
|
+
min_y = (Y_AXIS_SCALE * min_y).round.to_f / Y_AXIS_SCALE
|
63
|
+
|
64
|
+
max_y = [(prev_y + y) / 2, y, last ? next_y : (y + next_y) / 2].max
|
65
|
+
max_y = (Y_AXIS_SCALE * max_y).round.to_f / Y_AXIS_SCALE
|
66
|
+
|
67
|
+
@plot_lines.each.with_index do |row, row_index|
|
68
|
+
bottom_value = inner_height - row_index - 1
|
69
|
+
top_value = bottom_value + 1.0 / Y_AXIS_SCALE
|
70
|
+
|
71
|
+
top_half = min_y == top_value || (min_y <= top_value && top_value < max_y)
|
72
|
+
bottom_half = min_y == bottom_value || (min_y <= bottom_value && bottom_value < max_y)
|
73
|
+
|
74
|
+
row << if top_half && bottom_half
|
75
|
+
'█'
|
76
|
+
elsif top_half
|
77
|
+
'▀'
|
78
|
+
elsif bottom_half
|
79
|
+
'▄'
|
80
|
+
else
|
81
|
+
' '
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@plot_lines
|
86
|
+
end
|
87
|
+
|
88
|
+
def inner_height
|
89
|
+
settings.height - 1
|
90
|
+
end
|
91
|
+
|
92
|
+
def inner_width
|
93
|
+
(settings.width.nil? ? content.length : settings.width) - 1
|
94
|
+
end
|
95
|
+
|
96
|
+
def axes_lines
|
97
|
+
return @axes_lines if @axes_lines
|
98
|
+
@axes_lines = inner_height.times.map { '┃' }
|
99
|
+
@axes_lines << '┗' + '━' * inner_width
|
100
|
+
@axes_lines
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -28,9 +28,6 @@ module WhirledPeas
|
|
28
28
|
|
29
29
|
def dimensions
|
30
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
31
|
num_cols = settings.num_cols
|
35
32
|
num_rows = (num_children.to_f / num_cols).ceil
|
36
33
|
content_width = 0
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module WhirledPeas
|
2
|
+
module Graphics
|
3
|
+
module ScrollbarHelper
|
4
|
+
# Contants to paint scrollbars
|
5
|
+
HORIZONTAL = [' ', '▗', '▖', '▄']
|
6
|
+
VERTICAL = [
|
7
|
+
' ',
|
8
|
+
'▗',
|
9
|
+
'▝',
|
10
|
+
'▐'
|
11
|
+
]
|
12
|
+
|
13
|
+
# The number of units of scrollbar a single character can be divided into
|
14
|
+
SCROLL_HANDLE_SCALE = 2
|
15
|
+
private_constant :SCROLL_HANDLE_SCALE
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Return the characters to paint the horizontal scroll bar with for the given column
|
19
|
+
#
|
20
|
+
# @see #scroll_chars for more details
|
21
|
+
def horiz(col_count, viewable_col_count, first_visible_col)
|
22
|
+
scroll_chars(col_count, viewable_col_count, first_visible_col, HORIZONTAL)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return the characters to paint the vertical scroll bar with for the given row
|
26
|
+
#
|
27
|
+
# @see #scroll_chars for more details
|
28
|
+
def vert(row_count, viewable_row_count, first_visible_row)
|
29
|
+
scroll_chars(row_count, viewable_row_count, first_visible_row, VERTICAL)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Determine which character to paint a for a scrollbar
|
35
|
+
#
|
36
|
+
# @param total_count [Integer] total number of rows/columns in the content
|
37
|
+
# @param viewable_count [Integer] number of rows/columns visible in the viewport
|
38
|
+
# @param first_visible [Integer] zero-based index of the first row/column that is visible
|
39
|
+
# in the viewport
|
40
|
+
# @param chars [Array<String>] an array with four 1-character strings, the frist is the
|
41
|
+
# gutter (i.e. no scrolbar handle visible), then the "second half" scrollbar character,
|
42
|
+
# then second is the "first half" scrollbar character, and finally the "full"
|
43
|
+
def scroll_chars(total_count, viewable_count, first_visible, chars)
|
44
|
+
# Start by initializing the scroll back to to all gutters
|
45
|
+
scrollbar = Array.new(viewable_count) { chars[0] }
|
46
|
+
|
47
|
+
return scrollbar if total_count == 0
|
48
|
+
return scrollbar if viewable_count == 0
|
49
|
+
return scrollbar if viewable_count >= total_count
|
50
|
+
|
51
|
+
# The scroll handle has the exact same relative size and position in the scroll gutter
|
52
|
+
# that the viewable content has in the total content area. For example, a content area
|
53
|
+
# that is 50 columns wide with a view port that is 20 columns wide might look like
|
54
|
+
#
|
55
|
+
# +---------1-----****2*********3******---4---------+
|
56
|
+
# | * * |
|
57
|
+
# | hidden * viewable * hidden |
|
58
|
+
# | * * |
|
59
|
+
# +---------1-----****2*********3******---4---------+
|
60
|
+
#
|
61
|
+
# The scoll gutter, would look like
|
62
|
+
#
|
63
|
+
# |......********.....|
|
64
|
+
#
|
65
|
+
# Scrolling all the way to the right results in
|
66
|
+
#
|
67
|
+
# +---------1---------2---------3*********4*********+
|
68
|
+
# | * *
|
69
|
+
# | hidden * viewable *
|
70
|
+
# | * *
|
71
|
+
# +---------1---------2---------3*********4*********+
|
72
|
+
# |...........********|
|
73
|
+
#
|
74
|
+
# Returning to the first example, we can match up the arguments to this method to the
|
75
|
+
# diagram
|
76
|
+
#
|
77
|
+
# total_count = 50
|
78
|
+
# |<----------------------------------------------->|
|
79
|
+
# | |
|
80
|
+
# | veiwable_count = 20 |
|
81
|
+
# | |<----------------->| |
|
82
|
+
# ↓ ↓ ↓ ↓
|
83
|
+
# +---------1-----****2*********3******---4---------+
|
84
|
+
# | * * |
|
85
|
+
# | hidden * viewable * hidden |
|
86
|
+
# | * * |
|
87
|
+
# +---------1-----****2*********3******---4---------+
|
88
|
+
# |......****?***.....|
|
89
|
+
# ↑ ↑
|
90
|
+
# first_visible = 16 |
|
91
|
+
# curr = 11
|
92
|
+
|
93
|
+
# Always use the same length for the scrollbar so it does not give an inchworm effect
|
94
|
+
# as it scrolls along. This will calculate the "ideal" length of the scroll bar if we
|
95
|
+
# could infinitely divide a character.
|
96
|
+
length = (viewable_count ** 2).to_f / total_count
|
97
|
+
|
98
|
+
# Round the length to the nearst "scaled" value
|
99
|
+
length = (SCROLL_HANDLE_SCALE * length).round.to_f / SCROLL_HANDLE_SCALE
|
100
|
+
|
101
|
+
# Ensure we have a scrollbar!
|
102
|
+
length = 1.0 / SCROLL_HANDLE_SCALE if length == 0
|
103
|
+
|
104
|
+
# Find the "ideal" position of where the scrollbar should start.
|
105
|
+
start = first_visible * viewable_count.to_f / total_count
|
106
|
+
|
107
|
+
# Round the start to the nearest "scaled" value
|
108
|
+
start = (SCROLL_HANDLE_SCALE * start).round.to_f / SCROLL_HANDLE_SCALE
|
109
|
+
|
110
|
+
# Make sure we didn't scroll off the page!
|
111
|
+
start -= length if start == viewable_count
|
112
|
+
|
113
|
+
(start.floor..[(start + length).floor, viewable_count - 1].min).each do |curr_0|
|
114
|
+
curr_1 = curr_0 + 1.0 / SCROLL_HANDLE_SCALE
|
115
|
+
|
116
|
+
first_half = start <= curr_0 && curr_0 < start + length ? 2 : 0
|
117
|
+
second_half = start <= curr_1 && curr_1 < start + length ? 1 : 0
|
118
|
+
|
119
|
+
scrollbar[curr_0] = chars[second_half | first_half]
|
120
|
+
end
|
121
|
+
scrollbar
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -1,14 +1,12 @@
|
|
1
1
|
require 'whirled_peas/utils/formatted_string'
|
2
2
|
require 'whirled_peas/utils/title_font'
|
3
3
|
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
4
|
+
require_relative 'content_dimensions'
|
5
|
+
require_relative 'content_painter'
|
6
6
|
|
7
7
|
module WhirledPeas
|
8
8
|
module Graphics
|
9
|
-
class TextPainter <
|
10
|
-
attr_reader :content
|
11
|
-
|
9
|
+
class TextPainter < ContentPainter
|
12
10
|
def paint(canvas, left, top, &block)
|
13
11
|
return unless canvas.writable?
|
14
12
|
formatting = [*settings.color, *settings.bg_color]
|
@@ -16,17 +14,15 @@ module WhirledPeas
|
|
16
14
|
if settings.underline? && settings.title_font.nil?
|
17
15
|
formatting << Utils::Ansi::UNDERLINE
|
18
16
|
end
|
19
|
-
|
17
|
+
content_lines.each.with_index do |line, index|
|
20
18
|
canvas.stroke(left, top + index, line, formatting, &block)
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
24
|
-
|
25
|
-
TextDimensions.new(content)
|
26
|
-
end
|
22
|
+
private
|
27
23
|
|
28
|
-
def
|
29
|
-
@
|
24
|
+
def content_lines
|
25
|
+
@content_lines = if settings.title_font
|
30
26
|
Utils::TitleFont.to_s(
|
31
27
|
content, settings.title_font
|
32
28
|
).split("\n")
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'whirled_peas/utils/ansi'
|
2
2
|
|
3
3
|
require_relative 'color'
|
4
|
+
require_relative 'text_color'
|
4
5
|
|
5
6
|
module WhirledPeas
|
6
7
|
module Settings
|
7
8
|
class BgColor < Color
|
8
9
|
BG_OFFSET = 10
|
9
|
-
private_constant :BG_OFFSET
|
10
10
|
|
11
11
|
BLACK = new(Utils::Ansi::BLACK + BG_OFFSET)
|
12
12
|
RED = new(Utils::Ansi::RED + BG_OFFSET)
|
@@ -19,6 +19,10 @@ module WhirledPeas
|
|
19
19
|
GRAY = BLACK.bright
|
20
20
|
|
21
21
|
DEFAULT = nil # Use system defined default
|
22
|
+
|
23
|
+
def as_text_color
|
24
|
+
TextColor.new(code - BG_OFFSET, bright?)
|
25
|
+
end
|
22
26
|
end
|
23
27
|
end
|
24
28
|
end
|
@@ -51,8 +51,20 @@ module WhirledPeas
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def self.inherit(parent)
|
55
|
+
border = new(parent.theme)
|
56
|
+
border.inherit(parent)
|
57
|
+
border
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :theme
|
61
|
+
|
54
62
|
attr_writer :left, :top, :right, :bottom, :inner_horiz, :inner_vert
|
55
63
|
|
64
|
+
def initialize(theme)
|
65
|
+
@theme = theme
|
66
|
+
end
|
67
|
+
|
56
68
|
def left?
|
57
69
|
@left == true
|
58
70
|
end
|
@@ -82,20 +94,29 @@ module WhirledPeas
|
|
82
94
|
end
|
83
95
|
|
84
96
|
def style
|
85
|
-
@
|
97
|
+
@_style || theme.border_style
|
86
98
|
end
|
87
99
|
|
88
100
|
def style=(val)
|
89
|
-
@
|
101
|
+
@_style = Styles.validate!(val)
|
90
102
|
end
|
91
103
|
|
92
104
|
def color=(val)
|
93
|
-
@
|
105
|
+
@_color = TextColor.validate!(val)
|
94
106
|
end
|
95
107
|
|
96
108
|
def color
|
97
|
-
@
|
109
|
+
@_color || theme.border_color
|
98
110
|
end
|
111
|
+
|
112
|
+
def inherit(parent)
|
113
|
+
@_style = parent._style
|
114
|
+
@_color = parent._color
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
|
119
|
+
attr_reader :_style, :_color
|
99
120
|
end
|
100
121
|
end
|
101
122
|
end
|
@@ -43,11 +43,11 @@ module WhirledPeas
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def bright
|
46
|
-
bright? ? self : self.class.new(
|
46
|
+
bright? ? self : self.class.new(code + Utils::Ansi::BRIGHT_OFFSET, true)
|
47
47
|
end
|
48
48
|
|
49
49
|
def hash
|
50
|
-
[
|
50
|
+
[code, bright].hash
|
51
51
|
end
|
52
52
|
|
53
53
|
def ==(other)
|
@@ -56,12 +56,16 @@ module WhirledPeas
|
|
56
56
|
alias_method :eq?, :==
|
57
57
|
|
58
58
|
def to_s
|
59
|
-
|
59
|
+
code.to_s
|
60
60
|
end
|
61
61
|
|
62
62
|
def inspect
|
63
|
-
"#{self.class.name.split('::').last}(code=#{
|
63
|
+
"#{self.class.name.split('::').last}(code=#{code}, bright=#{bright?})"
|
64
64
|
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
attr_reader :code
|
65
69
|
end
|
66
70
|
private_constant :Color
|
67
71
|
end
|