Exemplo n.º 1
0
def main_page(scr: window):
    curses.curs_set(0)
    scr.noutrefresh()
    callbacks = {}

    def command_callback(command: str, **kwargs: Any):
        if command in callbacks:
            callbacks[command](**kwargs)

    setup_colours()
    max_rows, max_cols = scr.getmaxyx()
    views: dict[str, AbstractView] = {
        'header': HeaderView(max_cols),
        'footer': FooterView(max_rows, max_cols),
        'a2z': A2ZView(max_rows, command_callback),
        'tabs': TabView(max_cols, command_callback),
        'content': ContentView(max_rows, max_cols, command_callback)
    }
    keypress_table: dict[str, Callable[[int], None]] = {name: view.process_keystroke for name, view in views.items()}
    focus: list[str] = ['a2z']
    dispatch_to: list[Callable[[int], None]] = [keypress_table[focus[0]]]

    full_render_current_state(scr, views, focus[0])
    curses.doupdate()

    callbacks = setup_callbacks(views, focus, dispatch_to)
    command_callback('LOAD_PAGE', page='HOMEPAGE')

    keep_going = True
    while keep_going:
        update_render_current_state(scr, views)
        curses.doupdate()

        key: int = scr.getch()
        if key == ord('q'):
            keep_going = False
        elif key == ord('\t'):
            focus[0] = next_focus(focus[0])
            update_focuses(views, focus[0])
            dispatch_to[0] = keypress_table[focus[0]]
        elif key == curses.KEY_RESIZE:
            curses.resizeterm(*scr.getmaxyx())
            scr.erase()
            scr.noutrefresh()
            resize_all(focus[0], views, scr)
        else:
            dispatch_to[0](key)
Exemplo n.º 2
0
    def on_wnd(self, wnd: _curses.window, theme, y, x, scroll, template,
               **kwargs):
        """
        Render template on the window.

        TODO: Styles are a bit hacky.

        Args:
          y, x: Template start position
          scroll: skip this number of initial template lines (for scrolling)
          template: path to the template
          kwargs: template arguments.
        Returns:
          False: We hit the line limit during render (output cut)
          True: everything filled.
        """
        splitter = re.compile('(<theme=[_a-zA-Z0-9]+>|</theme>)')
        theme_parser = re.compile('^<theme=([_a-zA-Z0-9]+)>$')

        lines, cols = wnd.getmaxyx()
        self.max_cols = cols  # For wrap filter
        try:
            string = self.render_string(template, **kwargs)
        except jinja2.exceptions.TemplateError:
            log.exception(f"Template rendering {template} failed.")
            wnd.addstr(y, x, "*TEMPLATE RENDERING FAILED*")
            return True

        style = 0
        for line in string.split("\n"):
            line = line.rstrip()
            chunks = re.split(splitter, line)
            col = x
            for chunk in chunks:
                match = re.match(theme_parser, chunk)
                if match:
                    style_name = match.groups()[0]
                    style = getattr(theme, style_name)
                    continue
                if chunk == "</theme>":
                    style = 0
                    continue
                chunk = jinja2.Markup(chunk).unescape()
                try:
                    if scroll == 0:
                        wnd.addnstr(y, col, chunk, cols - col, style)
                    col += len(chunk)
                except _curses.error:
                    # Overflow is ok at the LAST character in LAST line.
                    # FIXME: Which makes it difficult to decide if we fit
                    return False

            if scroll > 0:
                scroll -= 1
            else:
                y += 1
            if y == lines:
                return False
        return True
Exemplo n.º 3
0
 def calculateCenter(self, texts: Union[str, list],
                     win: _curses.window) -> tuple:
     if isinstance(texts, str):
         texts = [texts]
     longestText = max(texts)
     ltexts = len(texts)
     ltxt = len(longestText)
     height, width = map(lambda x: (x // 2), win.getmaxyx())
     return height - (ltexts // 2), width - (
         ltxt // 2), longestText if ltexts == 1 else texts
Exemplo n.º 4
0
 def add_title(self,
               text: str,
               win: _curses.window,
               align: str = "center") -> None:
     if align == "alignleft":
         cw = 2
     elif align == "center":
         ch, cw, text = self.calculateCenter(text, win)
     elif align == "alignright":
         cw = win.getmaxyx()[1] - 2 - len(text)
     win.addstr(0, cw, f" {text.title()} ")
Exemplo n.º 5
0
    def redraw(self, wnd: _curses.window):
        "Refresh debug window"
        lines, cols = wnd.getmaxyx()
        wnd.erase()
        wnd.hline(0, 0, curses.ACS_HLINE, cols)

        self._logs = self._logs[-(lines-1):]
        line = 1
        for log in self._logs:
            wnd.addstr(line, 0, log[:cols-1])
            line += 1

        wnd.refresh()
Exemplo n.º 6
0
    def redraw(self, wnd: _curses.window):
        "Refresh the view display"
        lines, cols = wnd.getmaxyx()
        wnd.erase()

        line = 0
        wnd.addstr(line, 0, f"View screen ({self.ticks} ticks):")
        line += 1
        for i in range(0, curses.COLORS):
            wnd.addstr(line, i % cols, str(i % 10), curses.color_pair(i))
            if (i+1) % cols == 0:
                line += 1

        line += 1
        for i, message in enumerate(self.messages):
            wnd.addstr(i + line, 0, "Msg: " + message)
        wnd.refresh()
Exemplo n.º 7
0
    def drawCrossword(self, win: _curses.window, board: Optional[List] = None):
        mh, mw = map(lambda x: x - 4, win.getmaxyx())
        if not self.data:
            self.generateCrosswordBoard(mh, mw)
            self.data = None
        if self.data and (mh < self.data["height"] or mw < self.data["width"]):
            self.data = None

        if self.activeThread.get("gcb"):
            win.addstr(2, 2, f"generating Crossword {mh} x {mw}")
            win.refresh()
        elif self.data:
            ch, cw, board = self.calculateCenter(
                board or self.data["clueless"], win)
            for index, line in enumerate(board, start=ch):
                win.addstr(index, cw, line)

            for h, w, char in self.parseLoc.locaround:
                win.addstr(ch + h, cw + w, char, curses.color_pair(2))
Exemplo n.º 8
0
    def redraw(self, wnd: _curses.window):
        "Refresh the view display"
        lines, cols = wnd.getmaxyx()
        wnd.erase()
        msg = "Incremental search: " + self.query
        wnd.addstr(0, 0, msg)
        cursor_position = len(msg)

        self._update_search_state()

        msg = "{}/{}".format(len(self.results), len(self.all_issues))
        wnd.addstr(0, cols - len(msg), msg)

        if self.selected_idx >= len(self.results):
            self.selected_idx = max(0, len(self.results) - 1)

        line = 1
        max_summary = cols - 10 - 5
        for i, result in enumerate(self.results):
            if i == self.selected_idx:
                th_key = self.app.theme.ISSUE_KEY_SELECTED
                th_summary = self.app.theme.ISSUE_SUMMARY_SELECTED
            else:
                th_key = self.app.theme.ISSUE_KEY
                th_summary = self.app.theme.ISSUE_SUMMARY

            # TODO: Unified table generator
            j = result['fields']
            summary = j["summary"]
            if len(summary) > max_summary:
                summary = summary[:max_summary] + "…"
            summary += " " * (cols - len(summary) - 16)
            msg = f"{result['key']:10s} {summary}"
            wnd.addstr(line, 0, "{:15}".format(result['key']), th_key)
            wnd.addstr(line, 15, summary[:cols - 15 - 1], th_summary)
            line += 1
            if line == lines:
                break

        wnd.move(0, cursor_position)
Exemplo n.º 9
0
    def redraw(self, wnd: _curses.window):
        "Refresh status and keybindings display"
        # Update size on resize events
        self.lines_max, self.cols_max = wnd.getmaxyx()
        wnd.erase()
        wnd.hline(0, 0, curses.ACS_HLINE, self.cols_max)

        state = self.app.bindings.get_current()
        if state.render_callback is not None:
            state.render_callback()
        cmds = state.commands[:]

        col = self.margin_left

        while cmds:
            cmds, max_width = self._draw_column(wnd, col, cmds, state.disabled)
            col += max_width + self.margin_column

        hints = self.app.bindings.get_current().hints[:]
        while hints:
            hints, max_width = self._draw_hints(wnd, col, hints)
            col += max_width + self.margin_column

        wnd.refresh()
Exemplo n.º 10
0
def update_render_current_state(scr: window, views: dict[str, AbstractView]):
    for view in views.values():
        view.update_render(*scr.getmaxyx())
Exemplo n.º 11
0
def full_render_current_state(scr: window, views: dict[str, AbstractView], focus: str):
    for name, view in views.items():
        view.full_render(*scr.getmaxyx(), focus == name)
Exemplo n.º 12
0
def resize_all(focus: str,
               views: dict[str, AbstractView],
               scr: window):
    for name, view in views.items():
        view.on_resize(*scr.getmaxyx(), focus == name)