Exemplo n.º 1
0
Arquivo: views.py Projeto: pena-ara/tg
    def get_input(self, prefix: str = "") -> Optional[str]:
        curses.curs_set(1)
        buff = ""

        try:
            while True:
                self.win.erase()
                line = buff[-(self.w - 1):]
                self.win.addstr(0, 0, f"{prefix}{line}")

                key = self.win.get_wch(
                    0, min(string_len_dwc(buff + prefix), self.w - 1))
                key = ord(key)
                if key == 10:  # return
                    break
                elif key == 127:  # del
                    if buff:
                        buff = buff[:-1]
                elif key in (7, 27):  # (^G, <esc>) cancel
                    return None
                elif chr(key).isprintable():
                    buff += chr(key)
        finally:
            self.win.clear()
            curses.curs_set(0)
            curses.cbreak()
            curses.noecho()

        return buff
Exemplo n.º 2
0
Arquivo: views.py Projeto: pena-ara/tg
    def draw(self,
             current: int,
             chats: List[Dict[str, Any]],
             title: str = "Chats") -> None:
        self.win.erase()
        line = curses.ACS_VLINE  # type: ignore
        width = self.w - 1

        self.win.vline(0, width, line, self.h)
        self.win.addstr(0, 0,
                        title.center(width)[:width],
                        get_color(cyan, -1) | bold)

        for i, chat in enumerate(chats, 1):
            is_selected = i == current + 1
            date = get_date(chat)
            title = chat["title"]
            offset = 0

            last_msg_sender, last_msg = self._get_last_msg_data(chat)
            sender_label = f" {last_msg_sender}" if last_msg_sender else ""
            flags = self._get_flags(chat)
            flags_len = string_len_dwc(flags)

            if flags:
                self.win.addstr(
                    i,
                    max(0, width - flags_len),
                    truncate_to_len(flags, width)[-width:],
                    # flags[-width:],
                    self._unread_color(is_selected),
                )

            for attr, elem in zip(
                    self._chat_attributes(is_selected, title, last_msg_sender),
                [f"{date} ", title, sender_label, f" {last_msg}"],
            ):
                if not elem:
                    continue
                item = truncate_to_len(elem, max(0,
                                                 width - offset - flags_len))

                if len(item) > 1:
                    self.win.addstr(i, offset, item, attr)
                    offset += string_len_dwc(elem)

        self._refresh()
Exemplo n.º 3
0
Arquivo: views.py Projeto: chux0519/tg
    def draw(
        self,
        current_msg_idx: int,
        msgs: List[Tuple[int, Dict[str, Any]]],
        min_msg_padding: int,
        chat: Dict[str, Any],
    ) -> None:
        self.win.erase()
        msgs_to_draw = self._collect_msgs_to_draw(current_msg_idx, msgs,
                                                  min_msg_padding)

        if not msgs_to_draw:
            log.error("Can't collect message for drawing!")

        for elements, selected, line_num in msgs_to_draw:
            column = 0
            user = elements[1]
            for attr, elem in zip(self._msg_attributes(selected, user),
                                  elements):
                if not elem:
                    continue
                lines = (column + string_len_dwc(elem)) // self.w
                last_line = self.h == line_num + lines
                # work around agaist curses behaviour, when you cant write
                # char to the lower right coner of the window
                # see https://stackoverflow.com/questions/21594778/how-to-fill-to-lower-right-corner-in-python-curses/27517397#27517397
                if last_line:
                    start, stop = 0, self.w - column
                    for i in range(lines):
                        # insstr does not wraps long strings
                        self.win.insstr(
                            line_num + i,
                            column if not i else 0,
                            elem[start:stop],
                            attr,
                        )
                        start, stop = stop, stop + self.w
                else:
                    self.win.addstr(line_num, column, elem, attr)
                column += string_len_dwc(elem)

        self.win.addstr(0, 0, self._msg_title(chat),
                        get_color(cyan, -1) | bold)

        self._refresh()
Exemplo n.º 4
0
Arquivo: views.py Projeto: chux0519/tg
    def _collect_msgs_to_draw(
        self,
        current_msg_idx: int,
        msgs: List[Tuple[int, Dict[str, Any]]],
        min_msg_padding: int,
    ) -> List[Tuple[Tuple[str, ...], bool, int]]:
        """
        Tries to collect list of messages that will satisfy `min_msg_padding`
        theshold. Long messages could prevent other messages from displaying on
        the screen. In order to prevent scenario when *selected* message moved
        out from the visible area of the screen by some long messages, this
        function will remove message one by one from the start until selected
        message could be visible on the screen.
        """
        selected_item_idx: Optional[int] = None
        collected_items: List[Tuple[Tuple[str, ...], bool, int]] = []
        for ignore_before in range(len(msgs)):
            if selected_item_idx is not None:
                break
            collected_items = []
            line_num = self.h
            for msg_idx, msg_item in msgs[ignore_before:]:
                is_selected_msg = current_msg_idx == msg_idx
                msg_proxy = MsgProxy(msg_item)
                dt = msg_proxy.date.strftime("%H:%M:%S")
                user_id_item = msg_proxy.sender_id

                user_id = self.model.users.get_user_label(user_id_item)
                flags = self._get_flags(msg_proxy)
                if user_id and flags:
                    # if not channel add space between name and flags
                    flags = f" {flags}"
                label_elements = f" {dt} ", user_id, flags
                label_len = sum(string_len_dwc(e) for e in label_elements)

                msg = self._format_msg(msg_proxy,
                                       width_limit=self.w - label_len - 1)
                elements = *label_elements, f" {msg}"
                needed_lines = 0
                for i, msg_line in enumerate(msg.split("\n")):
                    # count wide character utf-8 symbols that take > 1 bytes to
                    # print it causes invalid offset
                    line_len = string_len_dwc(msg_line)

                    # first line cotains msg lable, e.g user name, date
                    if i == 0:
                        line_len += label_len

                    needed_lines += (line_len // self.w) + 1

                line_num -= needed_lines
                if line_num < 0:
                    tail_lines = needed_lines + line_num - 1
                    # try preview long message that did fit in the screen
                    if tail_lines > 0 and not is_selected_msg:
                        limit = self.w * tail_lines
                        tail_chatacters = len(msg) - limit - 3
                        elements = (
                            "",
                            "",
                            "",
                            f" ...{msg[tail_chatacters:]}",
                        )
                        collected_items.append((elements, is_selected_msg, 0))
                    break
                collected_items.append((elements, is_selected_msg, line_num))
                if is_selected_msg:
                    selected_item_idx = len(collected_items) - 1
            if (
                    # ignore first and last msg
                    selected_item_idx not in (0, len(msgs) - 1, None)
                    and selected_item_idx is not None
                    and len(collected_items) - 1 - selected_item_idx <
                    min_msg_padding):
                selected_item_idx = None

        return collected_items