dotfiles/config/lemonbar/display.py

724 lines
20 KiB
Python
Raw Normal View History

2018-08-21 19:50:44 +00:00
#!/usr/bin/env python3
import enum
import threading
import time
2018-10-06 08:27:36 +00:00
import i3ipc
import os
import signal
2018-08-22 10:04:55 +00:00
import subprocess
2018-09-06 10:17:03 +00:00
import logging
import coloredlogs
2018-10-14 14:59:49 +00:00
import updaters
2018-09-06 10:17:03 +00:00
2021-06-13 09:49:21 +00:00
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
2018-09-06 10:17:03 +00:00
log = logging.getLogger()
2018-08-21 19:50:44 +00:00
2018-10-06 08:27:36 +00:00
2018-08-21 19:50:44 +00:00
# TODO Allow deletion of Bar, BarGroup and Section for screen changes
2018-09-05 07:07:37 +00:00
# IDEA Use i3 ipc events rather than relying on xrandr or Xlib (less portable
# but easier)
2018-08-21 19:50:44 +00:00
# TODO Optimize to use write() calls instead of string concatenation (writing
# BarGroup strings should be a good compromise)
2018-08-22 10:04:55 +00:00
# TODO Use bytes rather than strings
# TODO Use default colors of lemonbar sometimes
2018-09-06 05:38:22 +00:00
# TODO Adapt bar height with font height
2018-10-06 08:27:36 +00:00
# TODO OPTI Static text objects that update its parents if modified
2018-10-18 19:14:11 +00:00
# TODO forceSize and changeText are different
2018-08-21 19:50:44 +00:00
class BarGroupType(enum.Enum):
LEFT = 0
RIGHT = 1
2018-09-05 07:07:37 +00:00
# TODO Middle
2018-08-21 19:50:44 +00:00
# MID_LEFT = 2
# MID_RIGHT = 3
2018-09-06 10:17:03 +00:00
class BarStdoutThread(threading.Thread):
2020-09-24 08:47:35 +00:00
def run(self) -> None:
2018-10-06 08:27:36 +00:00
while Bar.running:
2018-09-06 10:17:03 +00:00
handle = Bar.process.stdout.readline().strip()
if not len(handle):
2018-10-18 19:14:11 +00:00
Bar.stop()
2018-09-06 10:17:03 +00:00
if handle not in Bar.actionsH2F:
log.error("Unknown action: {}".format(handle))
continue
function = Bar.actionsH2F[handle]
function()
2018-08-21 19:50:44 +00:00
class Bar:
"""
One bar for each screen
"""
2018-08-22 10:04:55 +00:00
# Constants
2021-07-04 07:52:16 +00:00
FONTS = ["DejaVuSansMono Nerd Font Mono", "Font Awesome"]
2018-09-06 05:38:22 +00:00
FONTSIZE = 10
2018-08-21 19:50:44 +00:00
@staticmethod
2020-09-24 08:47:35 +00:00
def init() -> None:
2018-10-06 08:27:36 +00:00
Bar.running = True
2018-08-21 19:50:44 +00:00
Section.init()
2021-06-13 09:49:21 +00:00
cmd = ["lemonbar", "-b", "-a", "64"]
2018-09-05 07:07:37 +00:00
for font in Bar.FONTS:
2018-09-06 05:38:22 +00:00
cmd += ["-f", "{}:size={}".format(font, Bar.FONTSIZE)]
2021-06-13 09:49:21 +00:00
Bar.process = subprocess.Popen(
cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
Bar.stdoutThread = BarStdoutThread()
2018-09-06 10:17:03 +00:00
Bar.stdoutThread.start()
2018-08-22 10:04:55 +00:00
2018-08-21 19:50:44 +00:00
# Debug
2018-09-05 07:07:37 +00:00
Bar(0)
# Bar(1)
2018-08-22 10:04:55 +00:00
2018-10-06 08:27:36 +00:00
@staticmethod
2020-09-24 08:47:35 +00:00
def stop() -> None:
2018-10-06 08:27:36 +00:00
Bar.running = False
Bar.process.kill()
# TODO This is not really the best way to do it I guess
os.killpg(os.getpid(), signal.SIGTERM)
@staticmethod
2020-09-24 08:47:35 +00:00
def run() -> None:
2018-10-06 08:27:36 +00:00
Bar.forever()
i3 = i3ipc.Connection()
2020-09-24 08:47:35 +00:00
def doStop(*args) -> None:
2018-10-06 08:27:36 +00:00
Bar.stop()
print(88)
try:
2021-06-13 09:49:21 +00:00
i3.on("ipc_shutdown", doStop)
2018-10-06 08:27:36 +00:00
i3.main()
except BaseException:
print(93)
Bar.stop()
2018-08-22 10:04:55 +00:00
# Class globals
everyone = set()
string = ""
process = None
2018-10-06 08:27:36 +00:00
running = False
2018-08-21 19:50:44 +00:00
2018-09-06 10:17:03 +00:00
nextHandle = 0
actionsF2H = dict()
actionsH2F = dict()
@staticmethod
def getFunctionHandle(function):
assert callable(function)
if function in Bar.actionsF2H.keys():
return Bar.actionsF2H[function]
2021-06-13 09:49:21 +00:00
handle = "{:x}".format(Bar.nextHandle).encode()
2018-09-06 10:17:03 +00:00
Bar.nextHandle += 1
Bar.actionsF2H[function] = handle
Bar.actionsH2F[handle] = function
return handle
2018-09-05 07:07:37 +00:00
@staticmethod
def forever():
2018-10-18 19:14:11 +00:00
Bar.process.wait()
Bar.stop()
2018-09-05 07:07:37 +00:00
2018-08-22 10:04:55 +00:00
def __init__(self, screen):
assert isinstance(screen, int)
self.screen = "%{S" + str(screen) + "}"
2018-08-21 19:50:44 +00:00
self.groups = dict()
for groupType in BarGroupType:
group = BarGroup(groupType, self)
self.groups[groupType] = group
self.childsChanged = False
self.everyone.add(self)
@staticmethod
def addSectionAll(section, group, screens=None):
"""
.. note::
Add the section before updating it for the first time.
"""
assert isinstance(section, Section)
assert isinstance(group, BarGroupType)
# TODO screens selection
for bar in Bar.everyone:
bar.addSection(section, group=group)
def addSection(self, section, group):
assert isinstance(section, Section)
assert isinstance(group, BarGroupType)
self.groups[group].addSection(section)
def update(self):
if self.childsChanged:
2018-08-22 10:04:55 +00:00
self.string = self.screen
self.string += self.groups[BarGroupType.LEFT].string
self.string += self.groups[BarGroupType.RIGHT].string
2018-08-21 19:50:44 +00:00
self.childsChanged = False
@staticmethod
def updateAll():
2018-10-06 08:27:36 +00:00
if Bar.running:
Bar.string = ""
for bar in Bar.everyone:
bar.update()
Bar.string += bar.string
# Color for empty sections
Bar.string += BarGroup.color(*Section.EMPTY)
2018-08-21 19:50:44 +00:00
2020-09-24 08:47:35 +00:00
# print(Bar.string)
2021-06-13 09:49:21 +00:00
Bar.process.stdin.write(bytes(Bar.string + "\n", "utf-8"))
2018-10-06 08:27:36 +00:00
Bar.process.stdin.flush()
2018-08-21 19:50:44 +00:00
class BarGroup:
"""
One for each group of each bar
"""
everyone = set()
def __init__(self, groupType, parent):
assert isinstance(groupType, BarGroupType)
assert isinstance(parent, Bar)
self.groupType = groupType
self.parent = parent
self.sections = list()
2021-06-13 09:49:21 +00:00
self.string = ""
2018-08-22 10:04:55 +00:00
self.parts = []
2018-08-21 19:50:44 +00:00
#: One of the sections that had their theme or visibility changed
self.childsThemeChanged = False
#: One of the sections that had their text (maybe their size) changed
self.childsTextChanged = False
BarGroup.everyone.add(self)
def addSection(self, section):
self.sections.append(section)
2018-09-05 07:07:37 +00:00
section.addParent(self)
def addSectionAfter(self, sectionRef, section):
index = self.sections.index(sectionRef)
self.sections.insert(index + 1, section)
section.addParent(self)
2018-08-21 19:50:44 +00:00
2018-08-22 10:04:55 +00:00
ALIGNS = {BarGroupType.LEFT: "%{l}", BarGroupType.RIGHT: "%{r}"}
@staticmethod
def fgColor(color):
2021-06-13 09:49:21 +00:00
return "%{F" + (color or "-") + "}"
2018-08-22 10:04:55 +00:00
@staticmethod
def bgColor(color):
2021-06-13 09:49:21 +00:00
return "%{B" + (color or "-") + "}"
2018-09-06 05:38:22 +00:00
2018-08-22 10:04:55 +00:00
@staticmethod
def color(fg, bg):
return BarGroup.fgColor(fg) + BarGroup.bgColor(bg)
2018-08-21 19:50:44 +00:00
def update(self):
if self.childsThemeChanged:
2018-08-22 10:04:55 +00:00
parts = [BarGroup.ALIGNS[self.groupType]]
secs = [sec for sec in self.sections if sec.visible]
lenS = len(secs)
for s in range(lenS):
sec = secs[s]
theme = Section.THEMES[sec.theme]
if self.groupType == BarGroupType.LEFT:
oSec = secs[s + 1] if s < lenS - 1 else None
else:
2018-09-05 07:07:37 +00:00
oSec = secs[s - 1] if s > 0 else None
2021-06-13 09:49:21 +00:00
oTheme = (
Section.THEMES[oSec.theme] if oSec is not None else Section.EMPTY
)
2018-08-22 10:04:55 +00:00
if self.groupType == BarGroupType.LEFT:
if s == 0:
parts.append(BarGroup.bgColor(theme[1]))
parts.append(BarGroup.fgColor(theme[0]))
parts.append(sec)
2018-09-05 07:07:37 +00:00
if theme == oTheme:
parts.append("")
else:
parts.append(BarGroup.color(theme[1], oTheme[1]) + "")
2018-08-22 10:04:55 +00:00
else:
2018-09-05 07:07:37 +00:00
if theme is oTheme:
parts.append("")
else:
parts.append(BarGroup.fgColor(theme[1]) + "")
parts.append(BarGroup.color(*theme))
2018-08-22 10:04:55 +00:00
parts.append(sec)
2018-09-06 05:38:22 +00:00
# TODO OPTI Concatenate successive strings
2018-08-22 10:04:55 +00:00
self.parts = parts
2018-08-21 19:50:44 +00:00
if self.childsTextChanged or self.childsThemeChanged:
2018-08-22 10:04:55 +00:00
self.string = ""
for part in self.parts:
if isinstance(part, str):
self.string += part
elif isinstance(part, Section):
self.string += part.curText
2018-08-21 19:50:44 +00:00
self.parent.childsChanged = True
self.childsThemeChanged = False
self.childsTextChanged = False
@staticmethod
def updateAll():
for group in BarGroup.everyone:
group.update()
Bar.updateAll()
class SectionThread(threading.Thread):
2018-09-05 07:07:37 +00:00
ANIMATION_START = 0.025
ANIMATION_STOP = 0.001
ANIMATION_EVOLUTION = 0.9
2018-10-14 14:59:49 +00:00
2018-08-21 19:50:44 +00:00
def run(self):
while Section.somethingChanged.wait():
2018-10-14 14:59:49 +00:00
updaters.notBusy.wait()
2018-08-21 19:50:44 +00:00
Section.updateAll()
2018-09-05 07:07:37 +00:00
animTime = self.ANIMATION_START
frameTime = time.perf_counter()
2018-08-21 19:50:44 +00:00
while len(Section.sizeChanging) > 0:
2018-09-05 07:07:37 +00:00
frameTime += animTime
curTime = time.perf_counter()
sleepTime = frameTime - curTime
time.sleep(sleepTime if sleepTime > 0 else 0)
2018-08-21 19:50:44 +00:00
Section.updateAll()
2018-09-05 07:07:37 +00:00
animTime *= self.ANIMATION_EVOLUTION
if animTime < self.ANIMATION_STOP:
animTime = self.ANIMATION_STOP
2018-08-21 19:50:44 +00:00
class Section:
2018-08-22 10:04:55 +00:00
# TODO Update all of that to base16
2018-10-06 08:27:36 +00:00
# COLORS = ['#272822', '#383830', '#49483e', '#75715e', '#a59f85', '#f8f8f2',
# '#f5f4f1', '#f9f8f5', '#f92672', '#fd971f', '#f4bf75', '#a6e22e',
# '#a1efe4', '#66d9ef', '#ae81ff', '#cc6633']
2021-06-13 09:49:21 +00:00
COLORS = [
"#181818",
"#AB4642",
"#A1B56C",
"#F7CA88",
"#7CAFC2",
"#BA8BAF",
"#86C1B9",
"#D8D8D8",
"#585858",
"#AB4642",
"#A1B56C",
"#F7CA88",
"#7CAFC2",
"#BA8BAF",
"#86C1B9",
"#F8F8F8",
]
FGCOLOR = "#F8F8F2"
BGCOLOR = "#272822"
2018-08-22 10:04:55 +00:00
THEMES = list()
EMPTY = (FGCOLOR, BGCOLOR)
2018-08-21 19:50:44 +00:00
ICON = None
PERSISTENT = False
2018-09-05 07:07:37 +00:00
#: Sections that do not have their destination size
sizeChanging = set()
updateThread = SectionThread(daemon=True)
somethingChanged = threading.Event()
2018-09-06 05:38:22 +00:00
lastChosenTheme = 0
2018-09-05 07:07:37 +00:00
2018-08-21 19:50:44 +00:00
@staticmethod
def init():
2018-08-22 10:04:55 +00:00
for t in range(8, 16):
Section.THEMES.append((Section.COLORS[0], Section.COLORS[t]))
2018-08-21 19:50:44 +00:00
Section.updateThread.start()
2018-09-06 05:38:22 +00:00
def __init__(self, theme=None):
2018-08-21 19:50:44 +00:00
#: Displayed section
#: Note: A section can be empty and displayed!
self.visible = False
2018-09-06 05:38:22 +00:00
if theme is None:
theme = Section.lastChosenTheme
2021-06-13 09:49:21 +00:00
Section.lastChosenTheme = (Section.lastChosenTheme + 1) % len(
Section.THEMES
)
2018-08-22 10:04:55 +00:00
self.theme = theme
2018-08-21 19:50:44 +00:00
#: Displayed text
2021-06-13 09:49:21 +00:00
self.curText = ""
2018-08-21 19:50:44 +00:00
#: Displayed text size
self.curSize = 0
#: Destination text
2021-06-13 09:49:21 +00:00
self.dstText = Text(" ", Text(), " ")
2018-08-21 19:50:44 +00:00
#: Destination size
self.dstSize = 0
#: Groups that have this section
self.parents = set()
self.icon = self.ICON
2018-09-06 20:07:32 +00:00
self.persistent = self.PERSISTENT
2018-08-22 10:04:55 +00:00
def __str__(self):
2018-09-05 07:07:37 +00:00
try:
2021-06-13 09:49:21 +00:00
return "<{}><{}>{:01d}{}{:02d}/{:02d}".format(
self.curText,
self.dstText,
self.theme,
"+" if self.visible else "-",
self.curSize,
self.dstSize,
)
2018-09-05 07:07:37 +00:00
except:
return super().__str__()
def addParent(self, parent):
self.parents.add(parent)
def appendAfter(self, section):
assert len(self.parents)
for parent in self.parents:
parent.addSectionAfter(self, section)
2018-08-22 10:04:55 +00:00
2018-08-21 19:50:44 +00:00
def informParentsThemeChanged(self):
for parent in self.parents:
parent.childsThemeChanged = True
def informParentsTextChanged(self):
for parent in self.parents:
parent.childsTextChanged = True
def updateText(self, text):
if isinstance(text, str):
text = Text(text)
2018-10-06 08:27:36 +00:00
elif isinstance(text, Text) and not len(text.elements):
text = None
2018-09-05 07:07:37 +00:00
2021-06-13 09:49:21 +00:00
self.dstText[0] = (
None
if (text is None and not self.persistent)
else ((" " + self.icon + " ") if self.icon else " ")
)
2018-09-06 20:07:32 +00:00
self.dstText[1] = text
2021-06-13 09:49:21 +00:00
self.dstText[2] = (
" " if self.dstText[1] is not None and len(self.dstText[1]) else None
)
2018-09-06 05:38:22 +00:00
2018-09-06 20:07:32 +00:00
self.dstSize = len(self.dstText)
self.dstText.setSection(self)
2018-09-06 10:17:03 +00:00
2018-08-21 19:50:44 +00:00
if self.curSize == self.dstSize:
if self.dstSize > 0:
self.curText = str(self.dstText)
self.informParentsTextChanged()
2018-08-21 19:50:44 +00:00
else:
Section.sizeChanging.add(self)
Section.somethingChanged.set()
2018-10-06 08:27:36 +00:00
def setDecorators(self, **kwargs):
self.dstText.setDecorators(**kwargs)
self.curText = str(self.dstText)
self.informParentsTextChanged()
Section.somethingChanged.set()
2018-08-22 10:04:55 +00:00
def updateTheme(self, theme):
assert isinstance(theme, int)
assert theme < len(Section.THEMES)
if theme == self.theme:
return
2018-08-22 10:04:55 +00:00
self.theme = theme
self.informParentsThemeChanged()
Section.somethingChanged.set()
2018-08-21 19:50:44 +00:00
def updateVisibility(self, visibility):
assert isinstance(visibility, bool)
self.visible = visibility
self.informParentsThemeChanged()
Section.somethingChanged.set()
@staticmethod
def fit(text, size):
t = len(text)
2018-09-06 05:38:22 +00:00
return text[:size] if t >= size else text + [" "] * (size - t)
2018-08-21 19:50:44 +00:00
def update(self):
# TODO Might profit of a better logic
if not self.visible:
self.updateVisibility(True)
return
if self.dstSize > self.curSize:
self.curSize += 1
elif self.dstSize < self.curSize:
self.curSize -= 1
else:
# Visibility toggling must be done one step after curSize = 0
if self.dstSize == 0:
self.updateVisibility(False)
Section.sizeChanging.remove(self)
return
self.curText = self.dstText.text(size=self.curSize, pad=True)
2018-08-21 19:50:44 +00:00
self.informParentsTextChanged()
@staticmethod
def updateAll():
"""
Process all sections for text size changes
"""
for sizeChanging in Section.sizeChanging.copy():
sizeChanging.update()
BarGroup.updateAll()
Section.somethingChanged.clear()
2018-09-05 07:07:37 +00:00
@staticmethod
def ramp(p, ramp=" ▁▂▃▄▅▆▇█"):
2018-09-06 05:38:22 +00:00
if p > 1:
return ramp[-1]
elif p < 0:
return ramp[0]
else:
2021-06-13 09:49:21 +00:00
return ramp[round(p * (len(ramp) - 1))]
2018-09-06 05:38:22 +00:00
class StatefulSection(Section):
2018-09-06 20:07:32 +00:00
# TODO FEAT Allow to temporary expand the section (e.g. when important change)
NUMBER_STATES = None
2018-10-06 08:27:36 +00:00
DEFAULT_STATE = 0
2018-09-06 10:17:03 +00:00
def __init__(self, *args, **kwargs):
Section.__init__(self, *args, **kwargs)
2018-10-06 08:27:36 +00:00
self.state = self.DEFAULT_STATE
2021-06-13 09:49:21 +00:00
if hasattr(self, "onChangeState"):
2018-09-06 10:17:03 +00:00
self.onChangeState(self.state)
2021-06-13 09:49:21 +00:00
self.setDecorators(
clickLeft=self.incrementState, clickRight=self.decrementState
)
2018-09-06 10:17:03 +00:00
def incrementState(self):
newState = min(self.state + 1, self.NUMBER_STATES - 1)
self.changeState(newState)
def decrementState(self):
newState = max(self.state - 1, 0)
self.changeState(newState)
def changeState(self, state):
assert isinstance(state, int)
assert state < self.NUMBER_STATES
self.state = state
2021-06-13 09:49:21 +00:00
if hasattr(self, "onChangeState"):
2018-09-06 10:17:03 +00:00
self.onChangeState(state)
self.refreshData()
2021-06-13 09:49:21 +00:00
2018-09-06 10:17:03 +00:00
class ColorCountsSection(StatefulSection):
2018-10-06 08:27:36 +00:00
# TODO FEAT Blend colors when not expanded
# TODO FEAT Blend colors with importance of count
# TODO FEAT Allow icons instead of counts
2018-09-06 10:17:03 +00:00
NUMBER_STATES = 3
2021-06-13 09:49:21 +00:00
COLORABLE_ICON = "?"
2018-09-06 10:17:03 +00:00
def __init__(self, theme=None):
StatefulSection.__init__(self, theme=theme)
def fetcher(self):
counts = self.subfetcher()
# Nothing
if not len(counts):
return None
2018-09-06 10:17:03 +00:00
# Icon colored
elif self.state == 0 and len(counts) == 1:
count, color = counts[0]
return Text(self.COLORABLE_ICON, fg=color)
2018-09-06 10:17:03 +00:00
# Icon
elif self.state == 0 and len(counts) > 1:
return Text(self.COLORABLE_ICON)
2018-09-06 10:17:03 +00:00
# Icon + Total
elif self.state == 1 and len(counts) > 1:
total = sum([count for count, color in counts])
2021-06-13 09:49:21 +00:00
return Text(self.COLORABLE_ICON, " ", total)
2018-09-06 10:17:03 +00:00
# Icon + Counts
else:
text = Text(self.COLORABLE_ICON)
2018-09-06 10:17:03 +00:00
for count, color in counts:
2021-06-13 09:49:21 +00:00
text.append(" ", Text(count, fg=color))
2018-09-06 10:17:03 +00:00
return text
2018-09-06 05:38:22 +00:00
2018-08-21 19:50:44 +00:00
class Text:
def _setElements(self, elements):
# TODO OPTI Concatenate consecutrive string
self.elements = list(elements)
def _setDecorators(self, decorators):
# TODO OPTI Convert no decorator to strings
self.decorators = decorators
self.prefix = None
self.suffix = None
def __init__(self, *args, **kwargs):
self._setElements(args)
self._setDecorators(kwargs)
self.section = None
def append(self, *args):
self._setElements(self.elements + list(args))
def prepend(self, *args):
self._setElements(list(args) + self.elements)
def setElements(self, *args):
self._setElements(args)
def setDecorators(self, **kwargs):
self._setDecorators(kwargs)
def setSection(self, section):
assert isinstance(section, Section)
self.section = section
for element in self.elements:
if isinstance(element, Text):
element.setSection(section)
def _genFixs(self):
if self.prefix is not None and self.suffix is not None:
return
2021-06-13 09:49:21 +00:00
self.prefix = ""
self.suffix = ""
def nest(prefix, suffix):
2021-06-13 09:49:21 +00:00
self.prefix = self.prefix + "%{" + prefix + "}"
self.suffix = "%{" + suffix + "}" + self.suffix
def getColor(val):
# TODO Allow themes
assert isinstance(val, str) and len(val) == 7
return val
def button(number, function):
handle = Bar.getFunctionHandle(function)
2021-06-13 09:49:21 +00:00
nest("A" + number + ":" + handle.decode() + ":", "A" + number)
for key, val in self.decorators.items():
2018-10-06 08:27:36 +00:00
if val is None:
continue
2021-06-13 09:49:21 +00:00
if key == "fg":
reset = self.section.THEMES[self.section.theme][0]
2021-06-13 09:49:21 +00:00
nest("F" + getColor(val), "F" + reset)
elif key == "bg":
reset = self.section.THEMES[self.section.theme][1]
2021-06-13 09:49:21 +00:00
nest("B" + getColor(val), "B" + reset)
elif key == "clickLeft":
2021-06-13 09:49:21 +00:00
button("1", val)
elif key == "clickMiddle":
2021-06-13 09:49:21 +00:00
button("2", val)
elif key == "clickRight":
2021-06-13 09:49:21 +00:00
button("3", val)
elif key == "scrollUp":
2021-06-13 09:49:21 +00:00
button("4", val)
elif key == "scrollDown":
2021-06-13 09:49:21 +00:00
button("5", val)
else:
log.warn("Unkown decorator: {}".format(key))
def _text(self, size=None, pad=False):
self._genFixs()
curString = self.prefix
curSize = 0
remSize = size
for element in self.elements:
if element is None:
continue
elif isinstance(element, Text):
newString, newSize = element._text(size=remSize)
else:
newString = str(element)
if remSize is not None:
newString = newString[:remSize]
newSize = len(newString)
curString += newString
curSize += newSize
if remSize is not None:
remSize -= newSize
if remSize <= 0:
break
curString += self.suffix
if pad and remSize > 0:
2021-06-13 09:49:21 +00:00
curString += " " * remSize
curSize += remSize
if size is not None:
if pad:
assert size == curSize
else:
assert size >= curSize
return curString, curSize
def text(self, *args, **kwargs):
string, size = self._text(*args, **kwargs)
return string
def __str__(self):
2018-09-06 20:07:32 +00:00
self._genFixs()
curString = self.prefix
for element in self.elements:
if element is None:
continue
else:
curString += str(element)
curString += self.suffix
return curString
def __len__(self):
2018-09-06 20:07:32 +00:00
curSize = 0
for element in self.elements:
if element is None:
continue
elif isinstance(element, Text):
curSize += len(element)
else:
curSize += len(str(element))
return curSize
def __getitem__(self, index):
return self.elements[index]
def __setitem__(self, index, data):
self.elements[index] = data