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
def redraw(self, wnd: _curses.window): "Refresh the view display" wnd.clear() self.fits = self.app.renderer.on_wnd(wnd, self.app.theme, 0, 0, self.scroll, "issue_view.j2", key=self.issue.key, issue=self.issue.fields)
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()} ")
def __init__(self, stdscr: window, users: UserModel, p: float = 0.5): self.stdscr = stdscr self.h = 0 self.w = 0 self.win = stdscr.subwin(self.h, self.w, 0, 0) self._refresh = self.win.refresh self.users = users
def __init__(self, stdscr: window, model: Model) -> None: self.stdscr = stdscr self.h = 0 self.w = 0 self.win = stdscr.subwin(self.h, self.w, 0, 0) self._refresh = self.win.refresh self.model = model
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()
def __init__(self, stdscr: window) -> None: self.h = 1 self.w = curses.COLS self.y = curses.LINES - 1 self.x = 0 self.stdscr = stdscr self.win = stdscr.subwin(self.h, self.w, self.y, self.x) self._refresh = self.win.refresh
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
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)
def draw_boxes(stdscr: window): """ This method draws the boxes on startup """ h = curses.LINES #Total height w = curses.COLS #Total width #Some debug values, remove these later text = "This is the start of a new window" # Clear screen stdscr.clear() stdscr.addstr(0, 0, 'This is the start of a new window') stdscr.noutrefresh() theight = 2 swidth = 15 mheight = h - theight - 2 mwidth = w - swidth - 2 win['t'] = curses.newwin(theight, mwidth, h - 1 - theight, 1) win['t'].addstr(0, 0, "This is the messages windows") win['m'] = curses.newwin(mheight, mwidth, 1, 1) win['m'].addstr(0, 0, "Type something here.") win['m'].timeout(0) win['s'] = curses.newwin(h - 2, swidth, 1, w - swidth - 1) win['s'].addstr(0, 0, "This is a side pane")
def __init__( self, stdscr: window, chat_view: "ChatView", msg_view: "MsgView", status_view: "StatusView", ) -> None: curses.noecho() curses.cbreak() stdscr.keypad(True) curses.curs_set(0) curses.start_color() curses.use_default_colors() # init white color first to initialize colors correctly get_color(white, -1) self.stdscr = stdscr self.chats = chat_view self.msgs = msg_view self.status = status_view self.max_read = 2048
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))
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()
def redraw(self, wnd: _curses.window): "Refresh the view display" wnd.clear() wnd.addstr(0, 0, "Dashboard", self.app.theme.TITLE) if self.update_in_progress: wnd.addstr(2, 0, "Updating database")
def redraw(self, wnd: _curses.window): "Refresh the view display" wnd.erase() wnd.addstr(0, 0, "Secondary view.")
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()
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)
def resize_all(focus: str, views: dict[str, AbstractView], scr: window): for name, view in views.items(): view.on_resize(*scr.getmaxyx(), focus == name)
def update_render_current_state(scr: window, views: dict[str, AbstractView]): for view in views.values(): view.update_render(*scr.getmaxyx())
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)