@@ -147,6 +147,28 @@ def wrapper(*args, **kwargs):
147
147
TwistedEventLoop = urwid .TwistedEventLoop
148
148
149
149
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
+
150
172
class Statusbar (object ):
151
173
152
174
"""Statusbar object, ripped off from bpython.cli.
@@ -167,15 +189,22 @@ class Statusbar(object):
167
189
The "widget" attribute is an urwid widget.
168
190
"""
169
191
192
+ signals = ['prompt_result' ]
193
+
170
194
def __init__ (self , config , s = None , main_loop = None ):
171
195
self .config = config
172
196
self .timer = None
173
197
self .main_loop = main_loop
174
198
self .s = s or ''
175
199
176
- self .widget = urwid .Text (('main' , self .s ))
200
+ self .text = urwid .Text (('main' , self .s ))
177
201
# 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 ])
179
208
180
209
def _check (self , callback , userdata = None ):
181
210
"""This is the method is called from the timer to reset the status bar."""
@@ -189,30 +218,57 @@ def message(self, s, n=3):
189
218
self .settext (s )
190
219
self .timer = self .main_loop .set_alarm_in (n , self ._check )
191
220
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."""
196
226
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 )
199
240
200
241
def settext (self , s , permanent = False ):
201
242
"""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. """
203
245
204
246
if self .timer is not None :
205
247
self .main_loop .remove_alarm (self .timer )
206
248
self .timer = None
207
249
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 ))
209
257
if permanent :
210
258
self .s = s
211
259
212
260
def clear (self ):
213
261
"""Clear the status bar."""
214
262
self .settext ('' )
215
263
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
+
216
272
217
273
def decoding_input_filter (keys , raw ):
218
274
"""Input filter for urwid which decodes each key with the locale's
@@ -269,7 +325,7 @@ def __init__(self, config, *args, **kwargs):
269
325
self ._bpy_selectable = True
270
326
self ._bpy_may_move_cursor = False
271
327
self .config = config
272
- self .tab_length = config .tab_length
328
+ self .tab_length = config .tab_length
273
329
urwid .Edit .__init__ (self , * args , ** kwargs )
274
330
275
331
def set_edit_pos (self , pos ):
@@ -353,9 +409,6 @@ def keypress(self, size, key):
353
409
self .set_edit_text (line [:- self .tab_length ])
354
410
else :
355
411
return urwid .Edit .keypress (self , size , key )
356
- elif key == 'pastebin' :
357
- # do pastebin
358
- pass
359
412
else :
360
413
# TODO: Add in specific keypress fetching code here
361
414
return urwid .Edit .keypress (self , size , key )
@@ -461,21 +514,40 @@ def render(self, size, focus=False):
461
514
return canvas
462
515
463
516
class URWIDInteraction (repl .Interaction ):
464
- def __init__ (self , config , statusbar = None ):
517
+ def __init__ (self , config , statusbar , frame ):
465
518
repl .Interaction .__init__ (self , config , statusbar )
519
+ self .frame = frame
520
+ urwid .connect_signal (statusbar , 'prompt_result' , self ._prompt_result )
521
+ self .callback = None
466
522
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' )))
473
528
474
- return reply . lower () in ( _ ( 'y' ), _ ( 'yes' ) )
529
+ self . prompt ( q , callback_wrapper , single = True )
475
530
476
531
def notify (self , s , n = 10 ):
477
532
return self .statusbar .message (s , n )
478
533
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
+
479
551
480
552
class URWIDRepl (repl .Repl ):
481
553
@@ -490,21 +562,12 @@ def __init__(self, event_loop, palette, interpreter, config):
490
562
491
563
self .listbox = BPythonListBox (urwid .SimpleListWalker ([]))
492
564
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
-
502
565
self .tooltip = urwid .ListBox (urwid .SimpleListWalker ([]))
503
566
self .tooltip .grid = None
504
567
self .overlay = Tooltip (self .listbox , self .tooltip )
505
568
self .stdout_hist = ''
506
569
507
- self .frame = urwid .Frame (self .overlay , footer = self . statusbar . widget )
570
+ self .frame = urwid .Frame (self .overlay )
508
571
509
572
if urwid .get_encoding_mode () == 'narrow' :
510
573
input_filter = decoding_input_filter
@@ -516,7 +579,15 @@ def __init__(self, event_loop, palette, interpreter, config):
516
579
self .frame , palette ,
517
580
event_loop = event_loop , unhandled_input = self .handle_input ,
518
581
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 )
520
591
521
592
self .edits = []
522
593
self .edit = None
@@ -899,6 +970,11 @@ def on_edit_pos_changed(self, edit, position):
899
970
edit .set_edit_markup (list (format_tokens (tokens )))
900
971
901
972
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
+
902
978
if event == 'enter' :
903
979
inp = self .edit .get_edit_text ()
904
980
self .history .append (inp )
@@ -1189,6 +1265,7 @@ def load_urwid_command_map(config):
1189
1265
urwid .command_map [key_dispatch [config .down_one_line_key ]] = 'cursor down'
1190
1266
urwid .command_map [key_dispatch ['C-a' ]] = 'cursor max left'
1191
1267
urwid .command_map [key_dispatch ['C-e' ]] = 'cursor max right'
1268
+ urwid .command_map [key_dispatch [config .pastebin_key ]] = 'pastebin'
1192
1269
"""
1193
1270
'clear_line': 'C-u',
1194
1271
'clear_screen': 'C-l',
0 commit comments