InitConnWindowEvent, InitReconnectEvent, IrcMessage, LoginNames,
LogConnEvent, NickSetEvent, SendEvent, TIMEOUT_LOOP)
+_MIN_HEIGHT = 4
+_MIN_WIDTH = 32
_B64_PREFIX = 'b64:'
_OSC52_PREFIX = ']52;c;'
@abstractmethod
def __init__(self, *args, **kwargs) -> None:
self.tainted = True
+ self._drawable = False
@abstractmethod
- def set_geometry(self, measurements: _YX) -> None:
+ def set_geometry(self, measurements: _YX) -> bool:
'Update widget\'s measurements, re-generate content where necessary.'
self.tainted = True
+ self._drawable = len([m for m in measurements if m < 0]) == 0
+ return self._drawable
@abstractmethod
- def draw(self) -> None:
+ def draw(self) -> bool:
'Print widget\'s content in shape appropriate to set geometry.'
+ if not self._drawable:
+ return False
self.tainted = False
+ return True
class _ScrollableWidget(_Widget, ABC):
self._wrapped += [(idx_original, line) for line in wrapped_lines]
return len(wrapped_lines)
- def set_geometry(self, measurements: _YX) -> None:
- super().set_geometry(measurements)
+ def set_geometry(self, measurements: _YX) -> bool:
+ if not super().set_geometry(measurements):
+ return False
self._view_size = measurements
self._y_pgscroll = self._view_size.y // 2
self._wrapped.clear()
self._wrapped += [(None, '')] * self._view_size.y
if not self._history:
- return
+ return True
for idx_history, line in enumerate(self._history):
self._add_wrapped(idx_history, line)
wrapped_lines_for_history_idx = [
if t[0] == len(self._history) + self._history_idx]
idx_their_last = self._wrapped.index(wrapped_lines_for_history_idx[-1])
self._wrapped_idx = idx_their_last - len(self._wrapped)
+ return True
def append(self, to_append: str) -> None:
super().append(to_append)
+ self.tainted = True
+ if self._history_idx < -1:
+ self._history_idx -= 1
+ if not self._drawable:
+ return
n_wrapped_lines = self._add_wrapped(len(self._history) - 1, to_append)
if self._wrapped_idx < -1:
- self._history_idx -= 1
self._wrapped_idx -= n_wrapped_lines
- self.tainted = True
- def draw(self) -> None:
- super().draw()
+ def draw(self) -> bool:
+ if not super().draw():
+ return False
start_idx = self._wrapped_idx - self._view_size.y + 1
end_idx = self._wrapped_idx
to_write = [t[1] for t in self._wrapped[start_idx:end_idx]]
to_write += [self._wrapped[self._wrapped_idx][1]]
for i, line in enumerate(to_write):
self._write(line, i)
+ return True
def _scroll(self, up: bool = True) -> None:
super()._scroll(up)
+ if not self._drawable:
+ return
if up:
self._wrapped_idx = max(self._view_size.y + 1 - len(self._wrapped),
self._wrapped_idx - self._y_pgscroll)
self.tainted = True
self._input_buffer_unsafe = content
- def set_geometry(self, measurements: _YX) -> None:
- super().set_geometry(measurements)
+ def set_geometry(self, measurements: _YX) -> bool:
+ if not super().set_geometry(measurements):
+ return False
self._y, self._width = measurements
+ return True
- def draw(self) -> None:
- super().draw()
+ def draw(self) -> bool:
+ if not super().draw():
+ return False
prefix = self._prompt[:]
content = self._input_buffer
if self._cursor_x == len(self._input_buffer):
self._write(to_write[cursor_x_to_write], attribute='reverse',
padding=False)
self._write(to_write[cursor_x_to_write + 1:])
+ return True
def _archive_prompt(self) -> None:
self.append(self._input_buffer)
if hasattr(self._term, 'size'):
self.set_geometry()
- def set_geometry(self, _=None) -> None:
- super().set_geometry(_)
+ def set_geometry(self, _=None) -> bool:
assert _ is None
+ if self._term.size.y < _MIN_HEIGHT or self._term.size.x < _MIN_WIDTH:
+ bad_yx = _YX(-1, -1)
+ super().set_geometry(bad_yx)
+ self.log.set_geometry(bad_yx)
+ self.prompt.set_geometry(bad_yx)
+ return False
+ super().set_geometry(_YX(0, 0))
self._y_status = self._term.size.y - 2
self.log.set_geometry(_YX(self._y_status, self._term.size.x))
self.prompt.set_geometry(_YX(self._term.size.y - 1, self._term.size.x))
+ return True
- def draw(self) -> None:
- super().draw()
+ def draw(self) -> bool:
+ self._term.clear()
+ if not super().draw():
+ if self._term.size.x > 0:
+ lines = ['']
+ for i, c in enumerate('screen too small'):
+ if i > 0 and 0 == i % self._term.size.x:
+ lines += ['']
+ lines[-1] += c
+ for y, line in enumerate(lines):
+ self._term.write(line, y)
+ return False
idx_box = f'[{self.idx}]'
status_line = idx_box + '=' * (self._term.size.x - len(idx_box))
- self._term.clear()
self.log.draw()
self._term.write(status_line, self._y_status)
self.prompt.draw()
+ return True
def cmd__paste(self) -> None:
'Write OSC 52 ? sequence to get encoded clipboard paste into stdin.'
padding: bool = True
) -> None:
'Print to terminal, with position, padding to line end, attributes.'
- if start_y:
+ if start_y is not None:
self._cursor_yx = _YX(start_y, 0)
# ._blessed.length can slow down things notably: only use where needed!
end_x = self._cursor_yx.x + (len(msg) if msg.isascii()