lib/rbcurse/extras/bottomline.rb in rbcurse-1.3.0 vs lib/rbcurse/extras/bottomline.rb in rbcurse-1.4.0

- old
+ new

@@ -13,10 +13,15 @@ The character input routines are from io.rb, however, the user-interface to the input is copied from the Highline project (James Earl Gray) with permission. May later use a Label and Field. + NOTE : Pls avoid directly using this class. I am trying to redo this so ask, agree and say + can create their own window and be done with it. The hurdle in that is that ask calls + say, so when to close the window is not clear within say. Some shakeup is expected by + 1.4.0 or so. + =end module RubyCurses # just so the program does not bomb due to a tiny feature # I do not raise error on nil array, i create a dummy array @@ -81,12 +86,25 @@ attr_accessor :window attr_accessor :message_row attr_accessor :name # for debugging def initialize win=nil, row=nil @window = win - @message_row = row + #@window.wrefresh + #Ncurses::Panel.update_panels + #@message_row = row + @message_row = 0 # 2011-10-8 end + # + # create a window at bottom and show and hide it. + # Causing a stack overflow since Window creates a bottomline too ! + # + def _create_footer_window h = 1 , w = Ncurses.COLS, t = Ncurses.LINES-1, l = 0 + ewin = VER::Window.new(h, w , t, l) + #ewin.bkgd(Ncurses.COLOR_PAIR($promptcolor)); + @window = ewin + return ewin + end class QuestionError < StandardError # do nothing, just creating a unique error type end class Question @@ -945,11 +963,14 @@ "#{@validate.inspect})." ) end end def ask(question, answer_type=String, &details) - #clear_line 80 + $log.debug "XXXX inside ask win #{@window} " + #@window.show #unless @window.visible? + @window ||= _create_footer_window + @question ||= Question.new(question, answer_type, &details) say(@question) #unless @question.echo == true @completion_proc = @question.completion_proc @change_proc = @question.change_proc @@ -959,10 +980,11 @@ if @question.answer_type.is_a? Array @completion_proc = Proc.new{|str| @answer_type.dup.grep Regexp.new("^#{str}") } end begin + # FIXME a C-c still returns default to user ! @answer = @question.answer_or_default(get_response) unless @question.valid_answer?(@answer) explain_error(:not_valid) raise QuestionError end @@ -1008,14 +1030,42 @@ end retry rescue Question::NoAutoCompleteMatch explain_error(:no_completion) retry + rescue Interrupt + $log.warn "User interrupted ask() get_response does not want operation to proceed" + return nil ensure @question = nil # Reset Question object. + hide # assuming this method made it visible, not sure if this is called. end end + # + # bottomline user has to hide window if he called say(). + # Call this if you find the window persists after using some method from here + # usually say or ask. + # + # @param [int, float] time to sleep before hiding window. + # + def hide wait=nil + if @window + sleep(wait) if wait + @window.hide + @window.wrefresh + #Ncurses::Panel.update_panels + end + end + alias :hide_bottomline :hide + # + # destroy window, to be called by app when shutting down + # since we are normally hiding the window only. + def destroy + $log.debug "bottomline destroy... #{@window} " + @window.destroy if @window + @window = nil + end # # The basic output method for HighLine objects. # # The _statement_ parameter is processed as an ERb template, supporting @@ -1023,35 +1073,49 @@ # the HighLine instance. # NOTE: modified from original highline, does not care about space at end of # question. Also, ansi color constants will not work. Be careful what ruby code # you pass in. # + # NOTE: This uses a window, so it will persist in the last row. You must call + # hide_bottomline to remove the window. It is preferable to call say_with_pause + # from user programs + # def say statement, config={} + @window ||= _create_footer_window + #@window.show unless @window.visible? # this will need to be hidden manually NOTE + $log.debug "XXX: inside say win #{@window} !" case statement when Question if config.has_key? :color_pair $log.debug "INSIDE QUESTION 2 " if $log.debug? else - $log.debug "XXXX SAY using #{statement.color_pair} " if $log.debug? + $log.debug "XXXX SAY using colorpair: #{statement.color_pair} " if $log.debug? config[:color_pair] = statement.color_pair end else $log.debug "XXX INSDIE SAY #{statement.class} " if $log.debug? end statement = statement.to_str template = ERB.new(statement, nil, "%") statement = template.result(binding) - #puts statement + @prompt_length = statement.length # required by ask since it prints after @statement = statement # clear_line print_str statement, config end + # + # display some text at bottom and wait for a key before hiding window + # def say_with_pause statement, config={} + @window ||= _create_footer_window say statement, config + @window.wrefresh + Ncurses::Panel.update_panels ch=@window.getchar() + hide end # A helper method for sending the output stream and error and repeat # of the question. # # FIXME: since we write on one line in say, this often gets overidden @@ -1063,14 +1127,18 @@ elsif @question.responses[:ask_on_error] say(@question.responses[:ask_on_error]) end end + # + # Internal method for printing a string + # def print_str(text, config={}) win = config.fetch(:window, @window) # assuming its in App - x = config.fetch :x, @message_row # Ncurses.LINES-1 + x = config.fetch :x, 0 # @message_row # Ncurses.LINES-1, 0 since one line window 2011-10-8 y = config.fetch :y, 0 + $log.debug "XXX: print_str #{win} with text : #{text} at #{x} #{y} " color = config[:color_pair] || $datacolor raise "no window for ask print in #{self.class} name: #{name} " unless win color=Ncurses.COLOR_PAIR(color); win.attron(color); #win.mvprintw(x, y, "%-40s" % text); @@ -1149,16 +1217,18 @@ #next when KEY_LEFT curpos -= 1 if curpos > 0 len -= 1 if len > @prompt_length win.move r, c+len # since getchar is not going back on del and bs wmove to move FFIWINDOW + win.wrefresh next when KEY_RIGHT if curpos < str.length curpos += 1 #if curpos < str.length len += 1 win.move r, c+len # since getchar is not going back on del and bs + win.wrefresh end next when ?\C-a.getbyte(0) #olen = str.length clear_line len+maxlen+1, @prompt_length @@ -1321,10 +1391,11 @@ # noop, no echoing what is typed else print_str(@question.echo * str.length, :y => @prompt_length+0) end win.move r, c+len # more for arrow keys, curpos may not be end + win.wrefresh # 2011-10-10 prevchar = ch end $log.debug "XXXW bottomline: after while loop" str = default if str == "" @@ -1372,10 +1443,13 @@ # if @question.echo == true #and @question.limit.nil? ret, str = rbgetstr if ret == 0 return @question.change_case(@question.remove_whitespace(str)) end + if ret == -1 + raise Interrupt + end return "" end def agree( yes_or_no_question, character = nil ) ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q| q.validate = /\Ay(?:es)?|no?\Z/i @@ -1439,41 +1513,59 @@ $log.debug "NL2: #{retval} , #{retval.class} " retval end # Allows a selection in which options are shown over prompt. As user types # options are narrowed down. + # NOTE: For a directory we are not showing a slash, so currently you + # have to enter the slash manually when searching. # FIXME we can put remarks in fron as in memacs such as [No matches] or [single completion] # @param [Array] a list of items to select from # NOTE: if you use this please copy it to your app. This does not conform to highline's # choose, and I'd like to somehow get it to be identical. # def choose list1, config={} + dirlist = true case list1 when NilClass - list1 = Dir.glob("*") + #list1 = Dir.glob("*") + list1 = Dir.glob("*").collect { |f| File.directory?(f) ? f+"/" : f } when String - list1 = Dir.glob(list1) + list1 = Dir.glob(list1).collect { |f| File.directory?(f) ? f+"/" : f } when Array + dirlist = false # let it be, that's how it should come else # Dir listing as default - list1 = Dir.glob("*") + #list1 = Dir.glob("*") + list1 = Dir.glob("*").collect { |f| File.directory?(f) ? f+"/" : f } end require 'rbcurse/rcommandwindow' prompt = config[:prompt] || "Choose: " layout = { :height => 5, :width => Ncurses.COLS-1, :top => Ncurses.LINES-6, :left => 0 } rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title] begin w = rc.window rc.display_menu list1 # earlier wmove bombed, now move is (window.rb 121) - str = ask(prompt) { |q| q.change_proc = Proc.new { |str| w.wmove(1,1) ; w.wclrtobot; l = list1.select{|e| e.index(str)==0} ; rc.display_menu l; l} } + str = ask(prompt) { |q| q.change_proc = Proc.new { |str| w.wmove(1,1) ; w.wclrtobot; + l = list1.select{|e| e.index(str)==0} ; + if (l.size == 0 || str[-1]=='/') && dirlist + # used to help complete directories so we can drill up and down + #l = Dir.glob(str+"*") + l = Dir.glob(str +"*").collect { |f| File.directory?(f) ? f+"/" : f } + end + rc.display_menu l; + l + } } # need some validation here that its in the list TODO ensure rc.destroy rc = nil + hide_bottomline # since we called ask() we need to close bottomline end + hide_bottomline # since we called ask() we need to close bottomline + return str end def display_text_interactive text, config={} require 'rbcurse/rcommandwindow' ht = config[:height] || 15 layout = { :height => ht, :width => Ncurses.COLS-1, :top => Ncurses.LINES-ht+1, :left => 0 } @@ -1628,13 +1720,9 @@ #def_delegators :$tt, :ask, :say, :agree, :choose, :numbered_menu #end App.new do header = app_header "rbcurse 1.2.0", :text_center => "**** Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold message "Press F1 to exit from here" - ######## $tt.window = @window; $tt.message_row = @message_row - #@tt = Bottomline.new @window, @message_row - #extend Forwardable - #def_delegators :@tt, :ask, :say, :agree, :choose #stack :margin_top => 2, :margin => 5, :width => 30 do #end # stack #-----------------#------------------