mirror of
https://github.com/powerline/powerline.git
synced 2025-04-08 19:25:04 +02:00
rework gradient parsing; center bar; selectors
This commit is contained in:
parent
7310d53ade
commit
13d8261367
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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])
|
||||
)
|
||||
|
33
powerline/selectors/common.py
Normal file
33
powerline/selectors/common.py
Normal 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
|
67
powerline/selectors/i3wm.py
Normal file
67
powerline/selectors/i3wm.py
Normal 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
|
||||
)
|
||||
)
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user