module Vedeu # Renders the provided border. # # @api private class RenderBorder extend Forwardable include Vedeu::Common def_delegators :border, :bottom?, :bottom_left, :bottom_right, :bx, :bxn, :by, :byn, :colour, :enabled?, :height, :horizontal, :left?, :name, :right?, :style, :title, :top?, :top_left, :top_right, :width, :vertical def_delegators :geometry, :x, :xn, :y, :yn # @return [Array>] # @see Vedeu::RenderBorder#initialize def self.with(border) new(border).render end # Returns a new instance of Vedeu::RenderBorder. # # @param border [Vedeu::Border] # @return [Vedeu::RenderBorder] def initialize(border) @border = border end # @return [Array>] def render return [] unless enabled? Vedeu.timer("Bordering: '#{name}'") do out = [top, bottom] height.times { |y| out << [left(y), right(y)] } out.flatten end end protected # @!attribute [r] border # @return [Vedeu::Border] attr_reader :border private # @param value [String] # @param type [Symbol|NilClass] # @param iy [Fixnum] # @param ix [Fixnum] # @return [Vedeu::Char] def build(value, type = :border, iy = 0, ix = 0) Vedeu::Char.new(value: value, parent: interface, colour: colour, style: style, position: [iy, ix], border: type) end # @return [Vedeu::Char] def build_bottom_left build(bottom_left, :bottom_left, *[yn, x]) if left? end # @return [Vedeu::Char] def build_bottom_right build(bottom_right, :bottom_right, *[yn, xn]) if right? end # @return [Vedeu::Char] def build_top_left build(top_left, :top_left, *[y, x]) if left? end # @return [Vedeu::Char] def build_top_right build(top_right, :top_right, *[y, xn]) if right? end # Renders the bottom border for the interface. # # @return [String] def bottom return [] unless bottom? [ build_bottom_left, build_horizontal(:bottom_horizontal, yn), build_bottom_right, ].compact end # @return [Vedeu::Geometry] def geometry Vedeu.geometries.by_name(name) end # @param position [Symbol] Either :top_horizontal, or :bottom_horizontal. # @param y_coordinate [Fixnum] The value of either y or yn. # @return [Array] def build_horizontal(position, y_coordinate) Array.new(width) do |ix| build(horizontal, position, *[y_coordinate, (bx + ix)]) end end # The parent of a border is always an interface. # # @return [Vedeu::Interface] def interface @interface ||= Vedeu.interfaces.by_name(name) end alias_method :parent, :interface # Renders the left border for the interface. # # @param iy [Fixnum] # @return [String] def left(iy = 0) return [] unless left? build(vertical, :left_vertical, *[(by + iy), x]) end # Renders the right border for the interface. # # @param iy [Fixnum] # @return [String] def right(iy = 0) return [] unless right? build(vertical, :right_vertical, *[(by + iy), xn]) end # Renders the top border for the interface. # # @note # If a title has been specified, then the top border will include this # title unless the size of the interface is smaller than the padded title # length. # # @return [String] def top return [] unless top? [build_top_left, titlebar, build_top_right].compact end # From the second element of {#title_characters} remove the border from each # {#build_horizontal} Vedeu::Char, and add the title character. # # @return [Array] def titlebar return build_horizontal(:top_horizontal, y) unless title? && title_fits? build_horizontal(:top_horizontal, y).each_with_index do |char, index| if index >= 1 && index <= title_characters.size char.border = nil char.value = title_characters[(index - 1)] end end end # Return boolean indicating whether this border has a non-empty title. # # @return [Boolean] def title? present?(title) end # Return boolean indicating whether the title fits within the width of the # top border. # # @return [Boolean] def title_fits? width > title_characters.size end # @return [Array] def title_characters @title_characters ||= title_padded.chars end # Pads the title with a single whitespace either side. # # @example # title = 'Truncated!' # width = 20 # # => ' Truncated! ' # # width = 10 # # => ' Trunca ' # # @return [String] # @see #truncated_title def title_padded truncated_title.center(truncated_title.size + 2) end # Truncates the title to the width of the interface, minus characters needed # to ensure there is at least a single character of horizontal border and a # whitespace on either side of the title. # # @example # title = 'Truncated!' # width = 20 # # => 'Truncated!' # # width = 10 # # => 'Trunca' # # @return [String] def truncated_title title.chomp.slice(0..(width - 5)) end end # RenderBorder end # Vedeu