You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/prompt_toolkit/layout/screen.py

290 lines
8.7 KiB

from __future__ import unicode_literals
from prompt_toolkit.cache import FastDictCache
from prompt_toolkit.utils import get_cwidth
from collections import defaultdict, namedtuple
__all__ = [
'Point',
'Size',
'Screen',
'Char',
]
Point = namedtuple('Point', 'x y')
Size = namedtuple('Size', 'rows columns')
class Char(object):
"""
Represent a single character in a :class:`.Screen`.
This should be considered immutable.
:param char: A single character (can be a double-width character).
:param style: A style string. (Can contain classnames.)
"""
__slots__ = ('char', 'style', 'width')
# If we end up having one of these special control sequences in the input string,
# we should display them as follows:
# Usually this happens after a "quoted insert".
display_mappings = {
'\x00': '^@', # Control space
'\x01': '^A',
'\x02': '^B',
'\x03': '^C',
'\x04': '^D',
'\x05': '^E',
'\x06': '^F',
'\x07': '^G',
'\x08': '^H',
'\x09': '^I',
'\x0a': '^J',
'\x0b': '^K',
'\x0c': '^L',
'\x0d': '^M',
'\x0e': '^N',
'\x0f': '^O',
'\x10': '^P',
'\x11': '^Q',
'\x12': '^R',
'\x13': '^S',
'\x14': '^T',
'\x15': '^U',
'\x16': '^V',
'\x17': '^W',
'\x18': '^X',
'\x19': '^Y',
'\x1a': '^Z',
'\x1b': '^[', # Escape
'\x1c': '^\\',
'\x1d': '^]',
'\x1f': '^_',
'\x7f': '^?', # ASCII Delete (backspace).
# Special characters. All visualized like Vim does.
'\x80': '<80>',
'\x81': '<81>',
'\x82': '<82>',
'\x83': '<83>',
'\x84': '<84>',
'\x85': '<85>',
'\x86': '<86>',
'\x87': '<87>',
'\x88': '<88>',
'\x89': '<89>',
'\x8a': '<8a>',
'\x8b': '<8b>',
'\x8c': '<8c>',
'\x8d': '<8d>',
'\x8e': '<8e>',
'\x8f': '<8f>',
'\x90': '<90>',
'\x91': '<91>',
'\x92': '<92>',
'\x93': '<93>',
'\x94': '<94>',
'\x95': '<95>',
'\x96': '<96>',
'\x97': '<97>',
'\x98': '<98>',
'\x99': '<99>',
'\x9a': '<9a>',
'\x9b': '<9b>',
'\x9c': '<9c>',
'\x9d': '<9d>',
'\x9e': '<9e>',
'\x9f': '<9f>',
# For the non-breaking space: visualize like Emacs does by default.
# (Print a space, but attach the 'nbsp' class that applies the
# underline style.)
'\xa0': ' ',
}
def __init__(self, char=' ', style=''):
# If this character has to be displayed otherwise, take that one.
if char in self.display_mappings:
if char == '\xa0':
style += ' class:nbsp ' # Will be underlined.
else:
style += ' class:control-character '
char = self.display_mappings[char]
self.char = char
self.style = style
# Calculate width. (We always need this, so better to store it directly
# as a member for performance.)
self.width = get_cwidth(char)
def __eq__(self, other):
return self.char == other.char and self.style == other.style
def __ne__(self, other):
# Not equal: We don't do `not char.__eq__` here, because of the
# performance of calling yet another function.
return self.char != other.char or self.style != other.style
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.char, self.style)
_CHAR_CACHE = FastDictCache(Char, size=1000 * 1000)
Transparent = '[transparent]'
class Screen(object):
"""
Two dimensional buffer of :class:`.Char` instances.
"""
def __init__(self, default_char=None, initial_width=0, initial_height=0):
if default_char is None:
default_char = _CHAR_CACHE[' ', Transparent]
self.data_buffer = defaultdict(lambda: defaultdict(lambda: default_char))
#: Escape sequences to be injected.
self.zero_width_escapes = defaultdict(lambda: defaultdict(lambda: ''))
#: Position of the cursor.
self.cursor_positions = {} # Map `Window` objects to `Point` objects.
#: Visibility of the cursor.
self.show_cursor = True
#: (Optional) Where to position the menu. E.g. at the start of a completion.
#: (We can't use the cursor position, because we don't want the
#: completion menu to change its position when we browse through all the
#: completions.)
self.menu_positions = {} # Map `Window` objects to `Point` objects.
#: Currently used width/height of the screen. This will increase when
#: data is written to the screen.
self.width = initial_width or 0
self.height = initial_height or 0
# Windows that have been drawn. (Each `Window` class will add itself to
# this list.)
self.visible_windows = []
self._draw_float_functions = [] # List of (z_index, draw_func)
def set_cursor_position(self, window, position):
" Set the cursor position for a given window. "
self.cursor_positions[window] = position
def set_menu_position(self, window, position):
" Set the cursor position for a given window. "
self.menu_positions[window] = position
def get_cursor_position(self, window):
"""
Get the cursor position for a given window.
Returns a `Point`.
"""
try:
return self.cursor_positions[window]
except KeyError:
return Point(x=0, y=0)
def get_menu_position(self, window):
"""
Get the menu position for a given window.
(This falls back to the cursor position if no menu position was set.)
"""
try:
return self.menu_positions[window]
except KeyError:
try:
return self.cursor_positions[window]
except KeyError:
return Point(x=0, y=0)
def draw_with_z_index(self, z_index, draw_func):
"""
Add a draw-function for a `Window` which has a >= 0 z_index.
This will be postponed until `draw_all_floats` is called.
"""
assert isinstance(z_index, int), z_index
assert callable(draw_func)
self._draw_float_functions.append((z_index, draw_func))
def draw_all_floats(self):
"""
Draw all float functions in order of z-index.
"""
# We keep looping because some draw functions could add new functions
# to this list. See `FloatContainer`.
while self._draw_float_functions:
# Sort the floats that we have so far by z_index.
functions = sorted(self._draw_float_functions, key=lambda item: item[0])
# Draw only one at a time, then sort everything again. Now floats
# might have been added.
self._draw_float_functions = functions[1:]
functions[0][1]()
def append_style_to_content(self, style_str):
"""
For all the characters in the screen.
Set the style string to the given `style_str`.
"""
b = self.data_buffer
char_cache = _CHAR_CACHE
append_style = ' ' + style_str
for y, row in b.items():
for x, char in row.items():
b[y][x] = char_cache[char.char, char.style + append_style]
def fill_area(self, write_position, style='', after=False):
"""
Fill the content of this area, using the given `style`.
The style is prepended before whatever was here before.
"""
if not style.strip():
return
xmin = write_position.xpos
xmax = write_position.xpos + write_position.width
char_cache = _CHAR_CACHE
data_buffer = self.data_buffer
if after:
append_style = ' ' + style
prepend_style = ''
else:
append_style = ''
prepend_style = style + ' '
for y in range(write_position.ypos, write_position.ypos + write_position.height):
row = data_buffer[y]
for x in range(xmin, xmax):
cell = row[x]
row[x] = char_cache[cell.char, prepend_style + cell.style + append_style]
class WritePosition(object):
def __init__(self, xpos, ypos, width, height):
assert height >= 0
assert width >= 0
# xpos and ypos can be negative. (A float can be partially visible.)
self.xpos = xpos
self.ypos = ypos
self.width = width
self.height = height
def __repr__(self):
return '%s(x=%r, y=%r, width=%r, height=%r)' % (
self.__class__.__name__,
self.xpos, self.ypos, self.width, self.height)