whirled_peas 0.7.1 → 0.11.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +25 -0
- data/README.md +530 -121
- data/Rakefile +1 -20
- data/bin/easing +33 -0
- data/bin/reset_cursor +11 -0
- data/bin/screen_test +68 -0
- data/examples/components.rb +35 -0
- data/examples/graph.rb +54 -0
- data/examples/intro.rb +3 -3
- data/examples/scrolling.rb +13 -23
- data/lib/data/themes.yaml +13 -0
- data/lib/whirled_peas.rb +33 -13
- data/lib/whirled_peas/animator.rb +5 -0
- data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
- data/lib/whirled_peas/animator/easing.rb +72 -0
- data/lib/whirled_peas/animator/frame.rb +5 -0
- data/lib/whirled_peas/animator/frameset.rb +33 -0
- data/lib/whirled_peas/animator/producer.rb +35 -0
- data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
- data/lib/whirled_peas/command.rb +5 -0
- data/lib/whirled_peas/command/base.rb +86 -0
- data/lib/whirled_peas/command/config_command.rb +44 -0
- data/lib/whirled_peas/command/debug.rb +21 -0
- data/lib/whirled_peas/command/fonts.rb +22 -0
- data/lib/whirled_peas/command/frame_command.rb +34 -0
- data/lib/whirled_peas/command/frames.rb +24 -0
- data/lib/whirled_peas/command/help.rb +38 -0
- data/lib/whirled_peas/command/play.rb +112 -0
- data/lib/whirled_peas/command/record.rb +57 -0
- data/lib/whirled_peas/command/still.rb +29 -0
- data/lib/whirled_peas/command/themes.rb +77 -0
- data/lib/whirled_peas/command_line.rb +24 -212
- data/lib/whirled_peas/component.rb +35 -0
- data/lib/whirled_peas/component/list_with_active.rb +88 -0
- data/lib/whirled_peas/config.rb +56 -6
- data/lib/whirled_peas/device.rb +5 -0
- data/lib/whirled_peas/device/null_device.rb +8 -0
- data/lib/whirled_peas/device/output_file.rb +19 -0
- data/lib/whirled_peas/device/screen.rb +26 -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 +24 -2
- data/lib/whirled_peas/graphics/container_painter.rb +63 -82
- 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 +1 -7
- data/lib/whirled_peas/graphics/graph_dimensions.rb +12 -0
- data/lib/whirled_peas/graphics/graph_painter.rb +97 -0
- data/lib/whirled_peas/graphics/grid_painter.rb +0 -3
- data/lib/whirled_peas/graphics/painter.rb +10 -0
- data/lib/whirled_peas/graphics/renderer.rb +8 -2
- data/lib/whirled_peas/graphics/scrollbar_helper.rb +124 -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/border.rb +25 -4
- data/lib/whirled_peas/settings/container_settings.rb +14 -9
- data/lib/whirled_peas/settings/debugger.rb +1 -1
- data/lib/whirled_peas/settings/element_settings.rb +12 -3
- 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_settings.rb +1 -0
- data/lib/whirled_peas/settings/theme.rb +40 -0
- data/lib/whirled_peas/settings/theme_library.rb +63 -0
- data/lib/whirled_peas/utils/ansi.rb +13 -0
- data/lib/whirled_peas/utils/file_handler.rb +57 -0
- 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.frame +1 -0
- data/screen_test/elements/graph.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 +131 -65
- metadata +86 -25
- data/lib/whirled_peas/frame.rb +0 -6
- data/lib/whirled_peas/frame/consumer.rb +0 -30
- data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
- data/lib/whirled_peas/frame/event_loop.rb +0 -90
- data/lib/whirled_peas/frame/producer.rb +0 -67
- data/lib/whirled_peas/graphics/screen.rb +0 -70
- 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
|
@@ -18,13 +18,7 @@ module WhirledPeas
|
|
18
18
|
info << "#{indent + ' '}- Settings"
|
19
19
|
info << Settings::Debugger.new(painter.settings).debug(indent + ' ')
|
20
20
|
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
|
21
|
+
content = painter.content
|
28
22
|
info << "#{indent + ' '}- Content(value=#{content})"
|
29
23
|
elsif painter.is_a?(ContainerPainter)
|
30
24
|
info << "#{indent + ' '}- Children"
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'whirled_peas/utils/ansi'
|
2
|
+
|
3
|
+
require_relative 'content_painter'
|
4
|
+
|
5
|
+
module WhirledPeas
|
6
|
+
module Graphics
|
7
|
+
class GraphPainter < ContentPainter
|
8
|
+
def paint(canvas, left, top, &block)
|
9
|
+
axis_formatting = [*settings.axis_color, *settings.bg_color]
|
10
|
+
plot_formatting = [*settings.color, *settings.bg_color]
|
11
|
+
axes_lines.each.with_index do |axis_line, row_index|
|
12
|
+
canvas.stroke(left, top + row_index, axis_line, axis_formatting, &block)
|
13
|
+
next if row_index >= plot_lines.length
|
14
|
+
canvas.stroke(left + 1, top + row_index, plot_lines[row_index], plot_formatting, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def content_lines
|
21
|
+
axes_lines
|
22
|
+
end
|
23
|
+
|
24
|
+
def plot_lines
|
25
|
+
return @plot_lines if @plot_lines
|
26
|
+
min_y = 1.0 / 0
|
27
|
+
max_y = -1.0 / 0
|
28
|
+
if settings.width
|
29
|
+
interpolated = inner_width.times.map do |i|
|
30
|
+
x = (i * (content.length - 1).to_f / inner_width).floor
|
31
|
+
max_y = content[x] if content[x] > max_y
|
32
|
+
min_y = content[x] if content[x] < min_y
|
33
|
+
content[x]
|
34
|
+
end
|
35
|
+
else
|
36
|
+
interpolated = content
|
37
|
+
content.each do |y|
|
38
|
+
max_y = y if y > max_y
|
39
|
+
min_y = y if y < min_y
|
40
|
+
end
|
41
|
+
end
|
42
|
+
scaled = interpolated.map do |y|
|
43
|
+
(2 * inner_height * (y - min_y).to_f / (max_y - min_y)).floor
|
44
|
+
end
|
45
|
+
@plot_lines = Array.new(inner_height) { '' }
|
46
|
+
scaled.each.with_index do |y, x_index|
|
47
|
+
@plot_lines.each.with_index do |row, row_index|
|
48
|
+
bottom_half_index = 2 * (inner_height - row_index - 1)
|
49
|
+
top_half_index = bottom_half_index + 1
|
50
|
+
|
51
|
+
asc, next_y = if scaled.length == 1
|
52
|
+
[true, y]
|
53
|
+
elsif x_index == scaled.length - 1
|
54
|
+
y >= scaled[x_index - 1]
|
55
|
+
[true, y]
|
56
|
+
else
|
57
|
+
scaled[x_index + 1] >= y
|
58
|
+
[true, scaled[x_index + 1]]
|
59
|
+
end
|
60
|
+
if asc
|
61
|
+
top_half = y == top_half_index || (y...next_y).include?(top_half_index)
|
62
|
+
bottom_half = y == bottom_half_index || (y...next_y).include?(bottom_half_index)
|
63
|
+
else
|
64
|
+
top_half = y == top_half_index || (next_y...y).include?(top_half_index)
|
65
|
+
bottom_half = y == bottom_half_index || (next_y...y).include?(bottom_half_index)
|
66
|
+
end
|
67
|
+
row << if top_half && bottom_half
|
68
|
+
'█'
|
69
|
+
elsif top_half
|
70
|
+
'▀'
|
71
|
+
elsif bottom_half
|
72
|
+
'▄'
|
73
|
+
else
|
74
|
+
' '
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
@plot_lines
|
79
|
+
end
|
80
|
+
|
81
|
+
def inner_height
|
82
|
+
settings.height - 1
|
83
|
+
end
|
84
|
+
|
85
|
+
def inner_width
|
86
|
+
settings.width.nil? ? content.length : settings.width - 1
|
87
|
+
end
|
88
|
+
|
89
|
+
def axes_lines
|
90
|
+
return @axes_lines if @axes_lines
|
91
|
+
@axes_lines = inner_height.times.map { '┃' }
|
92
|
+
@axes_lines << '┗' + '━' * inner_width
|
93
|
+
@axes_lines
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
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
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module WhirledPeas
|
2
2
|
module Graphics
|
3
|
+
# Abstract base Painter class. Given a canvas and start coordinates (left, top), a painter
|
4
|
+
# is responsible for generating the "strokes" that display the element.
|
3
5
|
class Painter
|
4
6
|
attr_reader :name, :settings
|
5
7
|
|
@@ -8,9 +10,17 @@ module WhirledPeas
|
|
8
10
|
@name = name
|
9
11
|
end
|
10
12
|
|
13
|
+
# Paint the element onto the canvas by yielding strokes to the block. A stroke is composed
|
14
|
+
# of a left, top, and chars. E.g.
|
15
|
+
#
|
16
|
+
# yield 10, 3, 'Hello World!'
|
17
|
+
#
|
18
|
+
# paints the string "Hello World!" in the 10th column from the left, 3rd row down.
|
11
19
|
def paint(canvas, left, top, &block)
|
12
20
|
end
|
13
21
|
|
22
|
+
# Return a dimension object that provider the `outer_width` and `outer_height` of the
|
23
|
+
# element being painted.
|
14
24
|
def dimensions
|
15
25
|
end
|
16
26
|
|
@@ -9,13 +9,19 @@ module WhirledPeas
|
|
9
9
|
@height = height
|
10
10
|
end
|
11
11
|
|
12
|
-
def paint
|
12
|
+
def paint
|
13
13
|
# Modify the template's settings so that it fills the entire screen
|
14
14
|
template.settings.width = width
|
15
15
|
template.settings.height = height
|
16
16
|
template.settings.sizing = :border
|
17
17
|
template.settings.set_margin(left: 0, top: 0, right: 0, bottom: 0)
|
18
|
-
|
18
|
+
strokes = [Utils::Ansi.cursor_visible(false), Utils::Ansi.cursor_pos, Utils::Ansi.clear_down]
|
19
|
+
template.paint(Canvas.new(0, 0, width, height), 0, 0) do |left, top, fstring|
|
20
|
+
next unless fstring.length > 0
|
21
|
+
strokes << Utils::Ansi.cursor_pos(left: left, top: top)
|
22
|
+
strokes << fstring
|
23
|
+
end
|
24
|
+
strokes.join
|
19
25
|
end
|
20
26
|
|
21
27
|
private
|
@@ -0,0 +1,124 @@
|
|
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
|
+
# Determine the character to paint the horizontal scroll bar with for the given column
|
19
|
+
#
|
20
|
+
# @see #scroll_char for more details
|
21
|
+
def horiz_char(col_count, viewable_col_count, first_visible_col, curr_col)
|
22
|
+
scroll_char(col_count, viewable_col_count, first_visible_col, curr_col, HORIZONTAL)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Determine the character to paint the vertical scroll bar with for the given row
|
26
|
+
#
|
27
|
+
# @see #scroll_char for more details
|
28
|
+
def vert_char(row_count, viewable_row_count, first_visible_row, curr_row)
|
29
|
+
scroll_char(row_count, viewable_row_count, first_visible_row, curr_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 curr [Integer] zero-based index of the row/column (relative to the first visible
|
41
|
+
# row/column) that the painted character is being requested for
|
42
|
+
# @param chars [Array<String>] an array with three 1-character strings, the frist is the
|
43
|
+
# "second half" scrollbar character, the second is the "full" scrollbar character, and
|
44
|
+
# the third is the "first half" scrollbar character.
|
45
|
+
def scroll_char(total_count, viewable_count, first_visible, curr, chars)
|
46
|
+
return chars[0] if total_count == 0
|
47
|
+
return chars[0] if viewable_count == 0
|
48
|
+
return chars[0] if viewable_count >= total_count
|
49
|
+
|
50
|
+
# The scroll handle has the exact same relative size and position in the scroll gutter
|
51
|
+
# that the viewable content has in the total content area. For example, a content area
|
52
|
+
# that is 50 columns wide with a view port that is 20 columns wide might look like
|
53
|
+
#
|
54
|
+
# +---------1-----****2*********3******---4---------+
|
55
|
+
# | * * |
|
56
|
+
# | hidden * viewable * hidden |
|
57
|
+
# | * * |
|
58
|
+
# +---------1-----****2*********3******---4---------+
|
59
|
+
#
|
60
|
+
# The scoll gutter, would look like
|
61
|
+
#
|
62
|
+
# |......********.....|
|
63
|
+
#
|
64
|
+
# Scrolling all the way to the right results in
|
65
|
+
#
|
66
|
+
# +---------1---------2---------3*********4*********+
|
67
|
+
# | * *
|
68
|
+
# | hidden * viewable *
|
69
|
+
# | * *
|
70
|
+
# +---------1---------2---------3*********4*********+
|
71
|
+
# |...........********|
|
72
|
+
#
|
73
|
+
# Returning to the first example, we can match up the arguments to this method to the
|
74
|
+
# diagram
|
75
|
+
#
|
76
|
+
# total_count = 50
|
77
|
+
# |<----------------------------------------------->|
|
78
|
+
# | |
|
79
|
+
# | veiwable_count = 20 |
|
80
|
+
# | |<----------------->| |
|
81
|
+
# ↓ ↓ ↓ ↓
|
82
|
+
# +---------1-----****2*********3******---4---------+
|
83
|
+
# | * * |
|
84
|
+
# | hidden * viewable * hidden |
|
85
|
+
# | * * |
|
86
|
+
# +---------1-----****2*********3******---4---------+
|
87
|
+
# |......****?***.....|
|
88
|
+
# ↑ ↑
|
89
|
+
# first_visible = 16 |
|
90
|
+
# curr = 11
|
91
|
+
|
92
|
+
# Always use the same length for the scrollbar so it does not give an inchworm effect
|
93
|
+
# as it scrolls along. This will calculate the "ideal" length of the scroll bar if we
|
94
|
+
# could infinitely divide a character.
|
95
|
+
scrollbar_length = (viewable_count ** 2).to_f / total_count
|
96
|
+
|
97
|
+
# Round the length to the nearst "scaled" value
|
98
|
+
scrollbar_length = (SCROLL_HANDLE_SCALE * scrollbar_length).round.to_f / SCROLL_HANDLE_SCALE
|
99
|
+
|
100
|
+
# Ensure we have a scrollbar!
|
101
|
+
scrollbar_length = 1.0 / SCROLL_HANDLE_SCALE if scrollbar_length == 0
|
102
|
+
|
103
|
+
# Find the "ideal" position of where the scrollbar should start.
|
104
|
+
scrollbar_start = first_visible * viewable_count.to_f / total_count
|
105
|
+
|
106
|
+
# Round the start to the nearest "scaled" value
|
107
|
+
scrollbar_start = (SCROLL_HANDLE_SCALE * scrollbar_start).round.to_f / SCROLL_HANDLE_SCALE
|
108
|
+
|
109
|
+
# Make sure we didn't scroll off the page!
|
110
|
+
scrollbar_start -= scrollbar_length if scrollbar_start == viewable_count
|
111
|
+
|
112
|
+
# Create "scaled" indexes for the subdivided current character
|
113
|
+
curr_0 = curr
|
114
|
+
curr_1 = curr + 1.0 / SCROLL_HANDLE_SCALE
|
115
|
+
|
116
|
+
first_half = scrollbar_start <= curr_0 && curr_0 < scrollbar_start + scrollbar_length ? 2 : 0
|
117
|
+
second_half = scrollbar_start <= curr_1 && curr_1 < scrollbar_start + scrollbar_length ? 1 : 0
|
118
|
+
|
119
|
+
chars[second_half | first_half]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
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")
|
@@ -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 || Styles::DEFAULT
|
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
|