Add navigation breadcrumb footer
This change adds a navigational breadcrumb trail to the bottom of the interface to allow the user to easily keep track of their location in Gertty. Change-Id: I40de19084bf66d095c14ac7a4a901f293b30b9be
This commit is contained in:
parent
7d648c0e68
commit
b009075467
@ -16,6 +16,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
import dateutil
|
import dateutil
|
||||||
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -135,6 +136,41 @@ class StatusHeader(urwid.WidgetWrap):
|
|||||||
self.sync_widget.set_text(u' Sync: %i' % self._sync)
|
self.sync_widget.set_text(u' Sync: %i' % self._sync)
|
||||||
|
|
||||||
|
|
||||||
|
class BreadCrumbBar(urwid.WidgetWrap):
|
||||||
|
BREADCRUMB_SYMBOL = u'\N{BLACK RIGHT-POINTING SMALL TRIANGLE}'
|
||||||
|
BREADCRUMB_WIDTH = 25
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.prefix_text = urwid.Text(u' \N{WATCH} ')
|
||||||
|
self.breadcrumbs = urwid.Columns([], dividechars=3)
|
||||||
|
self.display_widget = urwid.Columns(
|
||||||
|
[('pack', self.prefix_text), self.breadcrumbs])
|
||||||
|
super(BreadCrumbBar, self).__init__(self.display_widget)
|
||||||
|
|
||||||
|
def _get_breadcrumb_text(self, screen):
|
||||||
|
title = getattr(screen, 'title', str(screen))
|
||||||
|
text = "%s %s" % (BreadCrumbBar.BREADCRUMB_SYMBOL, title)
|
||||||
|
if len(text) > 20:
|
||||||
|
text = "%s..." % text[:20]
|
||||||
|
return urwid.Text(text, wrap='clip')
|
||||||
|
|
||||||
|
def _get_breadcrumb_column_options(self):
|
||||||
|
return self.breadcrumbs.options("given", BreadCrumbBar.BREADCRUMB_WIDTH)
|
||||||
|
|
||||||
|
def _update(self, screens):
|
||||||
|
breadcrumb_contents = []
|
||||||
|
for screen in screens:
|
||||||
|
breadcrumb_contents.append((
|
||||||
|
self._get_breadcrumb_text(screen),
|
||||||
|
self._get_breadcrumb_column_options()))
|
||||||
|
self.breadcrumbs.contents = breadcrumb_contents
|
||||||
|
# Update focus so we always have the right end of the breadcrumb trail
|
||||||
|
# in view. Urwid will gracefully handle clipping from the left when
|
||||||
|
# there is overflow.as trail grows, shrinks, or screen is resized.
|
||||||
|
if len(self.breadcrumbs.contents):
|
||||||
|
self.breadcrumbs.focus_position = len(self.breadcrumbs.contents) - 1
|
||||||
|
|
||||||
|
|
||||||
class SearchDialog(mywid.ButtonDialog):
|
class SearchDialog(mywid.ButtonDialog):
|
||||||
signals = ['search', 'cancel']
|
signals = ['search', 'cancel']
|
||||||
def __init__(self, app, default):
|
def __init__(self, app, default):
|
||||||
@ -247,13 +283,18 @@ class App(object):
|
|||||||
self.db = db.Database(self, self.config.dburi, self.search)
|
self.db = db.Database(self, self.config.dburi, self.search)
|
||||||
self.sync = sync.Sync(self, disable_background_sync)
|
self.sync = sync.Sync(self, disable_background_sync)
|
||||||
|
|
||||||
self.screens = []
|
|
||||||
self.status = StatusHeader(self)
|
self.status = StatusHeader(self)
|
||||||
self.header = urwid.AttrMap(self.status, 'header')
|
self.header = urwid.AttrMap(self.status, 'header')
|
||||||
|
self.screens = urwid.MonitoredList()
|
||||||
|
self.breadcrumbs = BreadCrumbBar()
|
||||||
|
self.screens.set_modified_callback(
|
||||||
|
functools.partial(self.breadcrumbs._update, self.screens))
|
||||||
|
self.footer = urwid.AttrMap(self.breadcrumbs, 'footer')
|
||||||
screen = view_project_list.ProjectListView(self)
|
screen = view_project_list.ProjectListView(self)
|
||||||
self.status.update(title=screen.title)
|
self.status.update(title=screen.title)
|
||||||
self.updateStatusQueries()
|
self.updateStatusQueries()
|
||||||
self.loop = urwid.MainLoop(screen, palette=self.config.palette.getPalette(),
|
self.frame = urwid.Frame(body=screen, footer=self.footer)
|
||||||
|
self.loop = urwid.MainLoop(self.frame, palette=self.config.palette.getPalette(),
|
||||||
handle_mouse=self.config.handle_mouse,
|
handle_mouse=self.config.handle_mouse,
|
||||||
unhandled_input=self.unhandledInput)
|
unhandled_input=self.unhandledInput)
|
||||||
|
|
||||||
@ -342,9 +383,9 @@ class App(object):
|
|||||||
self.log.debug("Changing screen to %s" % (widget,))
|
self.log.debug("Changing screen to %s" % (widget,))
|
||||||
self.status.update(error=False, title=widget.title)
|
self.status.update(error=False, title=widget.title)
|
||||||
if push:
|
if push:
|
||||||
self.screens.append(self.loop.widget)
|
self.screens.append(self.frame.body)
|
||||||
self.clearInputBuffer()
|
self.clearInputBuffer()
|
||||||
self.loop.widget = widget
|
self.frame.body = widget
|
||||||
|
|
||||||
def backScreen(self, target_widget=None):
|
def backScreen(self, target_widget=None):
|
||||||
if not self.screens:
|
if not self.screens:
|
||||||
@ -357,7 +398,7 @@ class App(object):
|
|||||||
if hasattr(widget, 'title'):
|
if hasattr(widget, 'title'):
|
||||||
self.status.update(title=widget.title)
|
self.status.update(title=widget.title)
|
||||||
self.clearInputBuffer()
|
self.clearInputBuffer()
|
||||||
self.loop.widget = widget
|
self.frame.body = widget
|
||||||
self.refresh(force=True)
|
self.refresh(force=True)
|
||||||
|
|
||||||
def findChangeList(self):
|
def findChangeList(self):
|
||||||
@ -371,10 +412,10 @@ class App(object):
|
|||||||
while self.screens:
|
while self.screens:
|
||||||
widget = self.screens.pop()
|
widget = self.screens.pop()
|
||||||
self.clearInputBuffer()
|
self.clearInputBuffer()
|
||||||
self.loop.widget = widget
|
self.frame.body = widget
|
||||||
|
|
||||||
def refresh(self, data=None, force=False):
|
def refresh(self, data=None, force=False):
|
||||||
widget = self.loop.widget
|
widget = self.frame.body
|
||||||
while isinstance(widget, urwid.Overlay):
|
while isinstance(widget, urwid.Overlay):
|
||||||
widget = widget.contents[0][0]
|
widget = widget.contents[0][0]
|
||||||
interested = force
|
interested = force
|
||||||
@ -408,23 +449,23 @@ class App(object):
|
|||||||
width = ('relative', relative_width)
|
width = ('relative', relative_width)
|
||||||
if height is None:
|
if height is None:
|
||||||
height = ('relative', relative_height)
|
height = ('relative', relative_height)
|
||||||
overlay = urwid.Overlay(widget, self.loop.widget,
|
overlay = urwid.Overlay(widget, self.frame.body,
|
||||||
'center', width,
|
'center', width,
|
||||||
'middle', height,
|
'middle', height,
|
||||||
min_width=min_width, min_height=min_height)
|
min_width=min_width, min_height=min_height)
|
||||||
self.log.debug("Overlaying %s on screen %s" % (widget, self.loop.widget))
|
self.log.debug("Overlaying %s on screen %s" % (widget, self.frame.body))
|
||||||
self.screens.append(self.loop.widget)
|
self.screens.append(self.frame.body)
|
||||||
self.loop.widget = overlay
|
self.frame.body = overlay
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
if not hasattr(self.loop.widget, 'help'):
|
if not hasattr(self.frame.body, 'help'):
|
||||||
return
|
return
|
||||||
global_help = [(self.config.keymap.formatKeys(k), t)
|
global_help = [(self.config.keymap.formatKeys(k), t)
|
||||||
for (k, t) in mywid.GLOBAL_HELP]
|
for (k, t) in mywid.GLOBAL_HELP]
|
||||||
for d in self.config.dashboards.values():
|
for d in self.config.dashboards.values():
|
||||||
global_help.append((keymap.formatKey(d['key']), d['name']))
|
global_help.append((keymap.formatKey(d['key']), d['name']))
|
||||||
parts = [('Global Keys', global_help),
|
parts = [('Global Keys', global_help),
|
||||||
('This Screen', self.loop.widget.help())]
|
('This Screen', self.frame.body.help())]
|
||||||
keylen = 0
|
keylen = 0
|
||||||
for title, items in parts:
|
for title, items in parts:
|
||||||
for keys, text in items:
|
for keys, text in items:
|
||||||
|
@ -29,6 +29,7 @@ DEFAULT_PALETTE={
|
|||||||
'focused-min-label': ['light red,standout', ''],
|
'focused-min-label': ['light red,standout', ''],
|
||||||
'link': ['dark blue', ''],
|
'link': ['dark blue', ''],
|
||||||
'focused-link': ['light blue', ''],
|
'focused-link': ['light blue', ''],
|
||||||
|
'footer': ['light gray', 'dark gray'],
|
||||||
# Diff
|
# Diff
|
||||||
'context-button': ['dark magenta', ''],
|
'context-button': ['dark magenta', ''],
|
||||||
'focused-context-button': ['light magenta', ''],
|
'focused-context-button': ['light magenta', ''],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user