Adding theme list and enabling F2 / F3
This commit is contained in:
@@ -176,13 +176,15 @@ def main():
|
|||||||
|
|
||||||
term = Terminal(stdscr, config)
|
term = Terminal(stdscr, config)
|
||||||
|
|
||||||
if config['theme']:
|
if config['monochrome']:
|
||||||
|
theme = Theme(use_color=False)
|
||||||
|
elif config['theme']:
|
||||||
theme = Theme.from_name(config['theme'])
|
theme = Theme.from_name(config['theme'])
|
||||||
else:
|
else:
|
||||||
|
# Set to None to let the terminal figure out which default
|
||||||
|
# theme to use depending on if colors are supported or not
|
||||||
theme = None
|
theme = None
|
||||||
|
term.set_theme(theme)
|
||||||
term.set_theme(theme, monochrome=config['monochrome'])
|
|
||||||
|
|
||||||
|
|
||||||
with term.loader('Initializing', catch_exception=False):
|
with term.loader('Initializing', catch_exception=False):
|
||||||
reddit = praw.Reddit(user_agent=user_agent,
|
reddit = praw.Reddit(user_agent=user_agent,
|
||||||
|
|||||||
14
rtv/page.py
14
rtv/page.py
@@ -11,7 +11,7 @@ import six
|
|||||||
from kitchen.text.display import textual_width
|
from kitchen.text.display import textual_width
|
||||||
|
|
||||||
from . import docs
|
from . import docs
|
||||||
from .theme import Theme
|
from .theme import ThemeList
|
||||||
from .objects import Controller, Command
|
from .objects import Controller, Command
|
||||||
from .clipboard import copy
|
from .clipboard import copy
|
||||||
from .exceptions import TemporaryFileError, ProgramError
|
from .exceptions import TemporaryFileError, ProgramError
|
||||||
@@ -51,6 +51,7 @@ class Page(object):
|
|||||||
self.nav = None
|
self.nav = None
|
||||||
self.controller = None
|
self.controller = None
|
||||||
self.copy_to_clipboard = copy
|
self.copy_to_clipboard = copy
|
||||||
|
self.theme_list = ThemeList()
|
||||||
|
|
||||||
self.active = True
|
self.active = True
|
||||||
self._row = 0
|
self._row = 0
|
||||||
@@ -93,17 +94,19 @@ class Page(object):
|
|||||||
|
|
||||||
@PageController.register(Command('PREVIOUS_THEME'))
|
@PageController.register(Command('PREVIOUS_THEME'))
|
||||||
def previous_theme(self):
|
def previous_theme(self):
|
||||||
theme = Theme()
|
theme = self.theme_list.previous()
|
||||||
self.term.set_theme(theme)
|
self.term.set_theme(theme)
|
||||||
self.draw()
|
self.draw()
|
||||||
self.term.show_notification(theme.name, timeout=1)
|
message = self.term.theme.display_string
|
||||||
|
self.term.show_notification(message, timeout=1)
|
||||||
|
|
||||||
@PageController.register(Command('NEXT_THEME'))
|
@PageController.register(Command('NEXT_THEME'))
|
||||||
def next_theme(self):
|
def next_theme(self):
|
||||||
theme = Theme()
|
theme = self.theme_list.next()
|
||||||
self.term.set_theme(theme)
|
self.term.set_theme(theme)
|
||||||
self.draw()
|
self.draw()
|
||||||
self.term.show_notification(theme.name, timeout=1)
|
message = self.term.theme.display_string
|
||||||
|
self.term.show_notification(message, timeout=1)
|
||||||
|
|
||||||
@PageController.register(Command('HELP'))
|
@PageController.register(Command('HELP'))
|
||||||
def show_help(self):
|
def show_help(self):
|
||||||
@@ -582,4 +585,3 @@ class Page(object):
|
|||||||
ch = self.term.show_notification(message)
|
ch = self.term.show_notification(message)
|
||||||
ch = six.unichr(ch)
|
ch = six.unichr(ch)
|
||||||
return choices.get(ch)
|
return choices.get(ch)
|
||||||
|
|
||||||
|
|||||||
@@ -829,7 +829,7 @@ class Terminal(object):
|
|||||||
|
|
||||||
return self.theme.get(element)
|
return self.theme.get(element)
|
||||||
|
|
||||||
def set_theme(self, theme=None, monochrome=False):
|
def set_theme(self, theme=None):
|
||||||
"""
|
"""
|
||||||
Check that the terminal supports the provided theme, and applies
|
Check that the terminal supports the provided theme, and applies
|
||||||
the theme to the terminal if possible.
|
the theme to the terminal if possible.
|
||||||
@@ -837,25 +837,15 @@ class Terminal(object):
|
|||||||
If the terminal doesn't support the theme, this falls back to the
|
If the terminal doesn't support the theme, this falls back to the
|
||||||
default theme. The default theme only requires 8 colors so it
|
default theme. The default theme only requires 8 colors so it
|
||||||
should be compatible with any terminal that supports basic colors.
|
should be compatible with any terminal that supports basic colors.
|
||||||
|
|
||||||
Using ``monochrome=True`` will force loading the current theme
|
|
||||||
without any color support. The intention is that this be used as
|
|
||||||
a fallback for the default theme to support the old --monochrome
|
|
||||||
command line flag.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not monochrome and curses.has_colors():
|
if curses.has_colors():
|
||||||
terminal_colors = curses.COLORS
|
terminal_colors = curses.COLORS
|
||||||
else:
|
else:
|
||||||
terminal_colors = 0
|
terminal_colors = 0
|
||||||
|
|
||||||
if theme is None:
|
if theme is None:
|
||||||
theme = Theme()
|
theme = Theme(use_color=bool(terminal_colors))
|
||||||
|
|
||||||
elif monochrome:
|
|
||||||
# No need to display a warning message if the user has
|
|
||||||
# explicitly turned off support for colors
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif theme.required_color_pairs > curses.COLOR_PAIRS:
|
elif theme.required_color_pairs > curses.COLOR_PAIRS:
|
||||||
_logger.warning(
|
_logger.warning(
|
||||||
@@ -873,7 +863,7 @@ class Terminal(object):
|
|||||||
curses.COLORS)
|
curses.COLORS)
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
|
|
||||||
theme.bind_curses(use_color=bool(terminal_colors))
|
theme.bind_curses()
|
||||||
|
|
||||||
# Apply the default color to the whole screen
|
# Apply the default color to the whole screen
|
||||||
self.stdscr.bkgd(str(' '), theme.get('@normal'))
|
self.stdscr.bkgd(str(' '), theme.get('@normal'))
|
||||||
|
|||||||
238
rtv/theme.py
238
rtv/theme.py
@@ -3,8 +3,11 @@ import codecs
|
|||||||
import curses
|
import curses
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
|
from collections import OrderedDict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from .config import THEMES, DEFAULT_THEMES
|
from .config import THEMES, DEFAULT_THEMES
|
||||||
from .exceptions import ConfigError
|
from .exceptions import ConfigError
|
||||||
|
|
||||||
@@ -105,15 +108,29 @@ class Theme(object):
|
|||||||
|
|
||||||
BAR_LEVELS = ['bar_level_1', 'bar_level_2', 'bar_level_3', 'bar_level_4']
|
BAR_LEVELS = ['bar_level_1', 'bar_level_2', 'bar_level_3', 'bar_level_4']
|
||||||
|
|
||||||
def __init__(self, name='default', elements=None):
|
def __init__(self, name=None, source=None, elements=None, use_color=True):
|
||||||
"""
|
"""
|
||||||
Params:
|
Params:
|
||||||
name (str): A unique string that describes the theme
|
name (str): A unique string that describes the theme
|
||||||
|
source (str): A string that describes the source of the theme:
|
||||||
|
built-in - Should only be used when Theme() is called directly
|
||||||
|
preset - Themes packaged with rtv
|
||||||
|
installed - Themes in ~/.config/rtv/themes/
|
||||||
|
custom - When a filepath is explicitly provided, e.g.
|
||||||
|
``rtv --theme=/path/to/theme_file.cfg``
|
||||||
elements (dict): The theme's element map, should be in the same
|
elements (dict): The theme's element map, should be in the same
|
||||||
format as Theme.DEFAULT_THEME.
|
format as Theme.DEFAULT_THEME.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if name is None and source is None:
|
||||||
|
name = 'default' if use_color else 'monochrome'
|
||||||
|
source = 'built-in'
|
||||||
|
elif name is None or source is None:
|
||||||
|
raise ValueError('Must specify both `name` and `source`, or neither one')
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.source = source
|
||||||
|
self.use_color = use_color
|
||||||
self._color_pair_map = None
|
self._color_pair_map = None
|
||||||
self._attribute_map = None
|
self._attribute_map = None
|
||||||
self._modifier = None
|
self._modifier = None
|
||||||
@@ -166,28 +183,33 @@ class Theme(object):
|
|||||||
|
|
||||||
self.elements = elements
|
self.elements = elements
|
||||||
|
|
||||||
# Pre-calculate how many colors / color pairs the theme will need
|
if self.use_color:
|
||||||
colors, color_pairs = set(), set()
|
# Pre-calculate how many colors / color pairs the theme will need
|
||||||
for fg, bg, _ in self.elements.values():
|
colors, color_pairs = set(), set()
|
||||||
colors.add(fg)
|
for fg, bg, _ in self.elements.values():
|
||||||
colors.add(bg)
|
colors.add(fg)
|
||||||
color_pairs.add((fg, bg))
|
colors.add(bg)
|
||||||
|
color_pairs.add((fg, bg))
|
||||||
|
|
||||||
# Don't count the default (-1, -1) as a color pair because it doesn't
|
# Don't count the default (-1, -1) as a color pair because it doesn't
|
||||||
# need to be initialized by curses.init_pair().
|
# need to be initialized by curses.init_pair().
|
||||||
color_pairs.discard((-1, -1))
|
color_pairs.discard((-1, -1))
|
||||||
self.required_color_pairs = len(color_pairs)
|
self.required_color_pairs = len(color_pairs)
|
||||||
|
|
||||||
# Determine how many colors the terminal needs to support in order to
|
# Determine how many colors the terminal needs to support in order to
|
||||||
# be able to use the theme. This uses the common breakpoints that 99%
|
# be able to use the theme. This uses the common breakpoints that 99%
|
||||||
# of terminals follow and doesn't take into account 88 color themes.
|
# of terminals follow and doesn't take into account 88 color themes.
|
||||||
self.required_colors = None
|
self.required_colors = None
|
||||||
for marker in [0, 8, 16, 256]:
|
for marker in [0, 8, 16, 256]:
|
||||||
if max(colors) < marker:
|
if max(colors) < marker:
|
||||||
self.required_colors = marker
|
self.required_colors = marker
|
||||||
break
|
break
|
||||||
|
|
||||||
def bind_curses(self, use_color=True):
|
@property
|
||||||
|
def display_string(self):
|
||||||
|
return '{0} ({1})'.format(self.name, self.source)
|
||||||
|
|
||||||
|
def bind_curses(self):
|
||||||
"""
|
"""
|
||||||
Bind the theme's colors to curses's internal color pair map.
|
Bind the theme's colors to curses's internal color pair map.
|
||||||
|
|
||||||
@@ -203,7 +225,7 @@ class Theme(object):
|
|||||||
fg, bg, attrs = item
|
fg, bg, attrs = item
|
||||||
|
|
||||||
color_pair = (fg, bg)
|
color_pair = (fg, bg)
|
||||||
if use_color and color_pair != (-1, -1):
|
if self.use_color and color_pair != (-1, -1):
|
||||||
# Curses limits the number of available color pairs, so we
|
# Curses limits the number of available color pairs, so we
|
||||||
# need to reuse them if there are multiple elements with the
|
# need to reuse them if there are multiple elements with the
|
||||||
# same foreground and background.
|
# same foreground and background.
|
||||||
@@ -235,7 +257,20 @@ class Theme(object):
|
|||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def set_modifier(self, modifier=None):
|
def set_modifier(self, modifier=None):
|
||||||
|
"""
|
||||||
|
Sets the active modifier inside of context block.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
>>> with theme.set_modifier('highlight'):
|
||||||
|
>>> attr = theme.get('cursor')
|
||||||
|
|
||||||
|
Is the same as:
|
||||||
|
>>> attr = theme.get('cursor', modifier='highlight')
|
||||||
|
|
||||||
|
Is also the same as:
|
||||||
|
>>> attr = theme.get('cursor.highlight')
|
||||||
|
|
||||||
|
"""
|
||||||
# This case is undefined if the context manager is nested
|
# This case is undefined if the context manager is nested
|
||||||
assert self._modifier is None
|
assert self._modifier is None
|
||||||
|
|
||||||
@@ -250,28 +285,32 @@ class Theme(object):
|
|||||||
"""
|
"""
|
||||||
Compile all of the themes configuration files in the search path.
|
Compile all of the themes configuration files in the search path.
|
||||||
"""
|
"""
|
||||||
|
themes, errors = [], OrderedDict()
|
||||||
|
|
||||||
themes = {'invalid': {}, 'custom': {}, 'default': {}}
|
def load_themes(path, source):
|
||||||
for container, theme_path in [
|
"""
|
||||||
(themes['custom'], path),
|
Load all themes in the given path.
|
||||||
(themes['default'], DEFAULT_THEMES)]:
|
"""
|
||||||
|
if os.path.isdir(path):
|
||||||
if os.path.isdir(theme_path):
|
for filename in sorted(os.listdir(path)):
|
||||||
for filename in os.listdir(theme_path):
|
|
||||||
if not filename.endswith('.cfg'):
|
if not filename.endswith('.cfg'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
filepath = os.path.join(theme_path, filename)
|
filepath = os.path.join(path, filename)
|
||||||
name = filename[:-4]
|
name = filename[:-4]
|
||||||
try:
|
try:
|
||||||
# Make sure the theme is valid
|
# Make sure the theme is valid
|
||||||
theme = cls.from_file(filepath)
|
theme = cls.from_file(filepath, source)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
themes['invalid'][name] = e
|
errors[(source, name)] = e
|
||||||
else:
|
else:
|
||||||
container[name] = theme
|
themes.append(theme)
|
||||||
|
|
||||||
return themes
|
themes.extend([Theme(use_color=True), Theme(use_color=False)])
|
||||||
|
load_themes(DEFAULT_THEMES, 'preset')
|
||||||
|
load_themes(path, 'installed')
|
||||||
|
|
||||||
|
return themes, errors
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def print_themes(cls, path=THEMES):
|
def print_themes(cls, path=THEMES):
|
||||||
@@ -281,28 +320,30 @@ class Theme(object):
|
|||||||
This is intended to be used as a command-line utility, outside of the
|
This is intended to be used as a command-line utility, outside of the
|
||||||
main curses display loop.
|
main curses display loop.
|
||||||
"""
|
"""
|
||||||
themes = cls.list_themes(path=path)
|
themes, errors = cls.list_themes(path=path)
|
||||||
|
|
||||||
print('\nInstalled ({0}):'.format(path))
|
print('\nInstalled ({0}):'.format(path))
|
||||||
custom_themes = sorted(themes['custom'].items())
|
installed = [t for t in themes if t.source == 'installed']
|
||||||
if custom_themes:
|
if installed:
|
||||||
for name, theme in custom_themes:
|
for theme in installed:
|
||||||
print(' {0:<20}[requires {1} colors]'.format(
|
line = ' {0:<20}[requires {1} colors]'
|
||||||
name, theme.required_colors))
|
print(line.format(theme.name, theme.required_colors))
|
||||||
else:
|
else:
|
||||||
print(' (empty)')
|
print(' (empty)')
|
||||||
|
|
||||||
print('\nBuilt-in:')
|
print('\nPresets:')
|
||||||
default_themes = sorted(themes['default'].items())
|
preset = [t for t in themes if t.source == 'preset']
|
||||||
for name, theme in default_themes:
|
for theme in preset:
|
||||||
print(' {0:<20}[requires {1} colors]'.format(
|
line = ' {0:<20}[requires {1} colors]'
|
||||||
name, theme.required_colors))
|
print(line.format(theme.name, theme.required_colors))
|
||||||
|
|
||||||
invalid_themes = sorted(themes['invalid'].items())
|
if errors:
|
||||||
if invalid_themes:
|
print('\nWARNING: Some files encountered errors:')
|
||||||
print('\nWARNING: Some themes had problems loading:')
|
for (source, name), error in errors.items():
|
||||||
for name, error in invalid_themes:
|
theme_info = '({0}) {1}'.format(source, name)
|
||||||
print(' {0:<20}{1!r}'.format(name, error))
|
# Align multi-line error messages with the right column
|
||||||
|
err_message = six.text_type(error).replace('\n', '\n' + ' ' * 20)
|
||||||
|
print(' {0:<20}{1}'.format(theme_info, err_message))
|
||||||
|
|
||||||
print('')
|
print('')
|
||||||
|
|
||||||
@@ -315,21 +356,27 @@ class Theme(object):
|
|||||||
provided as an absolute file path, it will be loaded directly.
|
provided as an absolute file path, it will be loaded directly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
filenames = [
|
if os.path.isfile(name):
|
||||||
name,
|
return cls.from_file(name, 'custom')
|
||||||
os.path.join(path, '{0}.cfg'.format(name)),
|
|
||||||
os.path.join(DEFAULT_THEMES, '{0}.cfg'.format(name))]
|
|
||||||
|
|
||||||
for filename in filenames:
|
filename = os.path.join(path, '{0}.cfg'.format(name))
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
return cls.from_file(filename)
|
return cls.from_file(name, 'installed')
|
||||||
|
|
||||||
|
filename = os.path.join(DEFAULT_THEMES, '{0}.cfg'.format(name))
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
return cls.from_file(name, 'preset')
|
||||||
|
|
||||||
raise ConfigError('Could not find theme named "{0}"'.format(name))
|
raise ConfigError('Could not find theme named "{0}"'.format(name))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_file(cls, filename):
|
def from_file(cls, filename, source):
|
||||||
"""
|
"""
|
||||||
Load a theme from the specified configuration file.
|
Load a theme from the specified configuration file.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
filename: The name of the filename to load.
|
||||||
|
source: A description of where the theme was loaded from.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -348,15 +395,14 @@ class Theme(object):
|
|||||||
theme_name, _ = os.path.splitext(theme_name)
|
theme_name, _ = os.path.splitext(theme_name)
|
||||||
|
|
||||||
elements = {}
|
elements = {}
|
||||||
if config.has_section('theme'):
|
for element, line in config.items('theme'):
|
||||||
for element, line in config.items('theme'):
|
if element not in cls.DEFAULT_THEME:
|
||||||
if element not in cls.DEFAULT_THEME:
|
# Could happen if using a new config with an older version
|
||||||
# Could happen if using a new config with an older version
|
# of the software
|
||||||
# of the software
|
continue
|
||||||
continue
|
elements[element] = cls._parse_line(element, line, filename)
|
||||||
elements[element] = cls._parse_line(element, line, filename)
|
|
||||||
|
|
||||||
return cls(theme_name, elements)
|
return cls(name=theme_name, source=source, elements=elements)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _parse_line(cls, element, line, filename=None):
|
def _parse_line(cls, element, line, filename=None):
|
||||||
@@ -407,6 +453,11 @@ class Theme(object):
|
|||||||
else:
|
else:
|
||||||
attrs_code |= attr_code
|
attrs_code |= attr_code
|
||||||
|
|
||||||
|
if element.startswith('@') and None in (fg_code, bg_code, attrs_code):
|
||||||
|
raise ConfigError(
|
||||||
|
'Error loading {0}, {1} cannot have unspecified attributes:\n'
|
||||||
|
' {1} = {2}'.format(filename, element, line))
|
||||||
|
|
||||||
return fg_code, bg_code, attrs_code
|
return fg_code, bg_code, attrs_code
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -437,3 +488,56 @@ class Theme(object):
|
|||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeList(object):
|
||||||
|
"""
|
||||||
|
This is a small container around Theme.list_themes() that can be used
|
||||||
|
to cycle through all of the available themes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, current_theme=None):
|
||||||
|
|
||||||
|
self.index = 0
|
||||||
|
self.current_theme = current_theme
|
||||||
|
self.themes = None
|
||||||
|
self.errors = None
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
"""
|
||||||
|
This acts as a lazy load, it won't read all of the theme files from
|
||||||
|
disk until the first time somebody tries to access the theme list.
|
||||||
|
"""
|
||||||
|
self.themes, self.errors = Theme.list_themes()
|
||||||
|
|
||||||
|
if self.current_theme is not None:
|
||||||
|
# Try to find the starting index
|
||||||
|
key = (self.current_theme.source, self.current_theme.name)
|
||||||
|
for i, theme in enumerate(self.themes):
|
||||||
|
if (theme.source, theme.name) == key:
|
||||||
|
self.index = i
|
||||||
|
break
|
||||||
|
|
||||||
|
self.current_theme = self.themes[self.index]
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
"""
|
||||||
|
Retrieve the next theme in the list
|
||||||
|
"""
|
||||||
|
if not self.themes:
|
||||||
|
self.load()
|
||||||
|
|
||||||
|
self.index = (self.index + 1) % len(self.themes)
|
||||||
|
self.current_theme = self.themes[self.index]
|
||||||
|
return self.current_theme
|
||||||
|
|
||||||
|
def previous(self):
|
||||||
|
"""
|
||||||
|
Retrieve the previous theme in the list
|
||||||
|
"""
|
||||||
|
if not self.themes:
|
||||||
|
self.load()
|
||||||
|
|
||||||
|
self.index = (self.index - 1) % len(self.themes)
|
||||||
|
self.current_theme = self.themes[self.index]
|
||||||
|
return self.current_theme
|
||||||
|
|||||||
52
rtv/themes/default.cfg
Normal file
52
rtv/themes/default.cfg
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
[theme]
|
||||||
|
;<element> = <foreground> <background> <attributes>
|
||||||
|
@normal = default default normal
|
||||||
|
@highlight = default default normal
|
||||||
|
|
||||||
|
bar_level_1 = magenta - -
|
||||||
|
bar_level_1.highlight = magenta - reverse
|
||||||
|
bar_level_2 = cyan - -
|
||||||
|
bar_level_2.highlight = cyan - reverse
|
||||||
|
bar_level_3 = green - -
|
||||||
|
bar_level_3.highlight = green - reverse
|
||||||
|
bar_level_4 = yellow - -
|
||||||
|
bar_level_4.highlight = yellow - reverse
|
||||||
|
comment_author = blue - bold
|
||||||
|
comment_author_self = green - bold
|
||||||
|
comment_count = - - -
|
||||||
|
comment_text = - - -
|
||||||
|
created = - - -
|
||||||
|
cursor = - - -
|
||||||
|
cursor.highlight = - - reverse
|
||||||
|
downvote = red - bold
|
||||||
|
gold = yellow - bold
|
||||||
|
help_bar = cyan - bold+reverse
|
||||||
|
hidden_comment_expand = - - bold
|
||||||
|
hidden_comment_text = - - -
|
||||||
|
multireddit_name = yellow - bold
|
||||||
|
multireddit_text = - - -
|
||||||
|
neutral_vote = - - bold
|
||||||
|
notice_info = - - bold
|
||||||
|
notice_loading = - - bold
|
||||||
|
notice_error = red - bold
|
||||||
|
notice_success = green - bold
|
||||||
|
nsfw = red - bold+reverse
|
||||||
|
order_bar = yellow - bold
|
||||||
|
order_bar.highlight = yellow - bold+reverse
|
||||||
|
prompt = cyan - bold+reverse
|
||||||
|
saved = green - -
|
||||||
|
score = - - -
|
||||||
|
separator = - - bold
|
||||||
|
stickied = green - -
|
||||||
|
subscription_name = yellow - bold
|
||||||
|
subscription_text = - - -
|
||||||
|
submission_author = green - bold
|
||||||
|
submission_flair = red - -
|
||||||
|
submission_subreddit = yellow - -
|
||||||
|
submission_text = - - -
|
||||||
|
submission_title = - - bold
|
||||||
|
title_bar = cyan - bold+reverse
|
||||||
|
upvote = green - bold
|
||||||
|
url = blue - underline
|
||||||
|
url_seen = magenta - underline
|
||||||
|
user_flair = yellow - bold
|
||||||
@@ -14,7 +14,7 @@ from collections import Counter
|
|||||||
from vcr import VCR
|
from vcr import VCR
|
||||||
from six.moves.urllib.parse import urlparse, parse_qs
|
from six.moves.urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
from rtv.theme import Theme
|
from rtv.theme import ThemeList
|
||||||
from rtv.config import Config
|
from rtv.config import Config
|
||||||
from rtv.packages import praw
|
from rtv.packages import praw
|
||||||
from rtv.oauth import OAuthHelper
|
from rtv.oauth import OAuthHelper
|
||||||
@@ -91,7 +91,8 @@ def draw_screen(stdscr, reddit, config, theme, oauth):
|
|||||||
# Submission Page
|
# Submission Page
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
win1 = stdscr.derwin(tall_y - 1, mid_x - 1, 0, 0)
|
win1 = stdscr.derwin(tall_y - 1, mid_x - 1, 0, 0)
|
||||||
term = Terminal(win1, config, theme)
|
term = Terminal(win1, config)
|
||||||
|
term.set_theme(theme)
|
||||||
oauth.term = term
|
oauth.term = term
|
||||||
|
|
||||||
url = 'https://www.reddit.com/r/Python/comments/4dy7xr'
|
url = 'https://www.reddit.com/r/Python/comments/4dy7xr'
|
||||||
@@ -129,7 +130,8 @@ def draw_screen(stdscr, reddit, config, theme, oauth):
|
|||||||
# Subreddit Page
|
# Subreddit Page
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
win2 = stdscr.derwin(tall_y - 1, mid_x - 1, 0, mid_x + 1)
|
win2 = stdscr.derwin(tall_y - 1, mid_x - 1, 0, mid_x + 1)
|
||||||
term = Terminal(win2, config, theme)
|
term = Terminal(win2, config)
|
||||||
|
term.set_theme(theme)
|
||||||
oauth.term = term
|
oauth.term = term
|
||||||
|
|
||||||
with term.loader('Loading'):
|
with term.loader('Loading'):
|
||||||
@@ -157,7 +159,8 @@ def draw_screen(stdscr, reddit, config, theme, oauth):
|
|||||||
# Subscription Page
|
# Subscription Page
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
win3 = stdscr.derwin(short_y, mid_x - 1, tall_y, 0)
|
win3 = stdscr.derwin(short_y, mid_x - 1, tall_y, 0)
|
||||||
term = Terminal(win3, config, theme)
|
term = Terminal(win3, config)
|
||||||
|
term.set_theme(theme)
|
||||||
oauth.term = term
|
oauth.term = term
|
||||||
|
|
||||||
with term.loader('Loading'):
|
with term.loader('Loading'):
|
||||||
@@ -177,7 +180,8 @@ def draw_screen(stdscr, reddit, config, theme, oauth):
|
|||||||
# Multireddit Page
|
# Multireddit Page
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
win4 = stdscr.derwin(short_y, mid_x - 1, tall_y, mid_x + 1)
|
win4 = stdscr.derwin(short_y, mid_x - 1, tall_y, mid_x + 1)
|
||||||
term = Terminal(win4, config, theme)
|
term = Terminal(win4, config)
|
||||||
|
term.set_theme(theme)
|
||||||
oauth.term = term
|
oauth.term = term
|
||||||
|
|
||||||
with term.loader('Loading'):
|
with term.loader('Loading'):
|
||||||
@@ -193,7 +197,8 @@ def draw_screen(stdscr, reddit, config, theme, oauth):
|
|||||||
thread.start()
|
thread.start()
|
||||||
threads.append((thread, term))
|
threads.append((thread, term))
|
||||||
|
|
||||||
term = Terminal(win4, config, theme)
|
term = Terminal(win4, config)
|
||||||
|
term.set_theme(theme)
|
||||||
term.pause_getch = True
|
term.pause_getch = True
|
||||||
term.getch = MethodType(prompt_getch, term)
|
term.getch = MethodType(prompt_getch, term)
|
||||||
thread = threading.Thread(target=term.prompt_y_or_n, args=('Prompt: ',))
|
thread = threading.Thread(target=term.prompt_y_or_n, args=('Prompt: ',))
|
||||||
@@ -208,12 +213,13 @@ def draw_screen(stdscr, reddit, config, theme, oauth):
|
|||||||
def main():
|
def main():
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
theme_name = sys.argv[1]
|
current_theme = sys.argv[1]
|
||||||
else:
|
else:
|
||||||
theme_name = 'default'
|
current_theme = None
|
||||||
|
|
||||||
themes = Theme.list_themes()
|
theme_list = ThemeList(current_theme)
|
||||||
default_themes = sorted(themes['default'].keys())
|
theme_list.load()
|
||||||
|
theme = theme_list.current_theme
|
||||||
|
|
||||||
vcr = initialize_vcr()
|
vcr = initialize_vcr()
|
||||||
with vcr.use_cassette('demo_theme.yaml') as cassette, \
|
with vcr.use_cassette('demo_theme.yaml') as cassette, \
|
||||||
@@ -234,17 +240,18 @@ def main():
|
|||||||
config.history.add('https://www.reddit.com/r/Python/comments/6302cj/rpython_official_job_board/')
|
config.history.add('https://www.reddit.com/r/Python/comments/6302cj/rpython_official_job_board/')
|
||||||
|
|
||||||
term = Terminal(stdscr, config)
|
term = Terminal(stdscr, config)
|
||||||
|
term.set_theme()
|
||||||
oauth = OAuthHelper(reddit, term, config)
|
oauth = OAuthHelper(reddit, term, config)
|
||||||
oauth.authorize()
|
oauth.authorize()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
theme = Theme.from_name(theme_name)
|
term = Terminal(stdscr, config)
|
||||||
term = Terminal(stdscr, config, theme=theme)
|
term.set_theme(theme)
|
||||||
threads = draw_screen(stdscr, reddit, config, theme, oauth)
|
threads = draw_screen(stdscr, reddit, config, theme, oauth)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ch = term.show_notification(theme_name)
|
ch = term.show_notification(theme.display_string)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
ch = Terminal.ESCAPE
|
ch = Terminal.ESCAPE
|
||||||
|
|
||||||
@@ -258,11 +265,9 @@ def main():
|
|||||||
cassette.play_counts = Counter()
|
cassette.play_counts = Counter()
|
||||||
|
|
||||||
if ch == curses.KEY_RIGHT:
|
if ch == curses.KEY_RIGHT:
|
||||||
i = (default_themes.index(theme_name) + 1)
|
theme = theme_list.next()
|
||||||
theme_name = default_themes[i % len(default_themes)]
|
|
||||||
elif ch == curses.KEY_LEFT:
|
elif ch == curses.KEY_LEFT:
|
||||||
i = (default_themes.index(theme_name) - 1)
|
theme = theme_list.previous()
|
||||||
theme_name = default_themes[i % len(default_themes)]
|
|
||||||
elif ch == Terminal.ESCAPE:
|
elif ch == Terminal.ESCAPE:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user