module Sortviz # Canvas is the portion of the screen that contains the sorting bar charts. # It displays the partially sorted list of numbers and redraws on every iteration # marking the currently selected bar (current index in the partially sorted list) class Canvas extend Forwardable def_delegators :@window, :refresh, :getch, :close, :attron, :attroff MARGIN = 5 attr_reader :window # Initializes a new instance with a title to display, the current cursor # object (Sortviz::Cursor) and the modified screen dimensions from the # parent window/screen (standard screen created in Sortviz::Visualizer) def initialize(title, cursor, screen_dim) @screen_dim = screen_dim @cursor = cursor @title = title @red_highlight = Curses.color_pair(Curses.const_get("COLOR_RED")) end # Does the initial setup of creating an actual curses window, adding a # border to it and setting up non-blocking Curses::Window#getch def setup @window ||= Curses::Window.new( @screen_dim[:lines] - MARGIN, @screen_dim[:cols] - MARGIN, @cursor.y, @cursor.x) @window.nodelay = 1 # Non-blocking mode for #getch end # Draws the partially sorted list and highlights the current index # It also attempts to center the graph in the display area, does well but # not always, sometimes it'll be shifted to the right a bit. def draw(partially_sorted, selection) clear draw_title len = partially_sorted.join.center(4).length # We draw bottom up, this sets our y-position at the very bottom of # the canvas and our x-position half way through the canvas @cursor.move(@window.maxy - 1, (@window.maxx - len) / MARGIN) partially_sorted.each_with_index do |number, i| draw_number(number) draw_bar(number, i == selection) next_bar end Curses.doupdate end private def clear @window.clear @window.box('|', '-') end def draw_title @cursor.move(0, 0) @cursor.tprint("Algorithm: #{@title}") end def draw_number(number) @cursor.tprint(("|%02d|" % number)) @cursor.decr_y end def draw_bar(height, highlighted) attr = highlighted ? @red_highlight : Curses::A_REVERSE attron(attr) height.times do @cursor.tprint(" ".center(4)) # 4 spaces @cursor.decr_y end attroff(attr) end def next_bar @cursor.move_y(@window.maxy - 1) @cursor.incr_x MARGIN end end end