whirled_peas 0.4.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +37 -0
- data/README.md +529 -156
- data/Rakefile +9 -3
- data/bin/reset_cursor +11 -0
- data/bin/screen_test +68 -0
- data/examples/intro.rb +52 -0
- data/examples/scrolling.rb +54 -0
- data/lib/whirled_peas.rb +6 -12
- 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 +108 -0
- data/lib/whirled_peas/command/record.rb +57 -0
- data/lib/whirled_peas/command/still.rb +29 -0
- data/lib/whirled_peas/command_line.rb +23 -228
- 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/errors.rb +2 -0
- data/lib/whirled_peas/graphics.rb +19 -0
- data/lib/whirled_peas/graphics/box_painter.rb +101 -0
- data/lib/whirled_peas/graphics/canvas.rb +118 -0
- data/lib/whirled_peas/graphics/composer.rb +80 -0
- data/lib/whirled_peas/graphics/container_coords.rb +72 -0
- data/lib/whirled_peas/graphics/container_dimensions.rb +93 -0
- data/lib/whirled_peas/graphics/container_painter.rb +363 -0
- data/lib/whirled_peas/graphics/debugger.rb +52 -0
- data/lib/whirled_peas/graphics/grid_painter.rb +69 -0
- data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
- data/lib/whirled_peas/graphics/painter.rb +33 -0
- data/lib/whirled_peas/graphics/renderer.rb +32 -0
- data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
- data/lib/whirled_peas/graphics/text_painter.rb +40 -0
- data/lib/whirled_peas/settings.rb +5 -0
- data/lib/whirled_peas/settings/alignment.rb +24 -0
- data/lib/whirled_peas/settings/bg_color.rb +24 -0
- data/lib/whirled_peas/settings/border.rb +101 -0
- data/lib/whirled_peas/settings/box_settings.rb +8 -0
- data/lib/whirled_peas/settings/color.rb +68 -0
- data/lib/whirled_peas/settings/container_settings.rb +223 -0
- data/lib/whirled_peas/settings/debugger.rb +96 -0
- data/lib/whirled_peas/settings/display_flow.rb +27 -0
- data/lib/whirled_peas/settings/element_settings.rb +61 -0
- data/lib/whirled_peas/settings/grid_settings.rb +19 -0
- data/lib/whirled_peas/settings/margin.rb +8 -0
- data/lib/whirled_peas/settings/padding.rb +8 -0
- data/lib/whirled_peas/settings/position.rb +15 -0
- data/lib/whirled_peas/settings/scrollbar.rb +15 -0
- data/lib/whirled_peas/settings/sizing.rb +19 -0
- data/lib/whirled_peas/settings/spacing.rb +58 -0
- data/lib/whirled_peas/settings/text_color.rb +21 -0
- data/lib/whirled_peas/settings/text_settings.rb +15 -0
- data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
- data/lib/whirled_peas/utils/ansi.rb +19 -56
- data/lib/whirled_peas/utils/file_handler.rb +57 -0
- data/lib/whirled_peas/utils/formatted_string.rb +64 -0
- data/lib/whirled_peas/utils/title_font.rb +1 -1
- data/lib/whirled_peas/version.rb +1 -1
- data/screen_test/elements/box.frame +1 -0
- data/screen_test/elements/box.rb +20 -0
- data/screen_test/elements/grid.frame +1 -0
- data/screen_test/elements/grid.rb +13 -0
- data/screen_test/elements/screen_overflow_x.frame +1 -0
- data/screen_test/elements/screen_overflow_x.rb +9 -0
- data/screen_test/elements/screen_overflow_y.frame +1 -0
- data/screen_test/elements/screen_overflow_y.rb +9 -0
- data/screen_test/elements/text.frame +1 -0
- data/screen_test/elements/text.rb +9 -0
- data/screen_test/elements/text_multiline.frame +1 -0
- data/screen_test/elements/text_multiline.rb +9 -0
- data/screen_test/settings/align/box_around.frame +1 -0
- data/screen_test/settings/align/box_around.rb +16 -0
- data/screen_test/settings/align/box_between.frame +1 -0
- data/screen_test/settings/align/box_between.rb +16 -0
- data/screen_test/settings/align/box_center.frame +1 -0
- data/screen_test/settings/align/box_center.rb +21 -0
- data/screen_test/settings/align/box_default.frame +1 -0
- data/screen_test/settings/align/box_default.rb +20 -0
- data/screen_test/settings/align/box_evenly.frame +1 -0
- data/screen_test/settings/align/box_evenly.rb +16 -0
- data/screen_test/settings/align/box_left.frame +1 -0
- data/screen_test/settings/align/box_left.rb +21 -0
- data/screen_test/settings/align/box_right.frame +1 -0
- data/screen_test/settings/align/box_right.rb +21 -0
- data/screen_test/settings/align/children_center.frame +1 -0
- data/screen_test/settings/align/children_center.rb +15 -0
- data/screen_test/settings/align/children_left.frame +1 -0
- data/screen_test/settings/align/children_left.rb +15 -0
- data/screen_test/settings/align/children_right.frame +1 -0
- data/screen_test/settings/align/children_right.rb +15 -0
- data/screen_test/settings/align/grid_center.frame +1 -0
- data/screen_test/settings/align/grid_center.rb +18 -0
- data/screen_test/settings/align/grid_default.frame +1 -0
- data/screen_test/settings/align/grid_default.rb +17 -0
- data/screen_test/settings/align/grid_left.frame +1 -0
- data/screen_test/settings/align/grid_left.rb +18 -0
- data/screen_test/settings/align/grid_right.frame +1 -0
- data/screen_test/settings/align/grid_right.rb +18 -0
- data/screen_test/settings/ansi/bold.frame +1 -0
- data/screen_test/settings/ansi/bold.rb +14 -0
- data/screen_test/settings/ansi/color.frame +1 -0
- data/screen_test/settings/ansi/color.rb +37 -0
- data/screen_test/settings/ansi/underline.frame +1 -0
- data/screen_test/settings/ansi/underline.rb +14 -0
- data/screen_test/settings/border.frame +1 -0
- data/screen_test/settings/border.rb +13 -0
- data/screen_test/settings/flow/box_b2t.frame +1 -0
- data/screen_test/settings/flow/box_b2t.rb +26 -0
- data/screen_test/settings/flow/box_l2r.frame +1 -0
- data/screen_test/settings/flow/box_l2r.rb +26 -0
- data/screen_test/settings/flow/box_r2l.frame +1 -0
- data/screen_test/settings/flow/box_r2l.rb +26 -0
- data/screen_test/settings/flow/box_t2b.frame +1 -0
- data/screen_test/settings/flow/box_t2b.rb +26 -0
- data/screen_test/settings/flow/grid_b2t.frame +1 -0
- data/screen_test/settings/flow/grid_b2t.rb +14 -0
- data/screen_test/settings/flow/grid_l2r.frame +1 -0
- data/screen_test/settings/flow/grid_l2r.rb +14 -0
- data/screen_test/settings/flow/grid_r2l.frame +1 -0
- data/screen_test/settings/flow/grid_r2l.rb +14 -0
- data/screen_test/settings/flow/grid_t2b.frame +1 -0
- data/screen_test/settings/flow/grid_t2b.rb +14 -0
- data/screen_test/settings/height/box.frame +1 -0
- data/screen_test/settings/height/box.rb +13 -0
- data/screen_test/settings/height/box_border_sizing.frame +1 -0
- data/screen_test/settings/height/box_border_sizing.rb +15 -0
- data/screen_test/settings/height/grid.frame +1 -0
- data/screen_test/settings/height/grid.rb +14 -0
- data/screen_test/settings/height/overflow_box.frame +1 -0
- data/screen_test/settings/height/overflow_box.rb +13 -0
- data/screen_test/settings/height/overflow_box_l2r.frame +1 -0
- data/screen_test/settings/height/overflow_box_l2r.rb +17 -0
- data/screen_test/settings/height/overflow_box_t2b.frame +1 -0
- data/screen_test/settings/height/overflow_box_t2b.rb +16 -0
- data/screen_test/settings/height/overflow_grid.frame +1 -0
- data/screen_test/settings/height/overflow_grid.rb +16 -0
- data/screen_test/settings/margin.frame +1 -0
- data/screen_test/settings/margin.rb +16 -0
- data/screen_test/settings/padding.frame +1 -0
- data/screen_test/settings/padding.rb +13 -0
- data/screen_test/settings/position/box_left.frame +1 -0
- data/screen_test/settings/position/box_left.rb +17 -0
- data/screen_test/settings/position/box_left_negative.frame +1 -0
- data/screen_test/settings/position/box_left_negative.rb +17 -0
- data/screen_test/settings/position/box_top.frame +1 -0
- data/screen_test/settings/position/box_top.rb +17 -0
- data/screen_test/settings/position/box_top_negative.frame +1 -0
- data/screen_test/settings/position/box_top_negative.rb +17 -0
- data/screen_test/settings/position/grid_left.frame +1 -0
- data/screen_test/settings/position/grid_left.rb +18 -0
- data/screen_test/settings/position/grid_left_negative.frame +1 -0
- data/screen_test/settings/position/grid_left_negative.rb +18 -0
- data/screen_test/settings/position/grid_top.frame +1 -0
- data/screen_test/settings/position/grid_top.rb +18 -0
- data/screen_test/settings/position/grid_top_negative.frame +1 -0
- data/screen_test/settings/position/grid_top_negative.rb +18 -0
- data/screen_test/settings/scroll/horiz_box.frame +1 -0
- data/screen_test/settings/scroll/horiz_box.rb +17 -0
- data/screen_test/settings/scroll/horiz_box_align_center.rb +18 -0
- data/screen_test/settings/scroll/horiz_box_align_right.rb +18 -0
- data/screen_test/settings/scroll/vert_box.frame +1 -0
- data/screen_test/settings/scroll/vert_box.rb +20 -0
- data/screen_test/settings/title_font.frame +1 -0
- data/screen_test/settings/title_font.rb +12 -0
- data/screen_test/settings/valign/box_around.frame +1 -0
- data/screen_test/settings/valign/box_around.rb +17 -0
- data/screen_test/settings/valign/box_between.frame +1 -0
- data/screen_test/settings/valign/box_between.rb +17 -0
- data/screen_test/settings/valign/box_bottom.frame +1 -0
- data/screen_test/settings/valign/box_bottom.rb +17 -0
- data/screen_test/settings/valign/box_default.frame +1 -0
- data/screen_test/settings/valign/box_default.rb +16 -0
- data/screen_test/settings/valign/box_evenly.frame +1 -0
- data/screen_test/settings/valign/box_evenly.rb +17 -0
- data/screen_test/settings/valign/box_middle.frame +1 -0
- data/screen_test/settings/valign/box_middle.rb +17 -0
- data/screen_test/settings/valign/box_top.frame +1 -0
- data/screen_test/settings/valign/box_top.rb +17 -0
- data/screen_test/settings/valign/grid_bottom.frame +1 -0
- data/screen_test/settings/valign/grid_bottom.rb +15 -0
- data/screen_test/settings/valign/grid_default.frame +1 -0
- data/screen_test/settings/valign/grid_default.rb +14 -0
- data/screen_test/settings/valign/grid_middle.frame +1 -0
- data/screen_test/settings/valign/grid_middle.rb +15 -0
- data/screen_test/settings/valign/grid_top.frame +1 -0
- data/screen_test/settings/valign/grid_top.rb +15 -0
- data/screen_test/settings/width/box_border_sizing.frame +1 -0
- data/screen_test/settings/width/box_border_sizing.rb +15 -0
- data/screen_test/settings/width/box_content.frame +1 -0
- data/screen_test/settings/width/box_content.rb +15 -0
- data/screen_test/settings/width/box_default.frame +1 -0
- data/screen_test/settings/width/box_default.rb +14 -0
- data/screen_test/settings/width/grid.frame +1 -0
- data/screen_test/settings/width/grid.rb +14 -0
- data/screen_test/settings/width/overflow_align_center.frame +1 -0
- data/screen_test/settings/width/overflow_align_center.rb +14 -0
- data/screen_test/settings/width/overflow_align_right.frame +1 -0
- data/screen_test/settings/width/overflow_align_right.rb +14 -0
- data/screen_test/settings/width/overflow_box.frame +1 -0
- data/screen_test/settings/width/overflow_box.rb +13 -0
- data/screen_test/settings/width/overflow_box_l2r.frame +1 -0
- data/screen_test/settings/width/overflow_box_l2r.rb +16 -0
- data/screen_test/settings/width/overflow_box_t2b.frame +1 -0
- data/screen_test/settings/width/overflow_box_t2b.rb +17 -0
- data/screen_test/settings/width/overflow_grid.frame +1 -0
- data/screen_test/settings/width/overflow_grid.rb +14 -0
- data/tools/whirled_peas/tools/screen_tester.rb +285 -0
- metadata +213 -15
- data/bin/title_fonts +0 -6
- data/lib/whirled_peas/frame.rb +0 -7
- data/lib/whirled_peas/frame/event_loop.rb +0 -91
- data/lib/whirled_peas/frame/print_consumer.rb +0 -33
- data/lib/whirled_peas/frame/producer.rb +0 -61
- data/lib/whirled_peas/template.rb +0 -5
- data/lib/whirled_peas/template/element.rb +0 -230
- data/lib/whirled_peas/template/settings.rb +0 -530
- data/lib/whirled_peas/ui.rb +0 -5
- data/lib/whirled_peas/ui/canvas.rb +0 -68
- data/lib/whirled_peas/ui/painter.rb +0 -287
- data/lib/whirled_peas/ui/screen.rb +0 -63
- data/lib/whirled_peas/utils/color.rb +0 -101
    
        data/Rakefile
    CHANGED
    
    | @@ -1,6 +1,12 @@ | |
| 1 | 
            -
            require  | 
| 2 | 
            -
            require  | 
| 1 | 
            +
            require 'bundler/gem_tasks'
         | 
| 2 | 
            +
            require 'rspec/core/rake_task'
         | 
| 3 3 |  | 
| 4 4 | 
             
            RSpec::Core::RakeTask.new(:spec)
         | 
| 5 5 |  | 
| 6 | 
            -
            task : | 
| 6 | 
            +
            task :screen_test do
         | 
| 7 | 
            +
              require_relative 'tools/whirled_peas/tools/screen_tester'
         | 
| 8 | 
            +
              WhirledPeas::Tools::ScreenTester.run_all
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            task ci: %i[spec screen_test]
         | 
| 12 | 
            +
            task default: :ci
         | 
    
        data/bin/reset_cursor
    ADDED
    
    
    
        data/bin/screen_test
    ADDED
    
    | @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'whirled_peas'
         | 
| 5 | 
            +
            require 'whirled_peas/utils/formatted_string'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require_relative '../tools/whirled_peas/tools/screen_tester'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            def print_usage(exit_code=0)
         | 
| 10 | 
            +
              puts "Usage: #{$0} [file] [options]"
         | 
| 11 | 
            +
              puts
         | 
| 12 | 
            +
              puts 'If not file or options are provide, all tests are run'
         | 
| 13 | 
            +
              puts
         | 
| 14 | 
            +
              puts 'If no file is provided, the supported options are'
         | 
| 15 | 
            +
              puts '    --help             print this usage statement and exit'
         | 
| 16 | 
            +
              puts '    --view-pending     interactively display and optionally save rendered output for each pending test'
         | 
| 17 | 
            +
              puts '    --view-failed      interactively display and optionally save rendered output for each faiing test'
         | 
| 18 | 
            +
              puts
         | 
| 19 | 
            +
              puts 'If a screen test file is provided as the first argument, the supported options are'
         | 
| 20 | 
            +
              puts '    --run        run screen test for given file'
         | 
| 21 | 
            +
              puts '    --view       interactively display and optionally save the file\'s test output'
         | 
| 22 | 
            +
              puts '    --template   print out template tree for the test template'
         | 
| 23 | 
            +
              puts '    --debug      render the test template without displying it, printing out debug information'
         | 
| 24 | 
            +
              exit(exit_code)
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            module WhirledPeas
         | 
| 28 | 
            +
              if ARGV.length == 0
         | 
| 29 | 
            +
                Tools::ScreenTester.run_all
         | 
| 30 | 
            +
              elsif ARGV.length == 1
         | 
| 31 | 
            +
                option = ARGV[0]
         | 
| 32 | 
            +
                case option
         | 
| 33 | 
            +
                when '--help'
         | 
| 34 | 
            +
                  print_usage
         | 
| 35 | 
            +
                when '--view-pending'
         | 
| 36 | 
            +
                  Tools::ScreenTester.view_pending
         | 
| 37 | 
            +
                when '--view-failed'
         | 
| 38 | 
            +
                  Tools::ScreenTester.view_failed
         | 
| 39 | 
            +
                else
         | 
| 40 | 
            +
                  print_usage(1)
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              elsif ARGV.length == 2
         | 
| 43 | 
            +
                file, option = ARGV
         | 
| 44 | 
            +
                case option
         | 
| 45 | 
            +
                when '--run'
         | 
| 46 | 
            +
                  tester = Tools::ScreenTester.new(file)
         | 
| 47 | 
            +
                  if tester.pending?
         | 
| 48 | 
            +
                    puts Utils::FormattedString.new('Pending', Settings::TextColor::YELLOW)
         | 
| 49 | 
            +
                    puts "Run `#{$0} #{file} --view` to view and optionally save ouput."
         | 
| 50 | 
            +
                  elsif tester.failed?
         | 
| 51 | 
            +
                    puts Utils::FormattedString.new('Failed', Settings::TextColor::RED)
         | 
| 52 | 
            +
                    puts "Run `#{$0} #{file} --view` to compare and optionally save ouput."
         | 
| 53 | 
            +
                  else
         | 
| 54 | 
            +
                    puts Utils::FormattedString.new('Pass', Settings::TextColor::GREEN)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                when '--view'
         | 
| 57 | 
            +
                  Tools::ScreenTester.new(file).view
         | 
| 58 | 
            +
                when '--template'
         | 
| 59 | 
            +
                  Tools::ScreenTester.new(file).template
         | 
| 60 | 
            +
                when '--debug'
         | 
| 61 | 
            +
                  Tools::ScreenTester.new(file).debug
         | 
| 62 | 
            +
                else
         | 
| 63 | 
            +
                  print_usage(1)
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              else
         | 
| 66 | 
            +
                print_usage(1)
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
    
        data/examples/intro.rb
    ADDED
    
    | @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            require 'bundler/setup'
         | 
| 2 | 
            +
            require 'whirled_peas'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class TemplateFactory
         | 
| 5 | 
            +
              TITLE_FONT = :default #roman
         | 
| 6 | 
            +
              PRIMARY_COLOR = :bright_blue
         | 
| 7 | 
            +
              SECONDARY_COLOR = :blue
         | 
| 8 | 
            +
              BG_COLOR = :bright_white
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def build(name, args)
         | 
| 11 | 
            +
                WhirledPeas.template do |composer, settings|
         | 
| 12 | 
            +
                  settings.set_padding(left: 10, top: 6, right: 10, bottom: 6)
         | 
| 13 | 
            +
                  settings.align = :center
         | 
| 14 | 
            +
                  settings.width = 120
         | 
| 15 | 
            +
                  settings.flow = :t2b
         | 
| 16 | 
            +
                  settings.bold = true
         | 
| 17 | 
            +
                  settings.bg_color = BG_COLOR
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  composer.add_box do |composer, settings|
         | 
| 20 | 
            +
                    composer.add_text do |_, settings|
         | 
| 21 | 
            +
                      settings.title_font = TITLE_FONT
         | 
| 22 | 
            +
                      settings.color = PRIMARY_COLOR
         | 
| 23 | 
            +
                      'Visualize'
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                  composer.add_box do |_, settings|
         | 
| 27 | 
            +
                    settings.set_margin(top: 1)
         | 
| 28 | 
            +
                    settings.color = SECONDARY_COLOR
         | 
| 29 | 
            +
                    "your code's execution with"
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                  composer.add_box do |composer, settings|
         | 
| 32 | 
            +
                    settings.set_margin(top: 1)
         | 
| 33 | 
            +
                    composer.add_text do |_, settings|
         | 
| 34 | 
            +
                      settings.title_font = TITLE_FONT
         | 
| 35 | 
            +
                      settings.color = PRIMARY_COLOR
         | 
| 36 | 
            +
                      'Whirled Peas'
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            class Application
         | 
| 44 | 
            +
              def start(producer)
         | 
| 45 | 
            +
                producer.add_frame('intro', duration: 5)
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            WhirledPeas.configure do |config|
         | 
| 50 | 
            +
              config.template_factory = TemplateFactory.new
         | 
| 51 | 
            +
              config.application = Application.new
         | 
| 52 | 
            +
            end
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            require 'bundler/setup'
         | 
| 2 | 
            +
            require 'whirled_peas'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class TemplateFactory
         | 
| 5 | 
            +
              HPADDING = 0
         | 
| 6 | 
            +
              VPADDING = 0
         | 
| 7 | 
            +
              ITEM_COUNT = 64
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def build(name, args)
         | 
| 10 | 
            +
                top = args[:top]
         | 
| 11 | 
            +
                WhirledPeas.template do |composer, settings|
         | 
| 12 | 
            +
                  settings.color = :green
         | 
| 13 | 
            +
                  composer.add_box('Vert') do |composer, settings|
         | 
| 14 | 
            +
                    settings.bg_color = :bright_red
         | 
| 15 | 
            +
                    settings.full_border(color: :blue)
         | 
| 16 | 
            +
                    settings.set_scrollbar(vert: true)
         | 
| 17 | 
            +
                    settings.height = 12
         | 
| 18 | 
            +
                    composer.add_box('Inner') do |composer, settings|
         | 
| 19 | 
            +
                      settings.flow = :t2b
         | 
| 20 | 
            +
                      settings.set_padding(left: HPADDING, right: HPADDING, top: VPADDING, bottom: VPADDING)
         | 
| 21 | 
            +
                      settings.set_position(top: top)
         | 
| 22 | 
            +
                      ITEM_COUNT.times do |i|
         | 
| 23 | 
            +
                        composer.add_text { "%2d" % i }
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                  composer.add_box('Horiz') do |composer, settings|
         | 
| 28 | 
            +
                    settings.bg_color = :bright_red
         | 
| 29 | 
            +
                    settings.full_border(color: :blue)
         | 
| 30 | 
            +
                    settings.set_padding(left: HPADDING, right: HPADDING, top: VPADDING, bottom: VPADDING)
         | 
| 31 | 
            +
                    settings.set_scrollbar(horiz: true)
         | 
| 32 | 
            +
                    settings.width = 12
         | 
| 33 | 
            +
                    composer.add_box('Inner') do |composer, settings|
         | 
| 34 | 
            +
                      settings.set_position(left: top)
         | 
| 35 | 
            +
                      ITEM_COUNT.times.map { |i| (i % 10).to_s }.join
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            class Application
         | 
| 43 | 
            +
              def start(producer)
         | 
| 44 | 
            +
                producer.frameset(5, easing: :bezier) do |fs|
         | 
| 45 | 
            +
                  53.times { |i| fs.add_frame('intro', { top: -i }) }
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
                producer.add_frame('hold', duration: 1, args: { top: -52})
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
            end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            WhirledPeas.configure do |config|
         | 
| 52 | 
            +
              config.template_factory = TemplateFactory.new
         | 
| 53 | 
            +
              config.application = Application.new
         | 
| 54 | 
            +
            end
         | 
    
        data/lib/whirled_peas.rb
    CHANGED
    
    | @@ -1,11 +1,8 @@ | |
| 1 | 
            -
            require ' | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'whirled_peas/errors'
         | 
| 4 | 
            -
             | 
| 1 | 
            +
            require 'whirled_peas/animator'
         | 
| 5 2 | 
             
            require 'whirled_peas/config'
         | 
| 6 | 
            -
            require 'whirled_peas/ | 
| 7 | 
            -
            require 'whirled_peas/ | 
| 8 | 
            -
            require 'whirled_peas/ | 
| 3 | 
            +
            require 'whirled_peas/errors'
         | 
| 4 | 
            +
            require 'whirled_peas/graphics'
         | 
| 5 | 
            +
            require 'whirled_peas/settings'
         | 
| 9 6 | 
             
            require 'whirled_peas/utils'
         | 
| 10 7 | 
             
            require 'whirled_peas/version'
         | 
| 11 8 |  | 
| @@ -19,10 +16,7 @@ module WhirledPeas | |
| 19 16 | 
             
              end
         | 
| 20 17 |  | 
| 21 18 | 
             
              def self.template(&block)
         | 
| 22 | 
            -
                require 'whirled_peas/ | 
| 23 | 
            -
             | 
| 24 | 
            -
                template = UI::Template.new
         | 
| 25 | 
            -
                yield template, template.settings
         | 
| 26 | 
            -
                template
         | 
| 19 | 
            +
                require 'whirled_peas/graphics/composer'
         | 
| 20 | 
            +
                Graphics::Composer.build(&block)
         | 
| 27 21 | 
             
              end
         | 
| 28 22 | 
             
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            module WhirledPeas
         | 
| 2 | 
            +
              module Animator
         | 
| 3 | 
            +
                class DebugConsumer
         | 
| 4 | 
            +
                  def add_frameset(frameset)
         | 
| 5 | 
            +
                    require 'json'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    frameset.each_frame do |frame, args|
         | 
| 8 | 
            +
                      puts [frame, *(JSON.generate(args) unless args.empty?)].join(' ')
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def process
         | 
| 13 | 
            +
                    # no op
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,72 @@ | |
| 1 | 
            +
            module WhirledPeas
         | 
| 2 | 
            +
              module Animator
         | 
| 3 | 
            +
                class Easing
         | 
| 4 | 
            +
                  # Implementations of available ease-in functions. Ease-out and ease-in-out can all be
         | 
| 5 | 
            +
                  # derived from ease-in.
         | 
| 6 | 
            +
                  EASING = {
         | 
| 7 | 
            +
                    bezier: proc do |value|
         | 
| 8 | 
            +
                      value /= 2
         | 
| 9 | 
            +
                      2 * value * value * (3 - 2 * value)
         | 
| 10 | 
            +
                    end,
         | 
| 11 | 
            +
                    linear: proc { |value| value },
         | 
| 12 | 
            +
                    parametric: proc do |value|
         | 
| 13 | 
            +
                      value /= 2
         | 
| 14 | 
            +
                      squared = value * value
         | 
| 15 | 
            +
                      2 * squared / (2 * (squared - value) + 1)
         | 
| 16 | 
            +
                    end,
         | 
| 17 | 
            +
                    quadratic: proc do |value|
         | 
| 18 | 
            +
                      value * value
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  }
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  EFFECTS = %i[in out in_out]
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def initialize(easing=:linear, effect=:in_out)
         | 
| 25 | 
            +
                    unless EASING.key?(easing)
         | 
| 26 | 
            +
                      raise ArgumentError,
         | 
| 27 | 
            +
                            "Invalid easing function: #{easing}, expecting one of #{EASING.keys.join(', ')}"
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                    unless EFFECTS.include?(effect)
         | 
| 30 | 
            +
                      raise ArgumentError,
         | 
| 31 | 
            +
                            "Invalid effect: #{effect}, expecting one of #{EFFECTS.join(', ')}"
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                    @easing = easing
         | 
| 34 | 
            +
                    @effect = effect
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def ease(value)
         | 
| 38 | 
            +
                    case effect
         | 
| 39 | 
            +
                    when :in
         | 
| 40 | 
            +
                      ease_in(value)
         | 
| 41 | 
            +
                    when :out
         | 
| 42 | 
            +
                      ease_out(value)
         | 
| 43 | 
            +
                    else
         | 
| 44 | 
            +
                      ease_in_out(value)
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  private
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  attr_reader :easing, :effect
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  # The procs in EASING define the ease-in functions, so we simply need
         | 
| 53 | 
            +
                  # to invoke the function with the given normalized value
         | 
| 54 | 
            +
                  def ease_in(value)
         | 
| 55 | 
            +
                    EASING[easing].call(value)
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  # The ease-out function will be the ease-in function rotated 180 degrees.
         | 
| 59 | 
            +
                  def ease_out(value)
         | 
| 60 | 
            +
                    1 - EASING[easing].call(1 - value)
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def ease_in_out(value)
         | 
| 64 | 
            +
                    if value < 0.5
         | 
| 65 | 
            +
                      ease_in(value * 2) / 2
         | 
| 66 | 
            +
                    else
         | 
| 67 | 
            +
                      0.5 + ease_out(2 * value - 1) / 2
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            require_relative 'easing'
         | 
| 2 | 
            +
            require_relative 'frame'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module WhirledPeas
         | 
| 5 | 
            +
              module Animator
         | 
| 6 | 
            +
                class Frameset
         | 
| 7 | 
            +
                  def initialize(frame_slots, easing, effect)
         | 
| 8 | 
            +
                    @frame_slots = frame_slots
         | 
| 9 | 
            +
                    @easing = Easing.new(easing, effect)
         | 
| 10 | 
            +
                    @frames = []
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def add_frame(name, args={})
         | 
| 14 | 
            +
                    frames << [name, args]
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  # Yield each frame in an "eased" order
         | 
| 18 | 
            +
                  def each_frame(&block)
         | 
| 19 | 
            +
                    frame_slots.times do |i|
         | 
| 20 | 
            +
                      input = i.to_f / (frame_slots - 1)
         | 
| 21 | 
            +
                      eased_value = @easing.ease(input)
         | 
| 22 | 
            +
                      index = (eased_value * (frames.length - 1)).floor
         | 
| 23 | 
            +
                      yield *frames[index]
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  private
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  attr_reader :frame_slots, :easing, :frames
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                private_constant :Frameset
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            require_relative 'easing'
         | 
| 2 | 
            +
            require_relative 'frameset'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module WhirledPeas
         | 
| 5 | 
            +
              module Animator
         | 
| 6 | 
            +
                class Producer
         | 
| 7 | 
            +
                  def self.produce(consumer, refresh_rate)
         | 
| 8 | 
            +
                    producer = new(consumer, refresh_rate)
         | 
| 9 | 
            +
                    yield producer
         | 
| 10 | 
            +
                    consumer.process
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def initialize(consumer, refresh_rate)
         | 
| 14 | 
            +
                    @consumer = consumer
         | 
| 15 | 
            +
                    @refresh_rate = refresh_rate
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def add_frame(name, duration: nil, args: {})
         | 
| 19 | 
            +
                    frameset(duration || 1 / refresh_rate) do |fs|
         | 
| 20 | 
            +
                      fs.add_frame(name, args)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def frameset(duration, easing: :linear, effect: :in_out, &block)
         | 
| 25 | 
            +
                    fs = Frameset.new((duration * refresh_rate).round, easing, effect)
         | 
| 26 | 
            +
                    yield fs
         | 
| 27 | 
            +
                    consumer.add_frameset(fs)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  attr_reader :consumer, :refresh_rate
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            require 'whirled_peas/graphics/renderer'
         | 
| 2 | 
            +
            require 'whirled_peas/utils/ansi'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module WhirledPeas
         | 
| 5 | 
            +
              module Animator
         | 
| 6 | 
            +
                class RendererConsumer
         | 
| 7 | 
            +
                  def initialize(template_factory, device, width, height)
         | 
| 8 | 
            +
                    @template_factory = template_factory
         | 
| 9 | 
            +
                    @device = device
         | 
| 10 | 
            +
                    @width = width
         | 
| 11 | 
            +
                    @height = height
         | 
| 12 | 
            +
                    @renders = []
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def add_frameset(frameset)
         | 
| 16 | 
            +
                    frameset.each_frame do |frame, args|
         | 
| 17 | 
            +
                      template = template_factory.build(frame, args)
         | 
| 18 | 
            +
                      renders << Graphics::Renderer.new(template, width, height).paint
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def process
         | 
| 23 | 
            +
                    device.handle_renders(renders)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  private
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  attr_reader :template_factory, :device, :width, :height, :renders
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         |