mirror of
https://github.com/powerline/powerline.git
synced 2025-07-31 01:35:40 +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
|
exc = e
|
||||||
yield FailedUnicode(safe_unicode(exc))
|
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):
|
def setup(self, *args, **kwargs):
|
||||||
'''Setup the environment to use powerline.
|
'''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 copy import copy
|
||||||
|
|
||||||
from powerline.lib.unicode import unicode
|
from powerline.lib.unicode import unicode
|
||||||
|
from colorsys import hsv_to_rgb, rgb_to_hsv
|
||||||
|
|
||||||
DEFAULT_MODE_KEY = None
|
DEFAULT_MODE_KEY = None
|
||||||
ATTR_BOLD = 1
|
ATTR_BOLD = 1
|
||||||
ATTR_ITALIC = 2
|
ATTR_ITALIC = 2
|
||||||
ATTR_UNDERLINE = 4
|
ATTR_UNDERLINE = 4
|
||||||
|
ATTR_OVERLINE = 8
|
||||||
|
|
||||||
def get_attrs_flag(attrs):
|
def get_attrs_flag(attrs):
|
||||||
'''Convert an attribute array to a renderer flag.'''
|
'''Convert an attribute array to a renderer flag.'''
|
||||||
@ -21,47 +21,99 @@ def get_attrs_flag(attrs):
|
|||||||
attrs_flag |= ATTR_ITALIC
|
attrs_flag |= ATTR_ITALIC
|
||||||
if 'underline' in attrs:
|
if 'underline' in attrs:
|
||||||
attrs_flag |= ATTR_UNDERLINE
|
attrs_flag |= ATTR_UNDERLINE
|
||||||
|
if 'overline' in attrs:
|
||||||
|
attrs_flag |= ATTR_OVERLINE
|
||||||
return attrs_flag
|
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.
|
'''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.
|
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):
|
class Colorscheme(object):
|
||||||
def __init__(self, colorscheme_config, colors_config):
|
def __init__(self, colorscheme_config, colors_config):
|
||||||
'''Initialize a colorscheme.'''
|
'''Initialize a colorscheme.'''
|
||||||
self.colors = {}
|
self.colors = {}
|
||||||
self.gradients = {}
|
self.gradients = {}
|
||||||
|
self.gradient_types = {}
|
||||||
|
|
||||||
self.groups = colorscheme_config['groups']
|
self.groups = colorscheme_config['groups']
|
||||||
self.translations = colorscheme_config.get('mode_translations', {})
|
self.translations = colorscheme_config.get('mode_translations', {})
|
||||||
|
|
||||||
# Create a dict of color tuples with both a cterm and hex value
|
# Create a dict of color tuples with both a cterm and hex value
|
||||||
for color_name, color in colors_config['colors'].items():
|
for color_name, color in colors_config['colors'].items():
|
||||||
try:
|
if type(color) == int or type(color) == bool:
|
||||||
self.colors[color_name] = (color[0], int(color[1], 16))
|
|
||||||
except TypeError:
|
|
||||||
self.colors[color_name] = (color, cterm_to_hex[color])
|
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
|
# 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
|
# values. Two lists in place of one list of pairs were chosen because
|
||||||
# true colors allow more precise gradients.
|
# true colors allow more precise gradients.
|
||||||
for gradient_name, gradient in colors_config['gradients'].items():
|
for gradient_name, gradient in colors_config['gradients'].items():
|
||||||
if len(gradient) == 2:
|
if type(gradient[0]) == list:
|
||||||
self.gradients[gradient_name] = (
|
if len(gradient) > 1 and type(gradient[1][0]) == float:
|
||||||
(gradient[0], [int(color, 16) for color in gradient[1]]))
|
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:
|
else:
|
||||||
self.gradients[gradient_name] = (
|
self.gradients[gradient_name] = [cterm_to_hex[color] for color in gradient[0]]
|
||||||
(gradient[0], [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):
|
def get_gradient(self, gradient, gradient_level):
|
||||||
if gradient in self.gradients:
|
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:
|
else:
|
||||||
return self.colors[gradient]
|
return self.colors[gradient]
|
||||||
|
|
||||||
@ -105,14 +157,16 @@ class Colorscheme(object):
|
|||||||
raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(groups))
|
raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(groups))
|
||||||
|
|
||||||
if gradient_level is None:
|
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:
|
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 {
|
return {
|
||||||
'fg': pick_color(group_props['fg']),
|
'fg': pick_color(group_props['fg']),
|
||||||
'bg': pick_color(group_props['bg']),
|
'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
|
0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, # 24
|
||||||
0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee # 25
|
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
|
Maximum width text can occupy. May be exceeded if there are too much
|
||||||
non-removable segments.
|
non-removable segments.
|
||||||
:param str side:
|
: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.
|
If not present all sides are rendered.
|
||||||
:param int line:
|
: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
|
||||||
@ -315,6 +315,10 @@ class Renderer(object):
|
|||||||
'hard': self.strwidth(theme.get_divider('right', 'hard')),
|
'hard': self.strwidth(theme.get_divider('right', 'hard')),
|
||||||
'soft': self.strwidth(theme.get_divider('right', 'soft')),
|
'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)
|
hl_join = staticmethod(''.join)
|
||||||
@ -353,23 +357,28 @@ class Renderer(object):
|
|||||||
divider_widths = self.compute_divider_widths(theme)
|
divider_widths = self.compute_divider_widths(theme)
|
||||||
|
|
||||||
# Create an ordered list of segments that can be dropped
|
# 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)
|
no_priority_segments = filter(lambda segment: segment['priority'] is None, segments)
|
||||||
current_width = self._render_length(theme, segments, divider_widths)
|
current_width = self._render_length(theme, segments, divider_widths)
|
||||||
|
|
||||||
if current_width > width:
|
if current_width > width:
|
||||||
for segment in chain(segments_priority, no_priority_segments):
|
for segment in chain(segments_priority, no_priority_segments):
|
||||||
if segment['truncate'] is not None:
|
if segment['truncate'] is not None:
|
||||||
segment['contents'] = segment['truncate'](self.pl, current_width - width, segment)
|
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)
|
segments_priority = iter(segments_priority)
|
||||||
if current_width > width and len(segments) > 100:
|
if current_width > width and len(segments) > 100:
|
||||||
# When there are too many segments use faster, but less correct
|
# When there are too many segments use faster, but less correct
|
||||||
# algorithm for width computation
|
# algorithm for width computation
|
||||||
diff = current_width - width
|
|
||||||
for segment in segments_priority:
|
for segment in segments_priority:
|
||||||
segments.remove(segment)
|
segments.remove(segment)
|
||||||
diff -= segment['_len']
|
current_width -= segment['_len']
|
||||||
if diff <= 0:
|
if current_width <= width:
|
||||||
break
|
break
|
||||||
current_width = self._render_length(theme, segments, divider_widths)
|
current_width = self._render_length(theme, segments, divider_widths)
|
||||||
if current_width > width:
|
if current_width > width:
|
||||||
@ -409,6 +418,54 @@ class Renderer(object):
|
|||||||
|
|
||||||
return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width)
|
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):
|
def _prepare_segments(self, segments, calculate_contents_len):
|
||||||
'''Translate non-printable characters and calculate segment width
|
'''Translate non-printable characters and calculate segment width
|
||||||
'''
|
'''
|
||||||
@ -421,6 +478,13 @@ class Renderer(object):
|
|||||||
else:
|
else:
|
||||||
segment['_contents_len'] = self.strwidth(segment['contents'])
|
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):
|
def _render_length(self, theme, segments, divider_widths):
|
||||||
'''Update segments lengths and return them
|
'''Update segments lengths and return them
|
||||||
'''
|
'''
|
||||||
@ -448,7 +512,7 @@ class Renderer(object):
|
|||||||
side = segment['side']
|
side = segment['side']
|
||||||
segment_len = segment['_contents_len']
|
segment_len = segment['_contents_len']
|
||||||
if not segment['literal_contents'][1]:
|
if not segment['literal_contents'][1]:
|
||||||
if side == 'left':
|
if side != 'right':
|
||||||
if segment is not last_segment:
|
if segment is not last_segment:
|
||||||
compare_segment = next(iter((
|
compare_segment = next(iter((
|
||||||
segment
|
segment
|
||||||
@ -460,12 +524,16 @@ class Renderer(object):
|
|||||||
else:
|
else:
|
||||||
compare_segment = prev_segment
|
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(
|
outer_padding = int(bool(
|
||||||
segment is first_segment
|
segment is first_segment
|
||||||
if side == 'left' else
|
if side == 'left' else
|
||||||
segment is last_segment
|
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
|
)) * theme.outer_padding
|
||||||
|
|
||||||
draw_divider = segment['draw_' + divider_type + '_divider']
|
draw_divider = segment['draw_' + divider_type + '_divider']
|
||||||
@ -492,6 +560,7 @@ class Renderer(object):
|
|||||||
segments_len = len(segments)
|
segments_len = len(segments)
|
||||||
divider_spaces = theme.get_spaces()
|
divider_spaces = theme.get_spaces()
|
||||||
prev_segment = theme.EMPTY_SEGMENT
|
prev_segment = theme.EMPTY_SEGMENT
|
||||||
|
next_segment = theme.EMPTY_SEGMENT
|
||||||
try:
|
try:
|
||||||
first_segment = next(iter((
|
first_segment = next(iter((
|
||||||
segment
|
segment
|
||||||
@ -512,7 +581,6 @@ class Renderer(object):
|
|||||||
for index, segment in enumerate(segments):
|
for index, segment in enumerate(segments):
|
||||||
side = segment['side']
|
side = segment['side']
|
||||||
if not segment['literal_contents'][1]:
|
if not segment['literal_contents'][1]:
|
||||||
if side == 'left':
|
|
||||||
if segment is not last_segment:
|
if segment is not last_segment:
|
||||||
compare_segment = next(iter((
|
compare_segment = next(iter((
|
||||||
segment
|
segment
|
||||||
@ -520,15 +588,21 @@ class Renderer(object):
|
|||||||
if not segment['literal_contents'][1]
|
if not segment['literal_contents'][1]
|
||||||
)))
|
)))
|
||||||
else:
|
else:
|
||||||
compare_segment = theme.EMPTY_SEGMENT
|
next_segment = theme.EMPTY_SEGMENT
|
||||||
|
if side != 'right':
|
||||||
|
compare_segment = next_segment
|
||||||
else:
|
else:
|
||||||
compare_segment = prev_segment
|
compare_segment = prev_segment
|
||||||
outer_padding = int(bool(
|
outer_padding = int(bool(
|
||||||
segment is first_segment
|
segment is first_segment
|
||||||
if side == 'left' else
|
if side == 'left' else
|
||||||
segment is last_segment
|
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 * ' '
|
)) * 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 = ''
|
divider_highlighted = ''
|
||||||
contents_raw = segment['contents']
|
contents_raw = segment['contents']
|
||||||
@ -541,9 +615,9 @@ class Renderer(object):
|
|||||||
|
|
||||||
# XXX Make sure self.hl() calls are called in the same order
|
# XXX Make sure self.hl() calls are called in the same order
|
||||||
# segments are displayed. This is needed for Vim renderer to work.
|
# 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))
|
divider_raw = self.escape(theme.get_divider(side, divider_type))
|
||||||
if side == 'left':
|
if side != 'right':
|
||||||
contents_raw = outer_padding + contents_raw + (divider_spaces * ' ')
|
contents_raw = outer_padding + contents_raw + (divider_spaces * ' ')
|
||||||
else:
|
else:
|
||||||
contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding
|
contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding
|
||||||
@ -556,25 +630,31 @@ class Renderer(object):
|
|||||||
divider_fg = segment['highlight']['bg']
|
divider_fg = segment['highlight']['bg']
|
||||||
divider_bg = compare_segment['highlight']['bg']
|
divider_bg = compare_segment['highlight']['bg']
|
||||||
|
|
||||||
if side == 'left':
|
if side != 'right':
|
||||||
if render_highlighted:
|
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)
|
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
|
||||||
segment['_rendered_raw'] = contents_raw + divider_raw
|
segment['_rendered_raw'] = contents_raw + divider_raw
|
||||||
segment['_rendered_hl'] = contents_highlighted + divider_highlighted
|
segment['_rendered_hl'] = contents_highlighted + divider_highlighted
|
||||||
else:
|
else:
|
||||||
if render_highlighted:
|
if render_highlighted:
|
||||||
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
|
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_raw'] = divider_raw + contents_raw
|
||||||
segment['_rendered_hl'] = divider_highlighted + contents_highlighted
|
segment['_rendered_hl'] = divider_highlighted + contents_highlighted
|
||||||
else:
|
else:
|
||||||
if side == 'left':
|
if side == 'left' or (side == 'center' and segment != last_segment):
|
||||||
contents_raw = outer_padding + contents_raw
|
contents_raw = outer_padding + contents_raw
|
||||||
else:
|
else:
|
||||||
contents_raw = contents_raw + outer_padding
|
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_raw'] = contents_raw
|
||||||
segment['_rendered_hl'] = contents_highlighted
|
segment['_rendered_hl'] = contents_highlighted
|
||||||
prev_segment = segment
|
prev_segment = segment
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
from powerline.lib.watcher import create_file_watcher
|
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):
|
def list_segment_key_values(segment, theme_configs, segment_data, key, function_name=None, name=None, module=None, default=None):
|
||||||
@ -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)
|
return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, function_name, name, module, default)
|
||||||
data['get_key'] = get_key
|
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:
|
if '.' in function_name:
|
||||||
module, function_name = function_name.rpartition('.')[::2]
|
module, function_name = function_name.rpartition('.')[::2]
|
||||||
else:
|
else:
|
||||||
module = 'powerline.selectors.' + ext
|
module = 'powerline.selectors.' + ext
|
||||||
function = get_module_attr(module, function_name, prefix='segment_generator/selector_function')
|
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:
|
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
|
return function
|
||||||
|
|
||||||
def get_segment_selector(segment, selector_type):
|
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,
|
'divider_highlight_group': None,
|
||||||
'before': None,
|
'before': None,
|
||||||
'after': 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(
|
process_segment_lister(
|
||||||
pl, segment_info, parsed_segments, side, mode, colorscheme,
|
pl, segment_info, parsed_segments, side, mode, colorscheme,
|
||||||
patcher_args=args,
|
patcher_args=args,
|
||||||
subsegments=subsegments,
|
subsegments=subsegments,
|
||||||
lister=_contents_func,
|
lister=_contents_func,
|
||||||
|
**opt
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'contents': None,
|
'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
|
args[str('create_watcher')] = create_watcher
|
||||||
|
|
||||||
if hasattr(_contents_func, 'powerline_requires_segment_info'):
|
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:
|
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:
|
else:
|
||||||
startup_func = None
|
startup_func = None
|
||||||
shutdown_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
|
func.powerline_requires_filesystem_watcher = True
|
||||||
return func
|
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():
|
def new_empty_segment_line():
|
||||||
return {
|
return {
|
||||||
'left': [],
|
'left': [],
|
||||||
'right': []
|
'right': [],
|
||||||
|
'center': []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,7 +100,7 @@ class Theme(object):
|
|||||||
for segdict in itertools.chain((theme_config['segments'],),
|
for segdict in itertools.chain((theme_config['segments'],),
|
||||||
theme_config['segments'].get('above', ())):
|
theme_config['segments'].get('above', ())):
|
||||||
self.segments.append(new_empty_segment_line())
|
self.segments.append(new_empty_segment_line())
|
||||||
for side in ['left', 'right']:
|
for side in ['left', 'right', 'center']:
|
||||||
for segment in segdict.get(side, []):
|
for segment in segdict.get(side, []):
|
||||||
segment = get_segment(segment, side)
|
segment = get_segment(segment, side)
|
||||||
if segment:
|
if segment:
|
||||||
@ -133,7 +142,7 @@ class Theme(object):
|
|||||||
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).
|
zero (botmost line).
|
||||||
'''
|
'''
|
||||||
for side in [side] if side else ['left', 'right']:
|
for side in [side] if side else ['left', 'right', 'center']:
|
||||||
parsed_segments = []
|
parsed_segments = []
|
||||||
for segment in self.segments[line][side]:
|
for segment in self.segments[line][side]:
|
||||||
if segment['display_condition'](self.pl, segment_info, mode):
|
if segment['display_condition'](self.pl, segment_info, mode):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user