]> git.cworth.org Git - sup/blobdiff - lib/sup/textfield.rb
finally, attachment support\!
[sup] / lib / sup / textfield.rb
index 198de127e6e5565d34571c6296adeff95b859887..bdd1228882e0bc552cd4b855961d1b6864516795 100644 (file)
@@ -2,26 +2,47 @@ require 'curses'
 
 module Redwood
 
+## a fully-functional text field supporting completions, expansions,
+## history--everything!
+##
+## completion is done emacs-style, and mostly depends on outside
+## support, as we merely signal the existence of a new set of
+## completions to show (#new_completions?)  or that the current list
+## of completions should be rolled if they're too large to fill the
+## screen (#roll_completions?).
+##
+## in sup, completion support is implemented through BufferManager#ask
+## and CompletionMode.
 class TextField
-  attr_reader :value
-
   def initialize window, y, x, width
     @w, @x, @y = window, x, y
     @width = width
     @i = nil
     @history = []
+
+    @completion_block = nil
+    reset_completion_state
   end
 
-  def activate question, default=nil
+  bool_reader :new_completions, :roll_completions
+  attr_reader :completions
+
+  ## when the user presses enter, we store the value in @value and
+  ## clean up all the ncurses cruft. before @value is set, we can
+  ## get the current value from ncurses.
+  def value; @field ? get_cur_value : @value end
+
+  def activate question, default=nil, &block
     @question = question
     @value = nil
+    @completion_block = block
     @field = Ncurses::Form.new_field 1, @width - question.length,
                                      @y, @x + question.length, 0, 0
     @form = Ncurses::Form.new_form [@field]
 
     @history[@i = @history.size] = default || ""
     Ncurses::Form.post_form @form
-    @field.set_field_buffer 0, @history[@i]
+    set_cur_value @history[@i]
   end
 
   def position_cursor
@@ -33,24 +54,47 @@ class TextField
   end
 
   def deactivate
+    reset_completion_state
     @form.unpost_form
     @form.free_form
     @field.free_field
+    @field = nil
     Ncurses.curs_set 0
   end
 
   def handle_input c
-    if c == 10 # Ncurses::KEY_ENTER
-      Ncurses::Form.form_driver @form, Ncurses::Form::REQ_VALIDATION
-      @value = @history[@i] = @field.field_buffer(0).gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
+    ## short-circuit exit paths
+    case c
+    when Ncurses::KEY_ENTER # submit!
+      @value = @history[@i] = get_cur_value
       return false
-    elsif c == Ncurses::KEY_CANCEL
+    when Ncurses::KEY_CANCEL # cancel
       @history.delete_at @i
       @i = @history.empty? ? nil : (@i - 1) % @history.size 
       @value = nil
       return false
+    when Ncurses::KEY_TAB # completion
+      break unless @completion_block
+      if @completions.empty?
+        v = get_cur_value
+        c = @completion_block.call v
+        if c.size > 0
+          set_cur_value c.map { |full, short| full }.shared_prefix
+        end
+        if c.size > 1
+          @completions = c
+          @new_completions = true
+          @roll_completions = false
+        end
+      else
+        @new_completions = false
+        @roll_completions = true
+      end
+      return true
     end
 
+    reset_completion_state
+
     d =
       case c
       when Ncurses::KEY_LEFT
@@ -64,21 +108,39 @@ class TextField
       when ?\005
         Ncurses::Form::REQ_END_FIELD
       when Ncurses::KEY_UP
-        @history[@i] = @field.field_buffer(0)
+        @history[@i] = @field.field_buffer 0
         @i = (@i - 1) % @history.size
-        @field.set_field_buffer 0, @history[@i]
+        set_cur_value @history[@i]
       when Ncurses::KEY_DOWN
-        @history[@i] = @field.field_buffer(0)
+        @history[@i] = @field.field_buffer 0
         @i = (@i + 1) % @history.size
-        @field.set_field_buffer 0, @history[@i]
+        set_cur_value @history[@i]
       else
         c
       end
 
     Ncurses::Form.form_driver @form, d
-    Ncurses.refresh
-
     true
   end
+
+private
+
+  def reset_completion_state
+    @completions = []
+    @new_completions = @roll_completions = @clear_completions = false
+  end
+
+  ## ncurses inanity wrapper
+  def get_cur_value
+    Ncurses::Form.form_driver @form, Ncurses::Form::REQ_VALIDATION
+    @field.field_buffer(0).gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
+  end
+  
+  ## ncurses inanity wrapper
+  def set_cur_value v
+    @field.set_field_buffer 0, v
+    Ncurses::Form.form_driver @form, Ncurses::Form::REQ_END_FIELD
+  end
+
 end
 end