]> git.cworth.org Git - sup/blob - lib/sup/textfield.rb
bugfix in question asking: update position when window is resized
[sup] / lib / sup / textfield.rb
1 module Redwood
2
3 ## a fully-functional text field supporting completions, expansions,
4 ## history--everything!
5 ## 
6 ## writing this fucking sucked. if you thought ncurses was some 1970s
7 ## before-people-knew-how-to-program bullshit, wait till you see
8 ## ncurses forms.
9 ##
10 ## completion comments: completion is done emacs-style, and mostly
11 ## depends on outside support, as we merely signal the existence of a
12 ## new set of completions to show (#new_completions?)  or that the
13 ## current list of completions should be rolled if they're too large
14 ## to fill the screen (#roll_completions?).
15 ##
16 ## in sup, completion support is implemented through BufferManager#ask
17 ## and CompletionMode.
18 class TextField
19   def initialize
20     @i = nil
21     @history = []
22
23     @completion_block = nil
24     reset_completion_state
25   end
26
27   bool_reader :new_completions, :roll_completions
28   attr_reader :completions
29
30   def value; @value || get_cursed_value end
31
32   def activate window, y, x, width, question, default=nil, &block
33     @w, @y, @x, @width = window, y, x, width
34     @question = question
35     @completion_block = block
36     @field = Ncurses::Form.new_field 1, @width - question.length,
37                                      @y, @x + question.length, 0, 0
38     @form = Ncurses::Form.new_form [@field]
39     @value = default
40     Ncurses::Form.post_form @form
41     set_cursed_value default if default
42   end
43
44   def position_cursor
45     @w.attrset Colormap.color_for(:none)
46     @w.mvaddstr @y, 0, @question
47     Ncurses.curs_set 1
48     Ncurses::Form.form_driver @form, Ncurses::Form::REQ_END_FIELD
49     Ncurses::Form.form_driver @form, Ncurses::Form::REQ_NEXT_CHAR if @value && @value =~ / $/ # fucking RETARDED!!!!
50   end
51
52   def deactivate
53     reset_completion_state
54     @form.unpost_form
55     @form.free_form
56     @field.free_field
57     @field = nil
58     Ncurses.curs_set 0
59   end
60
61   def handle_input c
62     ## short-circuit exit paths
63     case c
64     when Ncurses::KEY_ENTER # submit!
65       @value = get_cursed_value
66       @history.push @value unless @value =~ /^\s*$/
67       return false
68     when Ncurses::KEY_CANCEL # cancel
69       @value = nil
70       return false
71     when Ncurses::KEY_TAB # completion
72       return true unless @completion_block
73       if @completions.empty?
74         v = get_cursed_value
75         c = @completion_block.call v
76         if c.size > 0
77           @value = c.map { |full, short| full }.shared_prefix(true)
78           set_cursed_value @value
79           position_cursor
80         end
81         if c.size > 1
82           @completions = c
83           @new_completions = true
84           @roll_completions = false
85         end
86       else
87         @new_completions = false
88         @roll_completions = true
89       end
90       return true
91     end
92
93     reset_completion_state
94     @value = nil
95
96     d =
97       case c
98       when Ncurses::KEY_LEFT
99         Ncurses::Form::REQ_PREV_CHAR
100       when Ncurses::KEY_RIGHT
101         Ncurses::Form::REQ_NEXT_CHAR
102       when Ncurses::KEY_BACKSPACE
103         Ncurses::Form::REQ_DEL_PREV
104       when 1 #ctrl-a
105         Ncurses::Form::REQ_BEG_FIELD
106       when 5 #ctrl-e
107         Ncurses::Form::REQ_END_FIELD
108       when 11 # ctrl-k
109         Ncurses::Form::REQ_CLR_EOF
110       when Ncurses::KEY_UP, Ncurses::KEY_DOWN
111         unless @history.empty?
112           value = get_cursed_value
113           @i ||= @history.size
114           #Redwood::log "history before #{@history.inspect}"
115           @history[@i] = value #unless value =~ /^\s*$/
116           @i = (@i + (c == Ncurses::KEY_UP ? -1 : 1)) % @history.size
117           @value = @history[@i]
118           #Redwood::log "history after #{@history.inspect}"
119           set_cursed_value @value
120           Ncurses::Form::REQ_END_FIELD
121         end
122       else
123         c
124       end
125
126     Ncurses::Form.form_driver @form, d if d
127     true
128   end
129
130 private
131
132   def reset_completion_state
133     @completions = []
134     @new_completions = @roll_completions = @clear_completions = false
135   end
136
137   ## ncurses inanity wrapper
138   ##
139   ## DO NOT READ THIS CODE. YOU WILL GO MAD.
140   def get_cursed_value
141     return nil unless @field
142
143     x = Ncurses.curx
144     Ncurses::Form.form_driver @form, Ncurses::Form::REQ_VALIDATION
145     v = @field.field_buffer(0).gsub(/^\s+|\s+$/, "")
146
147     ## cursor <= end of text
148     if x - @question.length - v.length <= 0
149       v
150     else # trailing spaces
151       v + (" " * (x - @question.length - v.length))
152     end
153   end
154
155   def set_cursed_value v
156     @field.set_field_buffer 0, v
157   end
158 end
159 end