bumblebee-status/core/output.py
2020-04-30 12:50:24 +02:00

205 lines
6.9 KiB
Python

import sys
import json
import time
import core.theme
import core.event
def dump_json(obj):
return obj.dict()
def assign(src, dst, key, src_key=None, default=None):
if not src_key:
src_key = key.replace('_', '-') # automagically replace - with _
for k in src_key if isinstance(src_key, list) else [src_key]:
if k in src:
dst[key] = src[k]
return
if default is not None:
dst[key] = default
class block(object):
__COMMON_THEME_FIELDS = [
'separator', 'separator-block-width', 'default-separators',
'border-top', 'border-left', 'border-right', 'border-bottom',
'fg', 'bg', 'padding', 'prefix', 'suffix'
]
def __init__(self, theme, module, widget):
self.__attributes = {}
for key in self.__COMMON_THEME_FIELDS:
tmp = theme.get(key, widget)
if tmp is not None:
self.__attributes[key] = tmp
self.__attributes['name'] = module.id
self.__attributes['instance'] = widget.id
self.__attributes['prev-bg'] = theme.get('bg', 'previous')
def set(self, key, value):
self.__attributes[key] = value
def is_pango(self, attr):
if isinstance(attr, dict) and 'pango' in attr:
return True
return False
def pangoize(self, text):
if not self.is_pango(text):
return text
self.__attributes['markup'] = 'pango'
attr = dict(text['pango'])
text = attr.get('full_text', '')
if 'full_text' in attr:
del attr['full_text']
result = '<span'
for key, value in attr.items():
result = '{} {}="{}"'.format(result, key, value)
result = '{}>{}</span>'.format(result, text)
return result
def dict(self):
result = {}
assign(self.__attributes, result, 'full_text', ['full_text', 'separator'])
assign(self.__attributes, result, 'separator', 'default-separators')
if '_decorator' in self.__attributes:
assign(self.__attributes, result, 'color', 'bg')
assign(self.__attributes, result, 'background', 'prev-bg')
result['_decorator'] = True
else:
assign(self.__attributes, result, 'color', 'fg')
assign(self.__attributes, result, 'background', 'bg')
if 'full_text' in self.__attributes:
result['full_text'] = self.pangoize(result['full_text'])
result['full_text'] = self.__format(self.__attributes['full_text'])
for k in [
'name', 'instance', 'separator_block_width', 'border', 'border_top',
'border_bottom', 'border_left', 'border_right', 'markup'
]:
assign(self.__attributes, result, k)
return result
def __pad(self, text):
padding = self.__attributes.get('padding', '')
if not text: return padding
return '{}{}{}'.format(padding, text, padding)
def __format(self, text):
if text is None: return None
prefix = self.pangoize(self.__attributes.get('prefix'))
suffix = self.pangoize(self.__attributes.get('suffix'))
return '{}{}{}'.format(
self.__pad(prefix),
text,
self.__pad(suffix)
)
class i3(object):
def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])):
self.__modules = []
self.__content = {}
self.__theme = theme
self.__config = config
core.event.register('update', self.update)
core.event.register('start', self.draw, 'start')
core.event.register('draw', self.draw, 'statusline')
core.event.register('stop', self.draw, 'stop')
def theme(self, new_theme=None):
if new_theme:
self.__theme = new_theme
return self.__theme
def modules(self, modules=None):
if not modules:
return self.__modules
self.__modules = modules if isinstance(modules, list) else [ modules ]
def draw(self, what, args=None):
cb = getattr(self, what)
data = cb(args) if args else cb()
if 'blocks' in data:
sys.stdout.write(json.dumps(data['blocks'], default=dump_json))
if 'suffix' in data:
sys.stdout.write(data['suffix'])
sys.stdout.write('\n')
sys.stdout.flush()
def start(self):
return {
'blocks': { 'version': 1, 'click_events': True },
'suffix': '\n[',
}
def stop(self):
return { 'suffix': '\n]' }
def __separator_block(self, module, widget):
if not self.__theme.get('separator'):
return []
blk = block(self.__theme, module, widget)
blk.set('_decorator', True)
return [blk]
def __content_block(self, module, widget):
blk = block(self.__theme, module, widget)
blk.set('min_width', widget.get('theme.minwidth'))
blk.set('full_text', self.__content[widget])
if widget.get('pango', False):
blk.set('markup', 'pango')
if self.__config.debug():
state = module.state(widget)
if isinstance(state, list):
state = ', '.join(state)
blk.set('__state', state)
return blk
def blocks(self, module):
blocks = []
for widget in module.widgets():
if widget.module and self.__config.autohide(widget.module.name):
if not any(state in widget.state() for state in [ 'warning', 'critical']):
continue
if module.hidden():
continue
blocks.extend(self.__separator_block(module, widget))
blocks.append(self.__content_block(module, widget))
core.event.trigger('next-widget')
return blocks
# TODO: only updates full text, not the state!?
def update(self, affected_modules=None, redraw_only=False):
now = time.time()
for module in self.__modules:
if affected_modules and not module.id in affected_modules:
continue
if not affected_modules and module.next_update:
if module.parameter('interval', '') == 'never':
continue
if now < module.next_update:
continue
if not redraw_only:
module.update_wrapper()
if module.parameter('interval', '') != 'never':
module.next_update = now + float(module.parameter('interval', self.__config.interval()))
for widget in module.widgets():
self.__content[widget] = widget.full_text()
def statusline(self):
blocks = []
for module in self.__modules:
blocks.extend(self.blocks(module))
return {
'blocks': blocks,
'suffix': ','
}
def wait(self, interval):
time.sleep(interval)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4