diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c9c27..fba24fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Next version +- Add nick list - Rename project from "cheuph" to "bowl" - Clean up code diff --git a/bowl/euphoria/__init__.py b/bowl/euphoria/__init__.py index 4be4986..9e147ba 100644 --- a/bowl/euphoria/__init__.py +++ b/bowl/euphoria/__init__.py @@ -4,6 +4,7 @@ from .edit_widgets import * from .euph_config import * from .euph_renderer import * from .launch_application import * +from .nick_list_widget import * from .room_widget import * from .single_room_application import * @@ -13,5 +14,6 @@ __all__ += edit_widgets.__all__ __all__ += euph_config.__all__ __all__ += euph_renderer.__all__ __all__ += launch_application.__all__ +__all__ += nick_list_widget.__all__ __all__ += room_widget.__all__ __all__ += single_room_application.__all__ diff --git a/bowl/euphoria/nick_list_widget.py b/bowl/euphoria/nick_list_widget.py new file mode 100644 index 0000000..99b3b17 --- /dev/null +++ b/bowl/euphoria/nick_list_widget.py @@ -0,0 +1,148 @@ +from typing import List, Optional, Tuple + +import urwid +import yaboli + +from ..attributed_lines import AttributedLines +from ..attributed_lines_widget import AttributedLinesWidget +from ..markup import AT, Attributes + +__all__ = ["NickListWidget"] + +class NickListWidget(urwid.WidgetWrap): + """ + This widget displays the nicks of the users currently connected to a Room. + + It must be notified of changes in the user list by the RoomWidget it is a + part of. + """ + + def __init__(self, + session: Optional[yaboli.Session] = None, + users: Optional[yaboli.LiveSessionListing] = None, + heading_attrs: Attributes = {}, + counter_attrs: Attributes = {}, + nick_attrs: Attributes = {}, + own_nick_attrs: Attributes = {}, + ) -> None: + + self._session = session + self._users = users + + self._heading_attrs = heading_attrs + self._counter_attrs = counter_attrs + self._nick_attrs = nick_attrs + self._own_nick_attrs = own_nick_attrs + + self._offset = 0 + self._lines = AttributedLinesWidget() + + super().__init__(self._lines) + + self.rerender() + + @property + def session(self) -> Optional[yaboli.Session]: + return self._session + + @session.setter + def session(self, session: Optional[yaboli.Session]) -> None: + self._session = session + self.rerender() + + @property + def users(self) -> Optional[yaboli.LiveSessionListing]: + return self._users + + @users.setter + def users(self, users: Optional[yaboli.LiveSessionListing]) -> None: + self._users = users + self.rerender() + + def _sort_users(self) -> Tuple[List[yaboli.Session], List[yaboli.Session], List[yaboli.Session], List[yaboli.Session]]: + people = [] + bots = [] + lurkers = [] + nurkers = [] + + users = [] if self._users is None else self._users.all + + if self.session is not None: + users.append(self.session) + + for user in users: + if user.nick: + if user.is_bot: + bots.append(user) + else: + people.append(user) + else: + if user.is_bot: + nurkers.append(user) + else: + lurkers.append(user) + + return people, bots, lurkers, nurkers + + def _render_section(self, + name: str, + sessions: List[yaboli.Session], + ) -> AttributedLines: + + lines = AttributedLines() + + sessions.sort(key=lambda sess: sess.nick) + count = len(sessions) + + title = AT(name, attributes=self._heading_attrs) + title += AT(f" ({count})", attributes=self._counter_attrs) + lines.append_below({}, title) + + for sess in sessions: + if not sess.nick: + continue + + if sess is self._session: + attributes = self._own_nick_attrs + else: + attributes = self._nick_attrs + + lines.append_below({}, AT(sess.nick, attributes=attributes)) + + return lines + + def rerender(self) -> None: + lines = AttributedLines() + people, bots, lurkers, nurkers = self._sort_users() + + sections = [] + + if people: + sections.append(self._render_section("People", people)) + if bots: + sections.append(self._render_section("Bots", bots)) + if lurkers: + sections.append(self._render_section("Lurkers", lurkers)) + if nurkers: + sections.append(self._render_section("Nurkers", nurkers)) + + lines = AttributedLines() + lines.upper_offset = self._lines.upper_offset + + if len(sections) < 1: + lines.extend_below(self._render_section("Nobody", [])) + else: + lines.extend_below(sections[0]) + for section in sections[1:]: + lines.append_below({}, AT()) + lines.extend_below(section) + + self._lines.set_lines(lines) + + def scroll(self, delta: int) -> None: + self._lines.upper_offset += delta + + self._lines.lower_offset = max(1, self._lines.lower_offset) + self._lines.upper_offset = min(0, self._lines.upper_offset) + + self._invalidate()