def render(self, width, height) -> Screen: render_screen = Screen(width, height) self._render_headers(render_screen) for index, row in enumerate(self.rows): self._render_row(row, index + 2, render_screen) return render_screen
def get_rendered_screen(self, width, height): screen = Screen(width, height) screen_width = screen.width column_widths = self.get_column_widths(screen_width) column_headers = self.get_column_headers() header_line = "" under_line = "" for width, header in zip(column_widths, column_headers): header_line += header.center(width - 1)[: width - 1] + Border.NORMAL[Border.VERTICAL] under_line += Border.NORMAL[Border.HORIZONTAL] * (width - 1) + Border.NORMAL[Border.INTERSECTION] screen.draw((0, 0), header_line) screen.draw((0, 1), under_line) for row_index in range(height): line = "" for column_index, column_width in enumerate(column_widths): line += ( " " + self.columns[column_index].formatData(self.get_cell(column_index, row_index), column_width) + Border.NORMAL[Border.VERTICAL] ) line = line screen.draw(Point(0, row_index + 2), line) return screen
def render(self, width, height) -> Screen: render_screen = Screen(width, height) scroll_threshold = int(height / 2) sub_contents = self.contents if self.pointer < height - scroll_threshold: sub_contents = self.contents[:height] elif self.pointer > len(self.contents) - scroll_threshold: sub_contents = self.contents[-height:] else: sub_contents = self.contents[self.pointer - height + scroll_threshold : self.pointer + scroll_threshold] for index, item in enumerate(sub_contents): render_screen = self._render_item(Point(0, index), item, render_screen) if sub_contents[0] != self.contents[0]: render_screen.draw((-1, 0), unicodedata.lookup("UPWARDS ARROW")) if sub_contents[-1] != self.contents[-1]: render_screen.draw((-1, -1), unicodedata.lookup("DOWNWARDS ARROW")) return render_screen
class SilicaWindowManager: """ Main object that handles window manager state. """ DEFAULT_DELAY = 0.01 # Delay in seconds between full redraws def __init__(self): self._windows = {} self.viewport = Screen(0, 0) self._recalculate_screen_size() self.set_delay(self.DEFAULT_DELAY) def setup(self): """ Explicitly prepare the terminal for drawing to. This will mess up the terminal for normal operations, and will need to be reversed on exit by calling cleanup() """ self._curses_setup() def set_delay(self, new_delay): self.delay = new_delay # seconds def _curses_setup(self): self.scr = curses.initscr() curses.start_color() curses.use_default_colors() for i in range(0, curses.COLORS): curses.init_pair(i + 1, i, -1) curses.noecho() curses.cbreak() curses.curs_set(0) self.scr.keypad(True) self.scr.scrollok(False) self.scr.nodelay(True) @staticmethod def cleanup(): curses.echo() curses.nocbreak() curses.endwin() curses.curs_set(1) def _recalculate_screen_size(self): """ Updates viewport size to be the current terminal size and recalculates all windows. """ x, y = os.get_terminal_size() self.viewport.width = x self.viewport.height = y for window in self._windows.values(): window._generate_screen() def add_window(self, screenspace_rect, identifier) -> Window: return self._add_window( ScreenspaceRect(*screenspace_rect, self.viewport), identifier) def add_centered_window(self, centered_rect, identifier): return self._add_window(CenteredRect(*centered_rect, self.viewport), identifier) def _add_window(self, rect, identifier): if identifier in self._windows.keys(): raise ValueError("A window with this identifier already exists") new_window = Window(rect) new_window.identifier = identifier z_index = 0 for window in self._windows.values(): z_index = max(window.z_index, z_index + 1) new_window.z_index = z_index self._windows[identifier] = new_window return new_window def get_window(self, identifier): try: return self._windows[identifier] except KeyError: raise KeyError("No window with this identifier exists") def get_keypress(self): return KeyPress(self.scr.getch()) def process(self): self.draw_windows() self.render() self.scr.refresh() self._recalculate_screen_size() time.sleep(self.delay) def draw_windows(self): for window in sorted(self._windows.values(), key=operator.attrgetter("z_index")): if not window.visible: continue rendered_window = window.render() self.viewport.draw(window.rect.origin, rendered_window) window.screen.clear() def safe_error(self, e): self.cleanup() raise e def render(self): for y_index, line in enumerate(self.viewport.as_list()): try: if y_index == self.viewport.size.height - 1: self.scr.insnstr(y_index, self.viewport.size.width - 1, line[-1].character, 0) line = line[:-1] for x_index, screencell in enumerate(line): self.scr.addstr(y_index, x_index, screencell.character, screencell.attributes.to_curses()) except curses.error as e: pass
def __init__(self): self._windows = {} self.viewport = Screen(0, 0) self._recalculate_screen_size() self.set_delay(self.DEFAULT_DELAY)
class Window: def __init__(self, rect, z_index=0): self.visible = True self.title = "" self.rect = rect self._generate_screen() self.theme = Border.NORMAL self.identifier = None self.z_index = z_index @property def width(self): return self.rect.width @property def height(self): return self.rect.height def _generate_screen(self): self.screen = Screen(self.rect.width - 2, self.rect.height - 2) def draw(self, *args, **kwargs): self.screen.draw(*args, **kwargs) def render(self) -> Screen: render_screen = Screen(self.rect.width, self.rect.height) top_edge = (self.theme[Border.TOPLEFT] + self.theme[Border.HORIZONTAL] * self.screen.width + self.theme[Border.TOPRIGHT]) if self.title != "": top_edge = (self.theme[Border.TOPLEFT] + " {title} ".format(title=self.title).center( self.screen.width, self.theme[Border.HORIZONTAL]) + self.theme[Border.TOPRIGHT]) bottom_edge = (self.theme[Border.BOTTOMLEFT] + self.theme[Border.HORIZONTAL] * self.screen.width + self.theme[Border.BOTTOMRIGHT]) render_screen.draw((0, 0), top_edge) for y in range(self.screen.height): render_screen.draw((0, y + 1), self.theme[Border.VERTICAL]) render_screen.draw(Point(self.screen.width + 1, y + 1), self.theme[Border.VERTICAL]) render_screen.draw((0, self.rect.height - 1), bottom_edge) render_screen.draw(Point(1, 1), self.screen) return render_screen def auto_title(self): try: self.title = str(self.identifier) return self except ValueError: raise TypeError("Window identifier must be a valid string") def set_title(self, new_title): self.title = new_title return self def set_theme(self, theme_name): self.theme = Border.get_theme(theme_name) return self
def render(self) -> Screen: render_screen = Screen(self.rect.width, self.rect.height) top_edge = (self.theme[Border.TOPLEFT] + self.theme[Border.HORIZONTAL] * self.screen.width + self.theme[Border.TOPRIGHT]) if self.title != "": top_edge = (self.theme[Border.TOPLEFT] + " {title} ".format(title=self.title).center( self.screen.width, self.theme[Border.HORIZONTAL]) + self.theme[Border.TOPRIGHT]) bottom_edge = (self.theme[Border.BOTTOMLEFT] + self.theme[Border.HORIZONTAL] * self.screen.width + self.theme[Border.BOTTOMRIGHT]) render_screen.draw((0, 0), top_edge) for y in range(self.screen.height): render_screen.draw((0, y + 1), self.theme[Border.VERTICAL]) render_screen.draw(Point(self.screen.width + 1, y + 1), self.theme[Border.VERTICAL]) render_screen.draw((0, self.rect.height - 1), bottom_edge) render_screen.draw(Point(1, 1), self.screen) return render_screen
def _generate_screen(self): self.screen = Screen(self.rect.width - 2, self.rect.height - 2)