module Vedeu module Terminal # All output will be written to this singleton, and #render will # be called at the end of each run of {Vedeu::MainLoop}; # effectively rendering this buffer to each registered renderer. # This buffer is not cleared after this action though, as # subsequent actions will modify the contents. This means that # individual parts of Vedeu can write content here at various # points and only at the end of each run of {Vedeu::MainLoop} will # it be actually output 'somewhere'. # module Buffer extend self # Return a grid of {Vedeu::Models::Cell} objects defined by the # height and width of this virtual terminal. # # @return [Array>] def buffer @output ||= Array.new(Vedeu.height + 1) do |y| Array.new(Vedeu.width + 1) do |x| Vedeu::Models::Cell.new(position: [y, x]) end end end alias_method :cells, :buffer # Clear the output. # # @example # Vedeu.clear # # @return [String|void] Most likely to be a String. def clear reset Vedeu.renderers.clear if Vedeu.ready? end # @return [Vedeu::Models::Page] def output Vedeu::Models::Page.coerce(buffer) end # Read a single cell from the virtual terminal. # # @note # Given two actual coordinates (y, x) e.g. (1, 1) # Convert to coordinate indices (cy, cx) e.g. (0, 0) # Fetch the row at cy and return the cell from cx # # @param y [Fixnum] The row/line coordinate. # @param x [Fixnum] The column/character coordinate. # @return [Vedeu::Views::Char] def read(y, x) cy, cx = Vedeu::Geometry::Position[y, x].as_indices row = fetch(cells, cy) cell = fetch(row, cx) cell end # Send the cells to the renderer and return the rendered result. # # @return [String|void] Most likely to be a String. def render Vedeu.renderers.render(output) if Vedeu.ready? end alias_method :refresh, :render # Removes all content from the virtual terminal; effectively # clearing it. # # @return [Array>] def reset @output = Array.new(Vedeu.height + 1) do |y| Array.new(Vedeu.width + 1) do |x| Vedeu::Models::Cell.new(position: [y, x]) end end end # Write a collection of cells to the virtual terminal, will # then send written content to be rendered by a renderer. # # @param value [Array>] # @return [Array>] def write(value) update_buffer(value) render self end # Write a collection of cells to the virtual terminal, but do # not send to a renderer. # # @param value [Array>] # @return [Array>] def update(value) update_buffer(value) self end private # @param from [Array] An Array of rows, or an Array of cells. # @param which [Fixnum] A Fixnum representing the index; the row # number or the cell number for a row. # @return [Array|Array] def fetch(from, which) from[which] || [] end # Returns a boolean indicating the value has a position # attribute. # # @param value [void] # @return [Boolean] def position?(value) value.respond_to?(:position) && value.position.is_a?(Vedeu::Geometry::Position) end # @param value [Array>] # @return [Array>] def update_buffer(value) values = Array(value).flatten values.each do |v| buffer[v.position.y][v.position.x] = v if valid_position?(v) end end # Returns a boolean indicating the value has a position # attribute and is within the terminal boundary. # # @param value [void] # @return [Boolean] def valid_position?(value) position?(value) && within_terminal_boundary?(value) end # Returns a boolean indicating the position of the value object # is valid for this terminal. # # @param value [void] # @return [Boolean] def within_terminal_boundary?(value) buffer[value.position.y] && buffer[value.position.y][value.position.x] end end # Buffer end # Terminal # @!method clear # @see Vedeu::Terminal::Buffer#clear def_delegators Vedeu::Terminal::Buffer, :clear, :refresh # :nocov: # See {file:docs/events/visibility.md#\_clear_} Vedeu.bind(:_clear_) { Vedeu.clear } # See {file:docs/events/drb.md#\_drb_retrieve_output_} Vedeu.bind(:_drb_retrieve_output_) { Vedeu::Terminal::Buffer.output } # See {file:docs/events/drb.md#\_drb_store_output_} Vedeu.bind(:_drb_store_output_) do |data| Vedeu::Terminal::Buffer.write(data) end # :nocov: end # Vedeu