def update(self): """Commit the changes in the screen buffer to the backend (terminal). Determines which pixels (characters) have been modified from their previous value, and re-draws those pixels using the backend. It should be noted that the pixel attributes are compared to their previous values. This means that if you, for example, clear a black screen to white, and then clear it back to black, no pixels will be re-rendered. This means that it is reasonable to clear and re-render the entire screen whenever you make an update, if it seems too challenging to manually make only the necessary changes. """ t0 = perf_counter() self._update_count = 0 for y in range(self.h): for x in range(self.w): pixel = self.at_unsafe(x, y) if pixel != self._pixels_cache[x][y]: self._pixels_cache[x][y].set(pixel) self._update_count += 1 # don't render a pixel shadowed by a fullwidth character if x > 0 and terminal_char_len( self.at_unsafe(x - 1, y).char) > 1: continue self.render(pixel, x, y) self.backend.cursor_pos = self.cursor_pos self.backend.flush() self._update_duration = perf_counter() - t0
def put_char(self, ch, x, y, *, fg=None, bg=None): """Put a single character and/or colors at a particular location. Puts a single character at the given x, y coordinates, only if those coordinates are within the bounds of the buffer. Coordinates are also considered out of bounds if the character has a width greater than 1 and it would not be entirely within the bounds of the buffer. If the given coordinates are out of bounds, this method has no effect. If fg or bg colors are specified, they replace the fg or bg colors at the given coordinates. If None is given for either argument, the existing colors are left unchanged. Returns the width of the character that was provided, even if it was out of bounds. """ ch_len = terminal_char_len(ch) if x >= 0 and y >= 0 and x + ch_len <= self.w and y < self.h: pixel = self.at_unsafe(x, y) pixel.char = ch if fg: pixel.fg = fg if bg: pixel.bg = bg # handle fullwidth (double-wide) characters. # clear character and set fg and bg so that values make sense if # the fullwidth character is later removed. if ch_len > 1: assert ch_len == 2 shadowed = self.at_unsafe(x + 1, y) shadowed.char = " " if fg: shadowed.fg = fg if bg: shadowed.bg = bg return ch_len
def test_terminal_len(): test_string = "你好 - Hello" assert terminal_len(test_string) == sum( terminal_char_len(i) for i in test_string)
def test_terminal_char_len_ambiguous(): set_ambiguous_is_wide(False) assert terminal_char_len(ambiguous_char) == 1 set_ambiguous_is_wide(True) assert terminal_char_len(ambiguous_char) == 2
def test_terminal_char_len_tab(): # no way to tell tab width without context assert terminal_char_len("\t") == None
def test_terminal_char_len_control(): assert terminal_char_len(control_char) == 0
def test_terminal_char_len_wide(): assert terminal_char_len(wide_char) == 2
def test_terminal_char_len_narrow(): assert terminal_char_len(narrow_char) == 1