Working on theme support

This commit is contained in:
Michael Lazar
2017-07-10 17:58:48 -04:00
parent 659807d890
commit 862d0e756d
17 changed files with 7728 additions and 190 deletions

View File

@@ -21,9 +21,9 @@ import six
from six.moves.urllib.parse import quote
from kitchen.text.display import textual_width_chop
from . import exceptions
from . import mime_parsers
from .objects import LoadScreen, Color
from . import exceptions, mime_parsers
from .theme import Theme
from .objects import LoadScreen
try:
# Fix only needed for versions prior to python 3.6
@@ -47,43 +47,39 @@ class Terminal(object):
MIN_HEIGHT = 10
MIN_WIDTH = 20
# ASCII code
# ASCII codes
ESCAPE = 27
RETURN = 10
SPACE = 32
def __init__(self, stdscr, config):
def __init__(self, stdscr, config, theme=None):
self.stdscr = stdscr
self.config = config
self.loader = LoadScreen(self)
self.theme = None
self.set_theme(theme)
self._display = None
self._mailcap_dict = mailcap.getcaps()
self._term = os.environ['TERM']
@property
def up_arrow(self):
symbol = '^' if self.config['ascii'] else ''
attr = curses.A_BOLD | Color.GREEN
return symbol, attr
return '^' if self.config['ascii'] else ''
@property
def down_arrow(self):
symbol = 'v' if self.config['ascii'] else ''
attr = curses.A_BOLD | Color.RED
return symbol, attr
return 'v' if self.config['ascii'] else ''
@property
def neutral_arrow(self):
symbol = 'o' if self.config['ascii'] else ''
attr = curses.A_BOLD
return symbol, attr
return 'o' if self.config['ascii'] else ''
@property
def guilded(self):
symbol = '*' if self.config['ascii'] else ''
attr = curses.A_BOLD | Color.YELLOW
return symbol, attr
return '*' if self.config['ascii'] else ''
@property
def vline(self):
@@ -194,11 +190,11 @@ class Terminal(object):
"""
if likes is None:
return self.neutral_arrow
return self.neutral_arrow, self.attr('neutral_vote')
elif likes:
return self.up_arrow
return self.up_arrow, self.attr('upvote')
else:
return self.down_arrow
return self.down_arrow, self.attr('downvote')
def clean(self, string, n_cols=None):
"""
@@ -275,7 +271,21 @@ class Terminal(object):
params = [] if attr is None else [attr]
window.addstr(row, col, text, *params)
def show_notification(self, message, timeout=None):
@staticmethod
def add_space(window):
"""
Shortcut for adding a single space to a window at the current position
"""
row, col = window.getyx()
_, max_cols = window.getmaxyx()
if max_cols - col - 1 <= 0:
# Trying to draw outside of the screen bounds
return
window.addstr(row, col, ' ')
def show_notification(self, message, timeout=None, style='info'):
"""
Overlay a message box on the center of the screen and wait for input.
@@ -283,12 +293,15 @@ class Terminal(object):
message (list or string): List of strings, one per line.
timeout (float): Optional, maximum length of time that the message
will be shown before disappearing.
style (str): The theme element that will be applied to the
notification window
"""
if isinstance(message, six.string_types):
message = message.splitlines()
n_rows, n_cols = self.stdscr.getmaxyx()
v_offset, h_offset = self.stdscr.getbegyx()
box_width = max(len(m) for m in message) + 2
box_height = len(message) + 2
@@ -298,10 +311,11 @@ class Terminal(object):
box_height = min(box_height, n_rows)
message = message[:box_height-2]
s_row = (n_rows - box_height) // 2
s_col = (n_cols - box_width) // 2
s_row = (n_rows - box_height) // 2 + v_offset
s_col = (n_cols - box_width) // 2 + h_offset
window = curses.newwin(box_height, box_width, s_row, s_col)
window.bkgd(str(' '), self.attr('notice_{0}'.format(style)))
window.erase()
window.border()
@@ -687,18 +701,22 @@ class Terminal(object):
"""
n_rows, n_cols = self.stdscr.getmaxyx()
ch, attr = str(' '), curses.A_BOLD | curses.A_REVERSE | Color.CYAN
v_offset, h_offset = self.stdscr.getbegyx()
ch, attr = str(' '), self.attr('prompt')
prompt = self.clean(prompt, n_cols-1)
# Create a new window to draw the text at the bottom of the screen,
# so we can erase it when we're done.
prompt_win = curses.newwin(1, len(prompt)+1, n_rows-1, 0)
s_row = v_offset + n_rows - 1
s_col = h_offset
prompt_win = curses.newwin(1, len(prompt) + 1, s_row, s_col)
prompt_win.bkgd(ch, attr)
self.add_line(prompt_win, prompt)
prompt_win.refresh()
# Create a separate window for text input
input_win = curses.newwin(1, n_cols-len(prompt), n_rows-1, len(prompt))
s_col = h_offset + len(prompt)
input_win = curses.newwin(1, n_cols - len(prompt), s_row, s_col)
input_win.bkgd(ch, attr)
input_win.refresh()
@@ -797,3 +815,46 @@ class Terminal(object):
self.stdscr.touchwin()
else:
self.stdscr.clearok(True)
def attr(self, element):
"""
Shortcut for fetching the color + attribute code for an element.
"""
return self.theme.get(element)
def set_theme(self, theme=None):
"""
Check that the terminal supports the provided theme, and applies
the theme to the terminal if possible.
If the terminal doesn't support the theme, this falls back to the
default theme. The default theme only requires 8 colors so it
should be compatible with any terminal that supports basic colors.
"""
monochrome = (not curses.has_colors())
if theme is None:
theme = Theme(monochrome=monochrome)
elif theme.required_color_pairs > curses.COLOR_PAIRS:
_logger.warning(
'Theme %s requires %s color pairs, but TERM %s only '
'supports %s color pairs, switching to default theme',
theme.name, theme.required_color_pairs, self._term,
curses.COLOR_PAIRS)
theme = Theme(monochrome=monochrome)
elif theme.required_colors > curses.COLORS:
_logger.warning(
'Theme %s requires %s colors, but TERM %s only '
'supports %s colors, switching to default theme',
theme.name, theme.required_colors, self._term,
curses.COLORS)
theme = Theme(monochrome=monochrome)
theme.bind_curses()
# Apply the default color to the whole screen
self.stdscr.bkgd(str(' '), theme.get('default'))
self.theme = theme