whirled_peas 0.9.1 → 0.13.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 +0 -2
- data/CHANGELOG.md +23 -0
- data/README.md +9 -1049
- data/doc/application.md +92 -0
- data/doc/cli.md +115 -0
- data/doc/components.md +51 -0
- data/doc/easing.md +257 -0
- data/doc/elements.md +69 -0
- data/doc/screen_test.md +22 -0
- data/doc/settings.md +466 -0
- data/doc/template_factory.md +53 -0
- data/doc/themes.md +50 -0
- data/examples/components.rb +33 -0
- data/examples/graph.rb +1 -1
- data/examples/scrolling.rb +8 -19
- data/lib/data/themes.yaml +15 -0
- data/lib/whirled_peas.rb +31 -9
- data/lib/whirled_peas/animator/debug_consumer.rb +2 -2
- data/lib/whirled_peas/animator/easing.rb +57 -0
- data/lib/whirled_peas/animator/frameset.rb +13 -8
- data/lib/whirled_peas/animator/producer.rb +7 -8
- data/lib/whirled_peas/animator/renderer_consumer.rb +10 -5
- data/lib/whirled_peas/command/config_command.rb +3 -3
- data/lib/whirled_peas/command/frames.rb +1 -3
- data/lib/whirled_peas/command/play.rb +9 -7
- data/lib/whirled_peas/command/record.rb +1 -3
- data/lib/whirled_peas/command/still.rb +6 -2
- data/lib/whirled_peas/command/themes.rb +78 -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 +89 -0
- data/lib/whirled_peas/config.rb +1 -8
- data/lib/whirled_peas/device/null_device.rb +1 -1
- data/lib/whirled_peas/device/output_file.rb +2 -2
- data/lib/whirled_peas/device/rendered_frame.rb +12 -0
- data/lib/whirled_peas/device/screen.rb +7 -8
- data/lib/whirled_peas/graphics/box_painter.rb +4 -4
- data/lib/whirled_peas/graphics/composer.rb +9 -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/debugger.rb +2 -7
- data/lib/whirled_peas/graphics/graph_painter.rb +31 -24
- data/lib/whirled_peas/graphics/pixel_grid.rb +81 -0
- data/lib/whirled_peas/graphics/renderer.rb +4 -5
- data/lib/whirled_peas/graphics/scrollbar_helper.rb +126 -0
- 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 +26 -5
- data/lib/whirled_peas/settings/color.rb +8 -4
- data/lib/whirled_peas/settings/container_settings.rb +14 -7
- data/lib/whirled_peas/settings/debugger.rb +3 -1
- data/lib/whirled_peas/settings/element_settings.rb +18 -6
- data/lib/whirled_peas/settings/graph_settings.rb +1 -1
- 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 +4 -2
- data/lib/whirled_peas/utils/file_handler.rb +14 -9
- data/lib/whirled_peas/utils/formatted_string.rb +5 -3
- data/lib/whirled_peas/version.rb +1 -1
- 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_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.rb → graph_asc.rb} +1 -1
- 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 +27 -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 +21 -3
- metadata +67 -22
- data/bin/easing +0 -33
- data/lib/whirled_peas/graphics/mock_screen.rb +0 -26
- data/screen_test/elements/graph.frame +0 -1
- 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,53 @@
|
|
1
|
+
## Template Factory
|
2
|
+
|
3
|
+
To render the frame events sent by the application, the application requires a template factory. This factory will be called for each frame event, with the frame name and the arguments supplied by the application. A template factory can be an instance of ruby class and thus can maintain state. Whirled Peas provides a few basic building blocks to make simple, yet elegant terminal-based UIs.
|
4
|
+
|
5
|
+
To build a template in a template factory, use `WhirledPeas.template(theme)`, which takes an optional [theme](themes.md) and yields a composer (used to add [elements](elements.md) to the template) and [settings](settings.md) (used to configure the element).
|
6
|
+
|
7
|
+
### Full example
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class TemplateFactory
|
11
|
+
def build(frame, args)
|
12
|
+
set_state(frame, args)
|
13
|
+
WhirledPeas.template do |composer, settings|
|
14
|
+
settings.flow = :l2r
|
15
|
+
settings.align = :center
|
16
|
+
|
17
|
+
composer.add_box('Title', &method(:title))
|
18
|
+
composer.add_box('Sum', &method(:sum))
|
19
|
+
composer.add_grid('NumberGrid', &method(:number_grid))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def set_state(frame, args)
|
26
|
+
@frame = frame
|
27
|
+
@numbers = args.key?(:numbers) ? args[:numbers] || []
|
28
|
+
@sum = args[:sum] if args.key?(:sum)
|
29
|
+
@low = args[:low] if args.key?(:low)
|
30
|
+
@high = args[:high] if args.key?(:high)
|
31
|
+
end
|
32
|
+
|
33
|
+
def title(_composer, settings)
|
34
|
+
settings.underline = true
|
35
|
+
"Pair Finder"
|
36
|
+
end
|
37
|
+
|
38
|
+
def sum(_composer, settings)
|
39
|
+
settings.color = @frame == 'found-pair' ? :green : :red
|
40
|
+
@sum ? "Sum: #{@sum}" : 'N/A'
|
41
|
+
end
|
42
|
+
|
43
|
+
def number_grid(composer, settings)
|
44
|
+
settings.full_border
|
45
|
+
@numbers.each.with_index do |num, index|
|
46
|
+
composer.add_text do |_, settings|
|
47
|
+
settings.bg_color = (@low == index || @high == index) ? :cyan : :white
|
48
|
+
num
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
data/doc/themes.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
## Themes
|
2
|
+
|
3
|
+
The template builder (`WhirledPeas.template`) takes an optional `theme` argument. You can provide the name of a predefined theme (run `whirled_peas themes` to see a list with samples) or define you own with the following
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
WhirledPeas.register_theme(:my_theme) do |theme|
|
7
|
+
theme.bg_color = :bright_white
|
8
|
+
theme.color = :blue
|
9
|
+
theme.border_color = :bright_green
|
10
|
+
theme.axis_color = :bright_red
|
11
|
+
theme.title_font = :default
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
Theme settings will be used as default settings throughout the template, however theme settings can be overridden on any element.
|
16
|
+
|
17
|
+
### Theme Settings
|
18
|
+
|
19
|
+
The following theme settings override the existing default settings for all elements.
|
20
|
+
|
21
|
+
- `axis_color` - axis color for graphs (defaults to `border_color`)
|
22
|
+
- `bg_color` - background color (defaults to system color)
|
23
|
+
- `border_color` - border color (defaults to `color`)
|
24
|
+
- `border_style` - border style (defaults to `bold`)
|
25
|
+
- `color` - text color (defaults to system color)
|
26
|
+
|
27
|
+
The following theme settings provide new options that can be applied to existing settings. E.g.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
WhirledPeas.register_theme(:my_theme) do |theme|
|
31
|
+
theme.highlight_bg_color = :bright_white
|
32
|
+
theme.highlight_color = :red
|
33
|
+
# ...
|
34
|
+
end
|
35
|
+
|
36
|
+
WhirledPeas.template(:my_theme) do |composer, settings|
|
37
|
+
# ...
|
38
|
+
composer.add_text do |composer, settings|
|
39
|
+
composer.bg_color = :highlight
|
40
|
+
composer.color = :highlight
|
41
|
+
"This Is Important!!!"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
- `highlight_bg_color` - provides a new `:highlight` option that can be applied to `bg_color` settings (defaults to `color`)
|
47
|
+
- `highlight_color` - provides a new `:highlight` option that can be applied to `color` settings (defaults to `bg_color`)
|
48
|
+
- `title_font` - provides a new `:theme` option that can be applied to `title_font` settings (defaults to system default)
|
49
|
+
|
50
|
+
Note: the defaults for the `highlight_bg_color` and `highlight_color` options result in inverting the background and text colors.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'whirled_peas'
|
3
|
+
require 'whirled_peas/component/list_with_active'
|
4
|
+
|
5
|
+
class TemplateFactory
|
6
|
+
ITEMS = 400.times.map(&:itself)
|
7
|
+
|
8
|
+
def build(name, args)
|
9
|
+
active = args[:active]
|
10
|
+
WhirledPeas.template do |composer, settings|
|
11
|
+
WhirledPeas.component(composer, settings, :list_with_active) do |component, settings|
|
12
|
+
component.items = %w[red orange yellow green blue indigo violet]
|
13
|
+
component.separator = ', '
|
14
|
+
component.active_index = active
|
15
|
+
component.viewport_size = 27
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Application
|
22
|
+
def start(producer)
|
23
|
+
producer.frameset(5, easing: :parametric) do |fs|
|
24
|
+
TemplateFactory::ITEMS.length.times { |i| fs.add_frame('intro', args: { active: i }) }
|
25
|
+
end
|
26
|
+
producer.add_frame('hold', duration: 1, args: { active: TemplateFactory::ITEMS.length - 1 })
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
WhirledPeas.configure do |config|
|
31
|
+
config.template_factory = TemplateFactory.new
|
32
|
+
config.application = Application.new
|
33
|
+
end
|
data/examples/graph.rb
CHANGED
data/examples/scrolling.rb
CHANGED
@@ -2,38 +2,27 @@ require 'bundler/setup'
|
|
2
2
|
require 'whirled_peas'
|
3
3
|
|
4
4
|
class TemplateFactory
|
5
|
-
HPADDING = 0
|
6
|
-
VPADDING = 0
|
7
5
|
ITEM_COUNT = 64
|
8
6
|
|
9
7
|
def build(name, args)
|
10
8
|
top = args[:top]
|
11
9
|
WhirledPeas.template do |composer, settings|
|
12
|
-
settings.color = :green
|
13
10
|
composer.add_box('Vert') do |composer, settings|
|
14
|
-
settings.
|
15
|
-
settings.full_border(color: :blue)
|
11
|
+
settings.full_border
|
16
12
|
settings.set_scrollbar(vert: true)
|
17
13
|
settings.height = 12
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
ITEM_COUNT.times do |i|
|
23
|
-
composer.add_text { "%2d" % i }
|
24
|
-
end
|
14
|
+
settings.flow = :t2b
|
15
|
+
settings.content_start.top = top
|
16
|
+
ITEM_COUNT.times do |i|
|
17
|
+
composer.add_text { "%2d" % i }
|
25
18
|
end
|
26
19
|
end
|
27
20
|
composer.add_box('Horiz') do |composer, settings|
|
28
|
-
settings.
|
29
|
-
settings.full_border(color: :blue)
|
30
|
-
settings.set_padding(left: HPADDING, right: HPADDING, top: VPADDING, bottom: VPADDING)
|
21
|
+
settings.full_border
|
31
22
|
settings.set_scrollbar(horiz: true)
|
32
23
|
settings.width = 12
|
33
|
-
|
34
|
-
|
35
|
-
ITEM_COUNT.times.map { |i| (i % 10).to_s }.join
|
36
|
-
end
|
24
|
+
settings.content_start.left = top
|
25
|
+
ITEM_COUNT.times.map { |i| (i % 10).to_s }.join
|
37
26
|
end
|
38
27
|
end
|
39
28
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
bright:
|
2
|
+
axis_color: :red
|
3
|
+
bg_color: :bright_white
|
4
|
+
border_color: :bright_blue
|
5
|
+
color: :blue
|
6
|
+
title_font: :default
|
7
|
+
|
8
|
+
dark:
|
9
|
+
axis_color: :bright_white
|
10
|
+
bg_color: :black
|
11
|
+
border_color: :gray
|
12
|
+
color: :white
|
13
|
+
highlight_bg_color: :bright_red
|
14
|
+
highlight_color: :black
|
15
|
+
title_font: :swan
|
data/lib/whirled_peas.rb
CHANGED
@@ -7,16 +7,38 @@ require 'whirled_peas/utils'
|
|
7
7
|
require 'whirled_peas/version'
|
8
8
|
|
9
9
|
module WhirledPeas
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
class << self
|
11
|
+
def configure(&block)
|
12
|
+
yield config
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def register_component(name, klass)
|
16
|
+
require 'whirled_peas/component'
|
17
|
+
Component::Factory.register(name, klass)
|
18
|
+
end
|
19
|
+
|
20
|
+
def component(composer, settings, name, &block)
|
21
|
+
require 'whirled_peas/component'
|
22
|
+
component = Component::Factory.build(name)
|
23
|
+
yield component
|
24
|
+
component.compose(composer, settings)
|
25
|
+
end
|
26
|
+
|
27
|
+
def template(theme_name=nil, &block)
|
28
|
+
require 'whirled_peas/graphics/composer'
|
29
|
+
Graphics::Composer.build(theme_name, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def register_theme(name, &block)
|
33
|
+
require 'whirled_peas/settings/theme'
|
34
|
+
require 'whirled_peas/settings/theme_library'
|
35
|
+
theme = Settings::Theme.new
|
36
|
+
yield theme
|
37
|
+
Settings::ThemeLibrary.add(name, theme)
|
38
|
+
end
|
17
39
|
|
18
|
-
|
19
|
-
|
20
|
-
|
40
|
+
def config
|
41
|
+
@config ||= Config.new
|
42
|
+
end
|
21
43
|
end
|
22
44
|
end
|
@@ -4,8 +4,8 @@ module WhirledPeas
|
|
4
4
|
def add_frameset(frameset)
|
5
5
|
require 'json'
|
6
6
|
|
7
|
-
frameset.each_frame do |frame, args|
|
8
|
-
puts [frame, *(JSON.generate(args) unless args.empty?)].join(' ')
|
7
|
+
frameset.each_frame do |frame, duration, args|
|
8
|
+
puts [frame, "duration=#{duration}", *(JSON.generate(args) unless args.empty?)].join(' ')
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -21,6 +21,10 @@ module WhirledPeas
|
|
21
21
|
|
22
22
|
EFFECTS = %i[in out in_out]
|
23
23
|
|
24
|
+
INVERSE_MAX_ITERATIONS = 10
|
25
|
+
INVERSE_DELTA = 0.0001
|
26
|
+
INVERSE_EPSILON = 0.00001
|
27
|
+
|
24
28
|
def initialize(easing=:linear, effect=:in_out)
|
25
29
|
unless EASING.key?(easing)
|
26
30
|
raise ArgumentError,
|
@@ -45,6 +49,56 @@ module WhirledPeas
|
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
52
|
+
def invert(target)
|
53
|
+
ease_fn = case effect
|
54
|
+
when :in
|
55
|
+
proc { |v| ease_in(v) }
|
56
|
+
when :out
|
57
|
+
proc { |v| ease_out(v) }
|
58
|
+
else
|
59
|
+
proc { |v| ease_in_out(v) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Use Newton's method(!!) to find the inverse values of the easing function for the
|
63
|
+
# specified target. Make an initial guess that is equal to the target and see how
|
64
|
+
# far off the eased value is from the target. If we are close enough (as dictated by
|
65
|
+
# INVERSE_EPSILON constant), then we return our guess. If we aren't close enough, then
|
66
|
+
# find the slope of the eased line (approximated with a small step of INVERSE_DELTA
|
67
|
+
# along the x-axis). The intersection of the slope and target will give us the value
|
68
|
+
# of our next guess.
|
69
|
+
#
|
70
|
+
# Since most easing functions only vary slightly from the identity line (y = x), we
|
71
|
+
# can typically get the eased guess within epsilon of the target in a few iterations,
|
72
|
+
# however only iterate at most INVERSE_MAX_ITERATIONS times.
|
73
|
+
#
|
74
|
+
# ┃ ......
|
75
|
+
# ┃ ...
|
76
|
+
# target -┃------------+ ...
|
77
|
+
# ┃ /.|..
|
78
|
+
# ┃ ../. |
|
79
|
+
# ┃ ... | |
|
80
|
+
# ┃... | |
|
81
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
82
|
+
# | |
|
83
|
+
# guess next guess
|
84
|
+
#
|
85
|
+
# IMPORTANT: This method only works well for monotonic easing functions
|
86
|
+
|
87
|
+
# Pick the target as the first guess. For targets of 0 and 1 (and 0.5 for ease_in_out),
|
88
|
+
# this guess will be the exact value that yields the target. For other values, the
|
89
|
+
# eased guess will generally be close to the target.
|
90
|
+
guess = target
|
91
|
+
INVERSE_MAX_ITERATIONS.times do |i|
|
92
|
+
eased_guess = ease_fn.call(guess)
|
93
|
+
error = eased_guess - target
|
94
|
+
break if error.abs < INVERSE_EPSILON
|
95
|
+
next_eased_guess = ease_fn.call(guess + INVERSE_DELTA)
|
96
|
+
delta_eased = next_eased_guess - eased_guess
|
97
|
+
guess -= INVERSE_DELTA * error / delta_eased
|
98
|
+
end
|
99
|
+
guess
|
100
|
+
end
|
101
|
+
|
48
102
|
private
|
49
103
|
|
50
104
|
attr_reader :easing, :effect
|
@@ -60,6 +114,9 @@ module WhirledPeas
|
|
60
114
|
1 - EASING[easing].call(1 - value)
|
61
115
|
end
|
62
116
|
|
117
|
+
# Ease in/ease out
|
118
|
+
#
|
119
|
+
# @see https://www.youtube.com/watch?v=5WPbqYoz9HA
|
63
120
|
def ease_in_out(value)
|
64
121
|
if value < 0.5
|
65
122
|
ease_in(value * 2) / 2
|
@@ -4,8 +4,8 @@ require_relative 'frame'
|
|
4
4
|
module WhirledPeas
|
5
5
|
module Animator
|
6
6
|
class Frameset
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(duration, easing, effect)
|
8
|
+
@duration = duration
|
9
9
|
@easing = Easing.new(easing, effect)
|
10
10
|
@frames = []
|
11
11
|
end
|
@@ -16,17 +16,22 @@ module WhirledPeas
|
|
16
16
|
|
17
17
|
# Yield each frame in an "eased" order
|
18
18
|
def each_frame(&block)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
return if frames.length == 0
|
20
|
+
if frames.length == 1
|
21
|
+
frame, args = frames[0]
|
22
|
+
yield frame, duration, args
|
23
|
+
else
|
24
|
+
frames.each.with_index do |(frame, args), index|
|
25
|
+
curr_ease = @easing.invert(index.to_f / frames.length)
|
26
|
+
next_ease = @easing.invert((index + 1).to_f / frames.length)
|
27
|
+
yield frame, duration * (next_ease - curr_ease), args
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
26
31
|
|
27
32
|
private
|
28
33
|
|
29
|
-
attr_reader :
|
34
|
+
attr_reader :duration, :easing, :frames
|
30
35
|
end
|
31
36
|
private_constant :Frameset
|
32
37
|
end
|
@@ -4,32 +4,31 @@ require_relative 'frameset'
|
|
4
4
|
module WhirledPeas
|
5
5
|
module Animator
|
6
6
|
class Producer
|
7
|
-
def self.produce(consumer
|
8
|
-
producer = new(consumer
|
7
|
+
def self.produce(consumer)
|
8
|
+
producer = new(consumer)
|
9
9
|
yield producer
|
10
10
|
consumer.process
|
11
11
|
end
|
12
12
|
|
13
|
-
def initialize(consumer
|
13
|
+
def initialize(consumer)
|
14
14
|
@consumer = consumer
|
15
|
-
@refresh_rate = refresh_rate
|
16
15
|
end
|
17
16
|
|
18
|
-
def add_frame(name, duration
|
19
|
-
frameset(duration
|
17
|
+
def add_frame(name, duration:, args: {})
|
18
|
+
frameset(duration) do |fs|
|
20
19
|
fs.add_frame(name, args: args)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
23
|
def frameset(duration, easing: :linear, effect: :in_out, &block)
|
25
|
-
fs = Frameset.new(
|
24
|
+
fs = Frameset.new(duration, easing, effect)
|
26
25
|
yield fs
|
27
26
|
consumer.add_frameset(fs)
|
28
27
|
end
|
29
28
|
|
30
29
|
private
|
31
30
|
|
32
|
-
attr_reader :consumer
|
31
|
+
attr_reader :consumer
|
33
32
|
end
|
34
33
|
end
|
35
34
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'whirled_peas/graphics/renderer'
|
2
2
|
require 'whirled_peas/utils/ansi'
|
3
|
+
require 'whirled_peas/device/rendered_frame'
|
3
4
|
|
4
5
|
module WhirledPeas
|
5
6
|
module Animator
|
@@ -9,23 +10,27 @@ module WhirledPeas
|
|
9
10
|
@device = device
|
10
11
|
@width = width
|
11
12
|
@height = height
|
12
|
-
@
|
13
|
+
@rendered_frames = []
|
14
|
+
@prev_pixel_grid = nil
|
13
15
|
end
|
14
16
|
|
15
17
|
def add_frameset(frameset)
|
16
|
-
frameset.each_frame do |frame, args|
|
18
|
+
frameset.each_frame do |frame, duration, args|
|
17
19
|
template = template_factory.build(frame, args)
|
18
|
-
|
20
|
+
pixel_grid = Graphics::Renderer.new(template, width, height).paint
|
21
|
+
strokes = prev_pixel_grid.nil? ? pixel_grid.to_s : pixel_grid.diff(prev_pixel_grid)
|
22
|
+
rendered_frames << Device::RenderedFrame.new(strokes, duration)
|
23
|
+
@prev_pixel_grid = pixel_grid
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
22
27
|
def process
|
23
|
-
device.
|
28
|
+
device.handle_rendered_frames(rendered_frames)
|
24
29
|
end
|
25
30
|
|
26
31
|
private
|
27
32
|
|
28
|
-
attr_reader :template_factory, :device, :width, :height, :
|
33
|
+
attr_reader :template_factory, :device, :width, :height, :rendered_frames, :prev_pixel_grid
|
29
34
|
end
|
30
35
|
end
|
31
36
|
end
|