Add a Quit dialog

Remove the contextlib pipe close feature -- file descriptors are
closed on program exit anyway.  Letting that happen normally
actually makes exiting with CTRL-C nicer (fewer race conditions).

Map "CTRL-Q" to the quit command, but pop up a yes/no dialog.

Refactor the MessageDialog into a ButtonDialog base class that
can serve MessageDialog and YesNoDialog.

Add global help text that is prepended to each screen's help text
to deal with the growing number of global commands.

Change-Id: I455344cb20fb19032a3964d602fc886e19f256e5
This commit is contained in:
James E. Blair 2014-05-02 19:38:00 -07:00
parent 5500eca644
commit 68c51ffdb7
6 changed files with 68 additions and 26 deletions

View File

@ -13,7 +13,6 @@
# under the License.
import argparse
import contextlib
import logging
import os
import sys
@ -123,6 +122,7 @@ class App(object):
self.loop = urwid.MainLoop(screen, palette=palette,
unhandled_input=self.unhandledInput)
self.sync_pipe = self.loop.watch_pipe(self.refresh)
self.loop.screen.tty_signal_keys(start='undefined', stop='undefined')
#self.loop.screen.set_terminal_properties(colors=88)
if not disable_sync:
self.sync_thread = threading.Thread(target=self.sync.run, args=(self.sync_pipe,))
@ -134,15 +134,23 @@ class App(object):
def run(self):
self.loop.run()
def close(self):
os.close(self.sync_pipe)
def _quit(self, widget=None):
raise urwid.ExitMainLoop()
def quit(self):
dialog = mywid.YesNoDialog(u'Quit',
u'Are you sure you want to quit?')
urwid.connect_signal(dialog, 'no', self.backScreen)
urwid.connect_signal(dialog, 'yes', self._quit)
self.popup(dialog)
def changeScreen(self, widget):
self.status.update(title=widget.title)
self.screens.append(self.loop.widget)
self.loop.widget = widget
def backScreen(self):
def backScreen(self, widget=None):
if not self.screens:
return
widget = self.screens.pop()
@ -173,15 +181,15 @@ class App(object):
lines = self.loop.widget.help.split('\n')
urwid.connect_signal(dialog, 'close',
lambda button: self.backScreen())
self.popup(dialog, min_width=76, min_height=len(lines)+2)
self.popup(dialog, min_width=76, min_height=len(lines)+4)
def unhandledInput(self, key):
if key == 'esc':
self.backScreen()
elif key == 'f1':
self.help()
elif key in ('q', 'Q'):
raise urwid.ExitMainLoop()
elif key == 'ctrl q':
self.quit()
def getRepo(self, project_name):
local_path = os.path.join(self.config.git_root, project_name)
@ -201,5 +209,4 @@ if __name__ == '__main__':
help='the server to use (as specified in config file)')
args = parser.parse_args()
g = App(args.server, args.debug, args.no_sync)
with contextlib.closing(g):
g.run()
g.run()

View File

@ -14,6 +14,14 @@
import urwid
GLOBAL_HELP = """\
Global Keys
===========
<F1> Help
<ESC> Back to previous screen
<CTRL-Q> Quit Gertty
"""
class TextButton(urwid.Button):
def selectable(self):
return True
@ -44,18 +52,36 @@ class Table(urwid.WidgetWrap):
for i, widget in enumerate(cells):
self._w.contents[i][0].contents.append((widget, ('pack', None)))
class MessageDialog(urwid.WidgetWrap):
signals = ['close']
def __init__(self, title, message):
ok_button = FixedButton(u'OK')
urwid.connect_signal(ok_button, 'click',
lambda button:self._emit('close'))
buttons = urwid.Columns([('pack', ok_button)],
dividechars=2)
class ButtonDialog(urwid.WidgetWrap):
def __init__(self, title, message, buttons=[]):
button_widgets = []
for button in buttons:
button_widgets.append(('pack', button))
button_columns = urwid.Columns(button_widgets, dividechars=2)
rows = []
rows.append(urwid.Text(message))
rows.append(urwid.Divider())
rows.append(buttons)
rows.append(button_columns)
pile = urwid.Pile(rows)
fill = urwid.Filler(pile, valign='top')
super(MessageDialog, self).__init__(urwid.LineBox(fill, title))
super(ButtonDialog, self).__init__(urwid.LineBox(fill, title))
class MessageDialog(ButtonDialog):
signals = ['close']
def __init__(self, title, message):
ok_button = FixedButton('OK')
urwid.connect_signal(ok_button, 'click',
lambda button:self._emit('close'))
super(MessageDialog, self).__init__(title, message, buttons=[ok_button])
class YesNoDialog(ButtonDialog):
signals = ['yes', 'no']
def __init__(self, title, message):
yes_button = FixedButton('Yes')
no_button = FixedButton('No')
urwid.connect_signal(yes_button, 'click',
lambda button:self._emit('yes'))
urwid.connect_signal(no_button, 'click',
lambda button:self._emit('no'))
super(YesNoDialog, self).__init__(title, message, buttons=[yes_button,
no_button])

View File

@ -238,9 +238,10 @@ class ChangeMessageBox(urwid.Text):
self.set_text(text)
class ChangeView(urwid.WidgetWrap):
help = """
help = mywid.GLOBAL_HELP + """
This Screen
===========
<r> Toggle the reviewed flag for the current change.
<ESC> Go back to the previous screen.
"""
def __init__(self, app, change_key):

View File

@ -14,6 +14,7 @@
import urwid
import mywid
import view.change
class ChangeRow(urwid.Button):
@ -64,10 +65,11 @@ class ChangeListHeader(urwid.WidgetWrap):
self._w.contents.append((urwid.Text(' %s' % category[0]), self._w.options('given', 3)))
class ChangeListView(urwid.WidgetWrap):
help = """
help = mywid.GLOBAL_HELP + """
This Screen
===========
<l> Toggle whether only unreviewed or all changes are displayed.
<r> Toggle the reviewed flag for the currently selected change.
<ESC> Go back to the previous screen.
"""
def __init__(self, app, project_key):

View File

@ -17,6 +17,8 @@ import logging
import urwid
import mywid
class LineContext(object):
def __init__(self, old_revision_key, new_revision_key,
old_revision_num, new_revision_num,
@ -101,9 +103,10 @@ class DiffLine(urwid.Button):
self._w = urwid.AttrMap(col, None, focus_map=map)
class DiffView(urwid.WidgetWrap):
help = """
help = mywid.GLOBAL_HELP + """
This Screen
===========
<Enter> Add an inline comment.
<ESC> Go back to the previous screen.
"""
def __init__(self, app, new_revision_key):

View File

@ -14,6 +14,7 @@
import urwid
import mywid
import sync
import view.change_list
@ -56,7 +57,9 @@ class ProjectRow(urwid.Button):
self.reviewed_changes.set_text(str(len(project.reviewed_changes)))
class ProjectListView(urwid.WidgetWrap):
help = """
help = mywid.GLOBAL_HELP + """
This Screen
===========
<l> Toggle whether only subscribed projects or all projects are listed.
<s> Toggle the subscription flag for the currently selected project.
"""