@@ -147,6 +147,28 @@ def wrapper(*args, **kwargs):
147147 TwistedEventLoop = urwid .TwistedEventLoop
148148
149149
150+ class StatusbarEdit (urwid .Edit ):
151+ """Wrapper around urwid.Edit used for the prompt in Statusbar.
152+
153+ This class only adds a single signal that is emitted if the user presses
154+ Enter."""
155+
156+ signals = urwid .Edit .signals + ['prompt_enter' ]
157+
158+ def __init__ (self , * args , ** kwargs ):
159+ self .single = False
160+ urwid .Edit .__init__ (self , * args , ** kwargs )
161+
162+ def keypress (self , size , key ):
163+ if self .single :
164+ urwid .emit_signal (self , 'prompt_enter' , self , key )
165+ elif key == 'enter' :
166+ urwid .emit_signal (self , 'prompt_enter' , self , self .get_edit_text ())
167+ else :
168+ return urwid .Edit .keypress (self , size , key )
169+
170+ urwid .register_signal (StatusbarEdit , 'prompt_enter' )
171+
150172class Statusbar (object ):
151173
152174 """Statusbar object, ripped off from bpython.cli.
@@ -167,15 +189,22 @@ class Statusbar(object):
167189 The "widget" attribute is an urwid widget.
168190 """
169191
192+ signals = ['prompt_result' ]
193+
170194 def __init__ (self , config , s = None , main_loop = None ):
171195 self .config = config
172196 self .timer = None
173197 self .main_loop = main_loop
174198 self .s = s or ''
175199
176- self .widget = urwid .Text (('main' , self .s ))
200+ self .text = urwid .Text (('main' , self .s ))
177201 # use wrap mode 'clip' to just cut off at the end of line
178- self .widget .set_wrap_mode ('clip' )
202+ self .text .set_wrap_mode ('clip' )
203+
204+ self .edit = StatusbarEdit (('main' , '' ))
205+ urwid .connect_signal (self .edit , 'prompt_enter' , self ._on_prompt_enter )
206+
207+ self .widget = urwid .Columns ([self .text , self .edit ])
179208
180209 def _check (self , callback , userdata = None ):
181210 """This is the method is called from the timer to reset the status bar."""
@@ -189,30 +218,57 @@ def message(self, s, n=3):
189218 self .settext (s )
190219 self .timer = self .main_loop .set_alarm_in (n , self ._check )
191220
192- def prompt (self , s = '' ):
193- """Prompt the user for some input (with the optional prompt 's') and
194- return the input text, then restore the statusbar to its original
195- value."""
221+ def prompt (self , s = None , single = False ):
222+ """Prompt the user for some input (with the optional prompt 's'). After
223+ the user hit enter the signal 'prompt_result' will be emited and the
224+ status bar will be reset. If single is True, the first keypress will be
225+ returned."""
196226
197- # TODO
198- return ''
227+ if self .timer is not None :
228+ self .main_loop .remove_alarm (self .timer )
229+ self .timer = None
230+
231+ self .edit .single = single
232+ self .edit .set_caption (('main' , s or '?' ))
233+ self .edit .set_edit_text ('' )
234+ # hide the text and display the edit widget
235+ if not self .edit in self .widget .widget_list :
236+ self .widget .widget_list .append (self .edit )
237+ if self .text in self .widget .widget_list :
238+ self .widget .widget_list .remove (self .text )
239+ self .widget .set_focus_column (0 )
199240
200241 def settext (self , s , permanent = False ):
201242 """Set the text on the status bar to a new value. If permanent is True,
202- the new value will be permanent."""
243+ the new value will be permanent. If that status bar is in prompt mode,
244+ the prompt will be aborted. """
203245
204246 if self .timer is not None :
205247 self .main_loop .remove_alarm (self .timer )
206248 self .timer = None
207249
208- self .widget .set_text (('main' , s ))
250+ # hide the edit and display the text widget
251+ if self .edit in self .widget .widget_list :
252+ self .widget .widget_list .remove (self .edit )
253+ if not self .text in self .widget .widget_list :
254+ self .widget .widget_list .append (self .text )
255+
256+ self .text .set_text (('main' , s ))
209257 if permanent :
210258 self .s = s
211259
212260 def clear (self ):
213261 """Clear the status bar."""
214262 self .settext ('' )
215263
264+ def _on_prompt_enter (self , edit , new_text ):
265+ """Reset the statusbar and pass the input from the prompt to the caller
266+ via 'prompt_result'."""
267+ self .settext (self .s )
268+ urwid .emit_signal (self , 'prompt_result' , new_text )
269+
270+ urwid .register_signal (Statusbar , 'prompt_result' )
271+
216272
217273def decoding_input_filter (keys , raw ):
218274 """Input filter for urwid which decodes each key with the locale's
@@ -269,7 +325,7 @@ def __init__(self, config, *args, **kwargs):
269325 self ._bpy_selectable = True
270326 self ._bpy_may_move_cursor = False
271327 self .config = config
272- self .tab_length = config .tab_length
328+ self .tab_length = config .tab_length
273329 urwid .Edit .__init__ (self , * args , ** kwargs )
274330
275331 def set_edit_pos (self , pos ):
@@ -353,9 +409,6 @@ def keypress(self, size, key):
353409 self .set_edit_text (line [:- self .tab_length ])
354410 else :
355411 return urwid .Edit .keypress (self , size , key )
356- elif key == 'pastebin' :
357- # do pastebin
358- pass
359412 else :
360413 # TODO: Add in specific keypress fetching code here
361414 return urwid .Edit .keypress (self , size , key )
@@ -461,21 +514,40 @@ def render(self, size, focus=False):
461514 return canvas
462515
463516class URWIDInteraction (repl .Interaction ):
464- def __init__ (self , config , statusbar = None ):
517+ def __init__ (self , config , statusbar , frame ):
465518 repl .Interaction .__init__ (self , config , statusbar )
519+ self .frame = frame
520+ urwid .connect_signal (statusbar , 'prompt_result' , self ._prompt_result )
521+ self .callback = None
466522
467- def confirm (self , q ):
468- """Ask for yes or no and return boolean"""
469- try :
470- reply = self .statusbar .prompt (q )
471- except ValueError :
472- return False
523+ def confirm (self , q , callback ):
524+ """Ask for yes or no and call callback to return the result"""
525+
526+ def callback_wrapper (result ):
527+ callback (result .lower () in (_ ('y' ), _ ('yes' )))
473528
474- return reply . lower () in ( _ ( 'y' ), _ ( 'yes' ) )
529+ self . prompt ( q , callback_wrapper , single = True )
475530
476531 def notify (self , s , n = 10 ):
477532 return self .statusbar .message (s , n )
478533
534+ def prompt (self , s , callback = None , single = False ):
535+ """Prompt the user for input. The result will be returned via calling
536+ callback."""
537+
538+ if self .callback is not None :
539+ raise Exception ('Prompt already in progress' )
540+
541+ self .callback = callback
542+ self .statusbar .prompt (s , single = single )
543+ self .frame .set_focus ('footer' )
544+
545+ def _prompt_result (self , text ):
546+ self .frame .set_focus ('body' )
547+ if self .callback is not None :
548+ self .callback (text )
549+ self .callback = None
550+
479551
480552class URWIDRepl (repl .Repl ):
481553
@@ -490,21 +562,12 @@ def __init__(self, event_loop, palette, interpreter, config):
490562
491563 self .listbox = BPythonListBox (urwid .SimpleListWalker ([]))
492564
493- # String is straight from bpython.cli
494- self .statusbar = Statusbar (config ,
495- _ (" <%s> Rewind <%s> Save <%s> Pastebin "
496- " <%s> Pager <%s> Show Source " ) %
497- (config .undo_key , config .save_key , config .pastebin_key ,
498- config .last_output_key , config .show_source_key ))
499-
500- self .interact = URWIDInteraction (self .config , self .statusbar )
501-
502565 self .tooltip = urwid .ListBox (urwid .SimpleListWalker ([]))
503566 self .tooltip .grid = None
504567 self .overlay = Tooltip (self .listbox , self .tooltip )
505568 self .stdout_hist = ''
506569
507- self .frame = urwid .Frame (self .overlay , footer = self . statusbar . widget )
570+ self .frame = urwid .Frame (self .overlay )
508571
509572 if urwid .get_encoding_mode () == 'narrow' :
510573 input_filter = decoding_input_filter
@@ -516,7 +579,15 @@ def __init__(self, event_loop, palette, interpreter, config):
516579 self .frame , palette ,
517580 event_loop = event_loop , unhandled_input = self .handle_input ,
518581 input_filter = input_filter , handle_mouse = False )
519- self .statusbar .main_loop = self .main_loop
582+
583+ # String is straight from bpython.cli
584+ self .statusbar = Statusbar (config ,
585+ _ (" <%s> Rewind <%s> Save <%s> Pastebin "
586+ " <%s> Pager <%s> Show Source " ) %
587+ (config .undo_key , config .save_key , config .pastebin_key ,
588+ config .last_output_key , config .show_source_key ), self .main_loop )
589+ self .frame .set_footer (self .statusbar .widget )
590+ self .interact = URWIDInteraction (self .config , self .statusbar , self .frame )
520591
521592 self .edits = []
522593 self .edit = None
@@ -899,6 +970,11 @@ def on_edit_pos_changed(self, edit, position):
899970 edit .set_edit_markup (list (format_tokens (tokens )))
900971
901972 def handle_input (self , event ):
973+ # Since most of the input handling here should be handled in the edit
974+ # instead, we return here early if the edit doesn't have the focus.
975+ if self .frame .get_focus () != 'body' :
976+ return
977+
902978 if event == 'enter' :
903979 inp = self .edit .get_edit_text ()
904980 self .history .append (inp )
@@ -1189,6 +1265,7 @@ def load_urwid_command_map(config):
11891265 urwid .command_map [key_dispatch [config .down_one_line_key ]] = 'cursor down'
11901266 urwid .command_map [key_dispatch ['C-a' ]] = 'cursor max left'
11911267 urwid .command_map [key_dispatch ['C-e' ]] = 'cursor max right'
1268+ urwid .command_map [key_dispatch [config .pastebin_key ]] = 'pastebin'
11921269"""
11931270 'clear_line': 'C-u',
11941271 'clear_screen': 'C-l',
0 commit comments