Бисер обнови решението на 26.11.2014 08:13 (преди почти 10 години)
+module UI
+ class TextScreen
+ def self.draw(&block)
+ # base for the "stack"
+ @result = TextStructure.new
+ # call the block which modifies the @result
+ block.call if block_given?
+ # finally get the whole result
+ @result.eval_self
+ end
+
+ def self.label(text:, border: nil, style: nil)
+ # take the input and give it borders
+ my_result = "#{border}#{text}#{border}"
+ # apply a style overriding the ones in the stack if needed
+ my_result = StyleManager.apply_style(my_result, style)
+ # this is where actual adding of text to the result happens
+ @result.add_text my_result
+ end
+
+ def self.horizontal(border: nil, style: nil, &block)
+ # create a new "level" in the "stack"
+ @result = @result.create_subtext
+ # call the block if such is given
+ block.call if block_given?
+ # evaluate this result, glue it to the parent and go back in the "stack"
+ @result = @result.eval_self
+ end
+
+ def self.vertical(border: nil, style: nil, &block)
+ # create a new "level" in the "stack"
+ @result = @result.create_subtext
+ # call the block if such is given
+ block.call if block_given?
+ # calculate maximum length so borders can be put correctly
+ length = @result.text.each_line.max { |l| l.length }.length
+ # put borders
+ @result.text.each_line { |l| l = "#{border}#{l.ljust! length}#{border}" }
+ # evaluate this result, glue it to the parent and go back in the "stack"
+ @result = @result.eval_self
+ end
+ end
+
+ # many instances of this class create a stack-like structure
+ class TextStructure
+ # the text of the instance
+ attr_accessor :text
+ # instance of TextStructure (the stack-like structure)
+ attr_accessor :subtext
+ # each instance has a parent, except the first one
+ attr_accessor :parent
+
+ def initialize(parent = nil)
+ @parent = parent
+ end
+
+ def add_text(text)
+ @text += text
+ end
+
+ # creates a subtext and returns it so it can be assigned to @result
+ # the global @result in TextScreen must be evaluated
+ # after adding text is finished to return to this node
+ def create_subtext
+ @subtext = TextStructure.new self
+ end
+
+ # this is not needed actually, but it's here, get used to it (:
+ def evaluate_subtext
+ @text += @subtext.text
+ @subtext = nil
+ end
+
+ # adds this text to the parent's text
+ # clears the subtext of the parent
+ # and returns the parent so it can be assigned to @result
+ def evaluate_self
+ if not @parent then @text end
+ @parent.text += @text
+ @parent.subtext = nil
+ @parent
+ end
+ end
+
+ # a stack-like structure for storing the styles
+ # reading the top element "stack" is handled in a special way
+ class StyleManager
+ def self.initialize
+ @styles_stack = []
+ end
+
+ def self.push(style)
+ @styles_stack = [] if not defined? @styles_stack
+ @styles_stack.push style
+ end
+
+ def self.apply_style(text, style = nil)
+ if not defined? @styles_stack then @styles_stack = [] end
+ # drop through all nils
+# style = @styles_stack.reverse.drop_while { |i| not i }.first if not style
+ # apply the style and return
+ case style
+ when :upcase then text.upcase
+ when :downcase then text.downcase
+ else text
+ end
+ end
+
+ def self.pop
+ @styles_stack = [] if not defined? @styles_stack
+ @styles_stack.pop
+ end
+ end
+end