rework gradient parsing; center bar; selectors

This commit is contained in:
Philip Wellnitz 2021-03-16 02:00:38 +09:00
parent 7310d53ade
commit 13d8261367
8 changed files with 468 additions and 53 deletions

View File

@ -901,6 +901,23 @@ class Powerline(object):
exc = e
yield FailedUnicode(safe_unicode(exc))
def force_update(self, *args, **kwargs):
'''Force a segment to update itself.
'''
try:
self.update_renderer()
return self.renderer.force_update(*args, **kwargs)
except Exception as e:
exc = e
try:
self.exception('Failed to force segment update: {0}', str(e))
except Exception as e:
exc = e
ret = FailedUnicode(safe_unicode(exc))
if kwargs.get('output_width', False):
ret = ret, len(ret)
return ret
def setup(self, *args, **kwargs):
'''Setup the environment to use powerline.

View File

@ -4,13 +4,13 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
from copy import copy
from powerline.lib.unicode import unicode
from colorsys import hsv_to_rgb, rgb_to_hsv
DEFAULT_MODE_KEY = None
ATTR_BOLD = 1
ATTR_ITALIC = 2
ATTR_UNDERLINE = 4
ATTR_OVERLINE = 8
def get_attrs_flag(attrs):
'''Convert an attribute array to a renderer flag.'''
@ -21,47 +21,99 @@ def get_attrs_flag(attrs):
attrs_flag |= ATTR_ITALIC
if 'underline' in attrs:
attrs_flag |= ATTR_UNDERLINE
if 'overline' in attrs:
attrs_flag |= ATTR_OVERLINE
return attrs_flag
def lerp(a, b, x):
return (1-x) * a + x * b
def pick_gradient_value(grad_list, gradient_level):
def round_col(*t):
return tuple(map(lambda x: int(x+0.5), t))
def pick_gradient_value(grad_list, gradient_level, is_hsv=False):
'''Given a list of colors and gradient percent, return a color that should be used.
Note: gradient level is not checked for being inside [0, 100] interval.
'''
return grad_list[int(round(gradient_level * (len(grad_list) - 1) / 100))]
idx = len(grad_list) * (gradient_level / 100)
fr = idx % 1
idx = int(idx)
if idx - 1 < 0:
return grad_list[0] if not is_hsv else rgb_to_hex(*round_col(*hsv_to_rgb(*grad_list[0])))
elif idx >= len(grad_list):
return grad_list[-1] if not is_hsv else rgb_to_hex(*round_col(*hsv_to_rgb(*grad_list[-1])))
if is_hsv:
h0, s0, v0 = grad_list[idx-1]
h1, s1, v1 = grad_list[idx]
else:
h0, s0, v0 = rgb_to_hsv(*hex_to_rgb(grad_list[idx-1]))
h1, s1, v1 = rgb_to_hsv(*hex_to_rgb(grad_list[idx]))
# adapt hue for gradients to black/white
if s1 < 10 ** -3:
h1 = h0
elif s0 < 10 ** -3:
h0 = h1
c = round_col(*hsv_to_rgb(lerp(h0, h1, fr), lerp(s0, s1, fr), lerp(v0, v1, fr)))
return rgb_to_hex(*c)
def add_transparency(str):
return "0x{0:f>8}".format(str[2:])
def hex_to_cterm(s):
'''Converts a string describing a hex color (e.g. "0xff6600") to an xterm color index'''
return cterm_color(*hex_str_to_rgb(add_transparency(s)))
class Colorscheme(object):
def __init__(self, colorscheme_config, colors_config):
'''Initialize a colorscheme.'''
self.colors = {}
self.gradients = {}
self.gradient_types = {}
self.groups = colorscheme_config['groups']
self.translations = colorscheme_config.get('mode_translations', {})
# Create a dict of color tuples with both a cterm and hex value
for color_name, color in colors_config['colors'].items():
try:
self.colors[color_name] = (color[0], int(color[1], 16))
except TypeError:
if type(color) == int or type(color) == bool:
self.colors[color_name] = (color, cterm_to_hex[color])
elif type(color) == str:
self.colors[color_name] = (hex_to_cterm(color), int(color, 16))
else:
self.colors[color_name] = (color[0], int(color[1], 16))
# Create a dict of gradient names with two lists: for cterm and hex
# values. Two lists in place of one list of pairs were chosen because
# Create a dict of gradient names with two lists: for cterm and hex
# values. Two lists in place of one list of pairs were chosen because
# true colors allow more precise gradients.
for gradient_name, gradient in colors_config['gradients'].items():
if len(gradient) == 2:
self.gradients[gradient_name] = (
(gradient[0], [int(color, 16) for color in gradient[1]]))
else:
self.gradients[gradient_name] = (
(gradient[0], [cterm_to_hex[color] for color in gradient[0]]))
if type(gradient[0]) == list:
if len(gradient) > 1 and type(gradient[1][0]) == float:
self.gradients[gradient_name]= gradient
self.gradient_types[gradient_name] = "hsv"
elif len(gradient) > 1: # legacy [[cterm], [hex]]
self.gradients[gradient_name] = [int(color, 16) for color in gradient[1]]
self.gradient_types[gradient_name] = "hex"
else:
self.gradients[gradient_name] = [cterm_to_hex[color] for color in gradient[0]]
self.gradient_types[gradient_name] = "hex"
elif type(gradient[0]) == str:
self.gradients[gradient_name] = [int(color, 16) for color in gradient]
self.gradient_types[gradient_name] = "hex"
elif type(gradient[0]) == int or type(gradient[0]) == bool:
self.gradients[gradient_name] = [cterm_to_hex[color] for color in gradient[0]]
self.gradient_types[gradient_name] = "hex"
def get_gradient(self, gradient, gradient_level):
if gradient in self.gradients:
return tuple((pick_gradient_value(grad_list, gradient_level) for grad_list in self.gradients[gradient]))
# cterm, hex
col = pick_gradient_value(self.gradients[gradient], gradient_level, is_hsv = (self. gradient_types[gradient] == "hsv"))
return (cterm_color(*hex_to_rgb(col)), col)
else:
return self.colors[gradient]
@ -105,14 +157,16 @@ class Colorscheme(object):
raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(groups))
if gradient_level is None:
pick_color = self.colors.__getitem__
pick_color = lambda str: (hex_to_cterm(str), int(add_transparency(str), 16)) if str. startswith('0x') or str.startswith('0X') else self.colors[str]
else:
pick_color = lambda gradient: self.get_gradient(gradient, gradient_level)
pick_color = lambda str: (hex_to_cterm(str), int(add_transparency(str), 16)) if str. startswith('0x') or str.startswith('0X') else self.get_gradient(str, gradient_level)
# attrs and click are optional
return {
'fg': pick_color(group_props['fg']),
'bg': pick_color(group_props['bg']),
'attrs': get_attrs_flag(group_props.get('attrs', [])),
'attrs': get_attrs_flag(group_props.get('attrs', [])) if 'attrs' in group_props else 0,
'click': group_props['click'] if 'click' in group_props else None
}
@ -145,3 +199,79 @@ cterm_to_hex = (
0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, # 24
0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee # 25
)
def rgb_to_hex_str(r, g, b):
return "0x{r:02x}{g:02x}{b:02x}".format(r=r, g=g, b=b)
def rgb_to_hex(r, g, b):
return r << 16 | g << 8 | b
def hex_str_to_rgb(s):
return int(s[-6:-4], 16), int(s[-4:-2], 16), int(s[-2:], 16)
def hex_to_rgb(x):
return tuple((x >> i) & 0xff for i in range(16, -1, -8))
def cterm_grey_number(x):
if x < 14:
return 0
else:
n = (x - 8) // 10
m = (x - 8) % 10
if m < 5:
return n
else:
return n + 1
def cterm_grey_level(n):
if n == 0:
return 0
return 10 * n + 8
def cterm_grey_color(n):
return {0: 0, 25: 231}.get(n, 231 + n)
def cterm_rgb_number(x):
if x < 75:
return 0
n = (x - 55) // 40
m = (x - 55) % 40
if m < 20:
return n
else:
return n + 1
def cterm_rgb_level(n):
if n == 0:
return 0
return 40 * n + 55
def cterm_rgb_color(x, y, z):
return 16 + (x * 36) + (y * 6) + z
def cterm_color(r, g, b):
if 3 < r == g == b < 243:
return int(int(r - 7.5) / 10) + 232
gx = cterm_grey_number(r)
gy = cterm_grey_number(g)
gz = cterm_grey_number(b)
x = cterm_rgb_number(r)
y = cterm_rgb_number(g)
z = cterm_rgb_number(b)
if gx == gy == gz:
dgr = cterm_grey_level(gx) - r
dgg = cterm_grey_level(gy) - g
dgb = cterm_grey_level(gz) - b
dgrey = dgr ** 2 + dgg ** 2 + dgb ** 2
dr = cterm_rgb_level(gx) - r
dg = cterm_rgb_level(gy) - g
db = cterm_rgb_level(gz) - b
drgb = dr ** 2 + dg ** 2 + db ** 2
if dgrey < drgb:
return cterm_grey_color(gx)
else:
return cterm_rgb_color(x, y, z)
else:
return cterm_rgb_color(x, y, z)

View File

@ -267,7 +267,7 @@ class Renderer(object):
Maximum width text can occupy. May be exceeded if there are too much
non-removable segments.
:param str side:
One of ``left``, ``right``. Determines which side will be rendered.
One of ``left``, ``right``, ``center``. Determines which side will be rendered.
If not present all sides are rendered.
:param int line:
Line number for which segments should be obtained. Is counted from
@ -315,6 +315,10 @@ class Renderer(object):
'hard': self.strwidth(theme.get_divider('right', 'hard')),
'soft': self.strwidth(theme.get_divider('right', 'soft')),
},
'center': {
'hard': self.strwidth(theme.get_divider('center', 'hard')),
'soft': self.strwidth(theme.get_divider('center', 'soft')),
},
}
hl_join = staticmethod(''.join)
@ -353,23 +357,28 @@ class Renderer(object):
divider_widths = self.compute_divider_widths(theme)
# Create an ordered list of segments that can be dropped
segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True)
segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: (-segment['priority'], segment['_len']))
no_priority_segments = filter(lambda segment: segment['priority'] is None, segments)
current_width = self._render_length(theme, segments, divider_widths)
if current_width > width:
for segment in chain(segments_priority, no_priority_segments):
if segment['truncate'] is not None:
segment['contents'] = segment['truncate'](self.pl, current_width - width, segment)
self._prepare_segments(segments, output_width or width)
current_width = self._render_length(theme, segments, divider_widths)
if current_width <= width:
break
if current_width > width:
segments_priority = iter(segments_priority)
if current_width > width and len(segments) > 100:
# When there are too many segments use faster, but less correct
# algorithm for width computation
diff = current_width - width
for segment in segments_priority:
segments.remove(segment)
diff -= segment['_len']
if diff <= 0:
current_width -= segment['_len']
if current_width <= width:
break
current_width = self._render_length(theme, segments, divider_widths)
if current_width > width:
@ -409,6 +418,54 @@ class Renderer(object):
return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width)
def force_update(self, mode, segment_name, matcher_info, segment_info = None, *args, **kwargs):
'''Force the first segment with name ``segment_name`` to update its content.
'''
theme = self.get_theme(matcher_info)
self.do_force_update(
mode,
segment_name,
theme,
segment_info=self.get_segment_info(segment_info, mode),
*args,
**kwargs)
def do_force_update(self, mode, segment_name, theme, segment_info = None, *args, **kwargs):
'''Force the first segment with name ``segment_name`` to update its content.
May silently render segment to obtain their ``payload_name``.
Only works for ThreadedSegments
'''
for i in range(0, len(theme.segments)):
for side in theme.segments[i]:
target = [segment for segment in theme.segments[i][side]
if 'type' in segment and segment['type'] == 'function' and
'name' in segment and segment['name'] == segment_name]
if len(target) > 0:
try:
target[0]['contents_func'](theme.pl, segment_info, force_update=True)
except TypeError:
pass
return
# Some segments have differing name and payload_name, so compute the latter
for i in range(0, len(theme.segments)):
for side in theme.segments[i]:
for segment in theme.segments[i][side]:
if segment['type'] != 'function':
continue
contents = segment['contents_func'](theme.pl, segment_info)
if contents == None:
continue
pns = [True for seg in contents if 'payload_name' in seg
and seg['payload_name'] == segment_name]
if len(pns):
try:
segment['contents_func'](theme.pl, segment_info, force_update=True)
except TypeError:
pass
return
return
def _prepare_segments(self, segments, calculate_contents_len):
'''Translate non-printable characters and calculate segment width
'''
@ -421,6 +478,13 @@ class Renderer(object):
else:
segment['_contents_len'] = self.strwidth(segment['contents'])
def _compare_bg(self, first_bg, second_bg):
if not first_bg and not second_bg:
return True
if not first_bg or not second_bg:
return False
return first_bg[0] == second_bg[0] and first_bg[1] == second_bg[1]
def _render_length(self, theme, segments, divider_widths):
'''Update segments lengths and return them
'''
@ -448,7 +512,7 @@ class Renderer(object):
side = segment['side']
segment_len = segment['_contents_len']
if not segment['literal_contents'][1]:
if side == 'left':
if side != 'right':
if segment is not last_segment:
compare_segment = next(iter((
segment
@ -460,12 +524,16 @@ class Renderer(object):
else:
compare_segment = prev_segment
divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard'
divider_type = 'soft' if self._compare_bg(compare_segment['highlight']['bg'], segment['highlight']['bg']) else 'hard'
outer_padding = int(bool(
segment is first_segment
if side == 'left' else
segment is last_segment
if side == 'right' else
segment is first_segment or segment is last_segment
if side == 'center' else
False
)) * theme.outer_padding
draw_divider = segment['draw_' + divider_type + '_divider']
@ -492,6 +560,7 @@ class Renderer(object):
segments_len = len(segments)
divider_spaces = theme.get_spaces()
prev_segment = theme.EMPTY_SEGMENT
next_segment = theme.EMPTY_SEGMENT
try:
first_segment = next(iter((
segment
@ -512,23 +581,28 @@ class Renderer(object):
for index, segment in enumerate(segments):
side = segment['side']
if not segment['literal_contents'][1]:
if side == 'left':
if segment is not last_segment:
compare_segment = next(iter((
segment
for segment in segments[index + 1:]
if not segment['literal_contents'][1]
)))
else:
compare_segment = theme.EMPTY_SEGMENT
if segment is not last_segment:
compare_segment = next(iter((
segment
for segment in segments[index + 1:]
if not segment['literal_contents'][1]
)))
else:
next_segment = theme.EMPTY_SEGMENT
if side != 'right':
compare_segment = next_segment
else:
compare_segment = prev_segment
outer_padding = int(bool(
segment is first_segment
if side == 'left' else
segment is last_segment
if side == 'right' else
segment is first_segment or segment is last_segment
if side == 'center' else
False
)) * theme.outer_padding * ' '
divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard'
divider_type = 'soft' if self._compare_bg(compare_segment['highlight']['bg'], segment['highlight']['bg']) else 'hard'
divider_highlighted = ''
contents_raw = segment['contents']
@ -541,9 +615,9 @@ class Renderer(object):
# XXX Make sure self.hl() calls are called in the same order
# segments are displayed. This is needed for Vim renderer to work.
if draw_divider:
if draw_divider and (side != 'center' or segment != last_segment):
divider_raw = self.escape(theme.get_divider(side, divider_type))
if side == 'left':
if side != 'right':
contents_raw = outer_padding + contents_raw + (divider_spaces * ' ')
else:
contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding
@ -556,25 +630,31 @@ class Renderer(object):
divider_fg = segment['highlight']['bg']
divider_bg = compare_segment['highlight']['bg']
if side == 'left':
if side != 'right':
if render_highlighted:
contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
contents_highlighted = self.hl(self.escape(contents_raw),
next_segment=next_segment,
**segment_hl_args)
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
segment['_rendered_raw'] = contents_raw + divider_raw
segment['_rendered_hl'] = contents_highlighted + divider_highlighted
else:
if render_highlighted:
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
contents_highlighted = self.hl(self.escape(contents_raw),
next_segment=next_segment,
**segment_hl_args)
segment['_rendered_raw'] = divider_raw + contents_raw
segment['_rendered_hl'] = divider_highlighted + contents_highlighted
else:
if side == 'left':
if side == 'left' or (side == 'center' and segment != last_segment):
contents_raw = outer_padding + contents_raw
else:
contents_raw = contents_raw + outer_padding
contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
contents_highlighted = self.hl(self.escape(contents_raw),
next_segment=next_segment,
**segment_hl_args)
segment['_rendered_raw'] = contents_raw
segment['_rendered_hl'] = contents_highlighted
prev_segment = segment

View File

@ -2,6 +2,7 @@
from __future__ import (unicode_literals, division, absolute_import, print_function)
from powerline.lib.watcher import create_file_watcher
from powerline.lib.dict import mergedicts_copy
def list_segment_key_values(segment, theme_configs, segment_data, key, function_name=None, name=None, module=None, default=None):
@ -181,7 +182,7 @@ def process_segment(pl, side, segment_info, parsed_segments, segment, mode, colo
return
if isinstance(contents, list):
# Needs copying here, but it was performed at the very start of the
# Needs copying here, but it was performed at the very start of the
# function
segment_base = segment
if contents:
@ -262,14 +263,48 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, function_name, name, module, default)
data['get_key'] = get_key
def get_selector(function_name):
def get_selector(function_details):
function_name = None
simple_mode = True
if isinstance(function_details, str):
# simple definition of include/exclude function
function_name = function_details
if isinstance(function_details, dict):
function_details = dict((
(str(k), v)
for k, v in
function_details.items()
))
function_name = function_details.get('function')
simple_mode = False
if not function_name:
pl.error('Failed to get segment selector name, ignoring the selector')
return None
if '.' in function_name:
module, function_name = function_name.rpartition('.')[::2]
else:
module = 'powerline.selectors.' + ext
function = get_module_attr(module, function_name, prefix='segment_generator/selector_function')
if getattr(function, 'powerline_layered_selector', False):
if simple_mode or not 'args' in function_details:
# simple mode doesn't support args for include/exclude functions
function = None
else:
layered_args = dict((
(str(k), v) if not getattr(function, 'powerline_recursive_selector', False)
else (str(k), get_selector(v))
for k, v in
function_details.get('args').items()
))
try:
function = function(**layered_args)
except TypeError:
function = None
if not function:
pl.error('Failed to get segment selector, ignoring it')
pl.error('Failed to get segment selector "{0}", ignoring it'.format(function_name))
return function
def get_segment_selector(segment, selector_type):
@ -370,12 +405,13 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
'divider_highlight_group': None,
'before': None,
'after': None,
'contents_func': lambda pl, segment_info, parsed_segments, side, mode, colorscheme: (
'contents_func': lambda pl, segment_info, parsed_segments, side, mode, colorscheme, **opt: (
process_segment_lister(
pl, segment_info, parsed_segments, side, mode, colorscheme,
patcher_args=args,
subsegments=subsegments,
lister=_contents_func,
**opt
)
),
'contents': None,
@ -409,9 +445,9 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
args[str('create_watcher')] = create_watcher
if hasattr(_contents_func, 'powerline_requires_segment_info'):
contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args)
contents_func = lambda pl, segment_info, **opt: _contents_func(pl=pl, segment_info=segment_info, **mergedicts_copy(args, opt))
else:
contents_func = lambda pl, segment_info: _contents_func(pl=pl, **args)
contents_func = lambda pl, segment_info, **opt: _contents_func(pl=pl, **mergedicts_copy(args, opt))
else:
startup_func = None
shutdown_func = None

View File

@ -0,0 +1,43 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
from powerline.theme import layered_selector, recursive_selector
@layered_selector
def mode(target_modes):
'''Returns True if the current mode to is contained in ``target_mode``
:param list target_modes:
The modes to filter.
'''
return lambda pl, segment_info, mode: (
mode in target_mode
)
@layered_selector
@recursive_selector
def all_of(**kwargs):
'''Checks whether all of the given conditions are satisfied
:param args condition:
Any argument passed to this selector will be interpreted as a selector on its own that may have arguments.
'''
return lambda pl, segment_info, mode: (
all([func(pl=pl, segment_info=segment_info, mode=mode) for arg, func in kwargs.items() if func])
)
@layered_selector
@recursive_selector
def any_of(**kwargs):
'''Checks whether any of the given conditions are satisfied
:param kwargs condition:
Any argument passed to this selector will be interpreted as a selector on its own that may have arguments.
'''
return lambda pl, segment_info, mode: (
any([func(pl=pl, segment_info=segment_info, mode=mode) for arg, func in kwargs.items() if func])
)

View File

@ -0,0 +1,33 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
from powerline.theme import layered_selector
from datetime import datetime, timezone
@layered_selector
def time(target_start_time, target_end_time, time_format = '%H:%M', time_zone = None):
'''Returns True if the current time to is between ``target_start_time`` and ``target_end_time``.
Times are to be specified in strftime-style format ``time_format``.
:param string target_start_time:
The (inclusive) start time.
:param string target_end_time:
The (exclusive) end time.
:param string time_format:
The strftime-style format to use for the given times.
:param string time_zone:
The time zone to use for the current time.
'''
try:
tz = datetime.strptime(time_zone, '%z').tzinfo if time_zone else None
except ValueError:
tz = None
def selector(pl, segment_info, mode):
nw = datetime.now(tz)
cur_time = datetime.strptime(nw.strftime(time_format), time_format)
return datetime.strptime(target_start_time, time_format) <= cur_time \
and cur_time < datetime.strptime(target_end_time, time_format)
return selector

View File

@ -0,0 +1,67 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
from powerline.theme import layered_selector
@layered_selector
def output(target_outputs):
'''Returns True if the current output rendered to is contained in ``target_output``
:param list target_outputs:
The outputs to filter.
'''
return lambda pl, segment_info, mode: (
'output' in segment_info
and segment_info['output'] in target_outputs
)
@layered_selector
def channel_full(channel_name):
'''Returns True while the specified channel exists and is filled with any value.
:param string channel_name:
The channel to check.
'''
return lambda pl, segment_info, mode: (
'payloads' in segment_info
and channel_name in segment_info['payloads']
and segment_info['payloads'][channel_name]
)
@layered_selector
def channel_empty(channel_name):
'''Returns True while the specified channel is empty or does not exist
:param string channel_name:
The channel to check.
'''
return lambda pl, segment_info, mode: (
not 'payloads' in segment_info
or not channel_name in segment_info['payloads']
or not segment_info['payloads'][channel_name]
)
@layered_selector
def channel_has_value(channel_name, value):
'''Returns True while the specified channel is filled with the specified value
:param string channel_name:
The channel to check.
:param string value:
The value to check against.
'''
return lambda pl, segment_info, mode: (
'payloads' in segment_info
and channel_name in segment_info['payloads']
and (
isinstance(segment_info['payloads'][channel_name], str)
and segment_info['payloads'][channel_name] == value
) or (
len(segment_info['payloads'][channel_name]) == 2
and segment_info['payloads'][channel_name][0] == value
)
)

View File

@ -16,11 +16,20 @@ def requires_filesystem_watcher(func):
func.powerline_requires_filesystem_watcher = True
return func
def layered_selector(func):
func.powerline_layered_selector = True
return func
def recursive_selector(func):
func.powerline_recursive_selector = True
return func
def new_empty_segment_line():
return {
'left': [],
'right': []
'right': [],
'center': []
}
@ -91,7 +100,7 @@ class Theme(object):
for segdict in itertools.chain((theme_config['segments'],),
theme_config['segments'].get('above', ())):
self.segments.append(new_empty_segment_line())
for side in ['left', 'right']:
for side in ['left', 'right', 'center']:
for segment in segdict.get(side, []):
segment = get_segment(segment, side)
if segment:
@ -130,10 +139,10 @@ class Theme(object):
and ljust/rjust properties applied.
:param int line:
Line number for which segments should be obtained. Is counted from
Line number for which segments should be obtained. Is counted from
zero (botmost line).
'''
for side in [side] if side else ['left', 'right']:
for side in [side] if side else ['left', 'right', 'center']:
parsed_segments = []
for segment in self.segments[line][side]:
if segment['display_condition'](self.pl, segment_info, mode):