lib/ruby_jard/screen.rb in ruby_jard-0.1.0 vs lib/ruby_jard/screen.rb in ruby_jard-0.2.0

- old
+ new

@@ -4,58 +4,136 @@ ## # A screen is a unit of information drawing on the terminal. Each screen is # generated based on input layout specifiation, screen data, and top-left # corner cordination. class Screen - attr_reader :output + attr_accessor :output, :rows, :width, :height, :x, :y - def initialize(layout:, output:, session:, row:, col:) - @output = output - @session = session - @layout = layout - @row = row - @col = col - @color_decorator = Pastel.new + def initialize(screen_template:, session: nil, width:, height:, x:, y:) + @session = session || RubyJard.current_session + @screen_template = screen_template + @width = width + @height = height + @x = x + @y = y end - def draw(_row, _col, _size) - raise NotImplementedError, "#{self.class} must implement #draw method" + def draw(output) + calculate + drawer = RubyJard::ScreenDrawer.new( + output: output, + screen: self, + x: @x, + y: @y + ) + drawer.draw end - def decorate_text - # TODO: this interface is ugly as fuck - RubyJard::Decorators::TextDecorator.new(@color_decorator) + def data_size + raise NotImplementedError, "#{self.class} must implement #data_size method" end - def decorate_path(path, lineno) - # TODO: this interface is ugly as fuck - RubyJard::Decorators::PathDecorator.new(path, lineno) + def data_window + raise NotImplementedError, "#{self.class} must implement #data_window method" end - def decorate_source(file, lineno, window) - # TODO: this interface is ugly as fuck - RubyJard::Decorators::SourceDecorator.new(file, lineno, window) + def calculate + @rows = [] + row_template = @screen_template.row_template + @rows = data_window.map.with_index do |data_row, index| + create_row(row_template, data_row, index) + end + column_widths = calculate_column_widths(row_template, @rows) + fill_column_widths(@rows, column_widths) end - def decorate_loc(loc, highlighted) - # TODO: this interface is ugly as fuck - RubyJard::Decorators::LocDecorator.new(@color_decorator, loc, highlighted) + private + + def calculate_column_widths(row_template, rows) + column_widths = {} + ideal_column_width = @width / row_template.columns.length + row_template.columns.each_with_index do |_column_template, column_index| + column_widths[column_index] ||= 0 + rows.each do |row| + column = row.columns[column_index] + if column.content_length > ideal_column_width + column_widths[column_index] = nil + break + elsif column.content_length > column_widths[column_index] + column_widths[column_index] = column.content_length + end + end + end + column_widths end - private + def fill_column_widths(rows, column_widths) + fixed_count = column_widths.length + fixed_width = column_widths.values.inject(0) do |sum, col| + col.nil? ? sum : sum + col + end - def default_frame_styles - { - style: { - fg: :white - }, - border: { - bottom_left: false, - bottom_right: false, - bottom: false, - left: :line, - right: false - } - } + rows.each do |row| + total_width = 0 + row.columns.each_with_index do |column, column_index| + column.width = + if column_index == row.columns.length - 1 + @width - total_width + elsif column_widths[column_index].nil? + (@width - fixed_width) / fixed_count + else + column_widths[column_index] + end + total_width += column.width + end + end + end + + def create_row(row_template, data_row, index) + row = Row.new(row_template: row_template) + row.columns = row_template.columns.map do |column_template| + create_column(column_template, data_row, index) + end + row + end + + def create_column(column_template, data_row, index) + column = Column.new(column_template: column_template) + column.spans = column_template.spans.map do |span_template| + create_span(span_template, data_row, index) + end.flatten + column.content_length = + column.spans.map(&:content_length).inject(&:+) + + column.margin_left + + column.margin_right + column + end + + def create_span(span_template, data_row, index) + span = Span.new(span_template: span_template) + span_content_method = "span_#{span_template.name}".to_sym + + if respond_to?(span_content_method) + content, styles = send(span_content_method, data_row, index) + if content.nil? + span.content = '' + span.content_length = 0 + elsif content.is_a?(Array) + content.each do |sub_span| + sub_span.styles += Array(styles).flatten.compact + end + return content + else + content = ' ' * span_template.margin_left + content if span_template.margin_left + content += ' ' * span_template.margin_right if span_template.margin_right + span.content = content + span.styles = Array(styles).flatten.compact + span.content_length = span.content.length + end + else + raise NotImplementedError, "#{self.class} must implement #{span_content_method} method" + end + + span end end end