2025-02-04 13:16:23 +03:00
|
|
|
|
import curses
|
|
|
|
|
|
|
|
|
|
class CursesAdapter:
|
|
|
|
|
def __init__(self) -> None:
|
2025-02-04 14:46:01 +03:00
|
|
|
|
self.KEY_LEFT = curses.KEY_LEFT
|
|
|
|
|
self.KEY_RIGHT = curses.KEY_RIGHT
|
|
|
|
|
self.KEY_UP = curses.KEY_UP
|
|
|
|
|
self.KEY_DOWN = curses.KEY_DOWN
|
2025-02-04 14:59:23 +03:00
|
|
|
|
self.KEY_BACKSPACE_1 = 127
|
|
|
|
|
self.KEY_BACKSPACE_2 = 8
|
2025-02-04 14:46:01 +03:00
|
|
|
|
self.KEY_ENTER = 10
|
|
|
|
|
self.KEY_CTRL_S = 19
|
|
|
|
|
self.KEY_ESCAPE = 27
|
2025-02-04 18:36:12 +03:00
|
|
|
|
self.KEY_TWO_DOTS = 59
|
2025-02-04 14:46:01 +03:00
|
|
|
|
|
|
|
|
|
|
2025-02-04 13:16:23 +03:00
|
|
|
|
self.screen = curses.initscr()
|
|
|
|
|
self.screen.keypad(True)
|
|
|
|
|
self.cols = curses.COLS
|
|
|
|
|
self.lines = curses.LINES
|
|
|
|
|
# init color system
|
|
|
|
|
curses.start_color()
|
|
|
|
|
# green color pair
|
|
|
|
|
curses.init_pair(1, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
|
|
|
|
curses.curs_set(1) # Make cursor visible
|
|
|
|
|
|
|
|
|
|
def Refresh(self) -> None:
|
|
|
|
|
"""Apply changes"""
|
|
|
|
|
self.screen.refresh()
|
|
|
|
|
|
|
|
|
|
def Cleanup(self) -> None:
|
|
|
|
|
curses.endwin()
|
|
|
|
|
|
|
|
|
|
def SetCursor(self, x: int, y: int):
|
|
|
|
|
"""set cursor position (x, y)"""
|
|
|
|
|
self.screen.move(x, y)
|
|
|
|
|
|
|
|
|
|
def SetChar(self, x: int, y: int, code: int):
|
|
|
|
|
"""set char position (x, y)"""
|
|
|
|
|
self.screen.addch(x, y, code)
|
|
|
|
|
|
|
|
|
|
def SetString(self, x: int, y: int, data: str):
|
|
|
|
|
"""set string begin position (x, y)"""
|
|
|
|
|
self.screen.addstr(x, y, data)
|
|
|
|
|
|
|
|
|
|
def GetChar(self) -> int:
|
|
|
|
|
"""Wait users input"""
|
2025-02-04 20:01:25 +03:00
|
|
|
|
return self.screen.getch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VimView:
|
2025-02-04 20:28:41 +03:00
|
|
|
|
def __init__(self) -> None:
|
|
|
|
|
self.curses_adapter = CursesAdapter()
|
2025-02-04 20:01:25 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def Render(self,
|
|
|
|
|
displayBuffer: list[list[str]],
|
|
|
|
|
currentLine: int, currentCol: int,
|
|
|
|
|
scrollX: int, scrollY: int,
|
|
|
|
|
modeBarData: str):
|
|
|
|
|
"""Отрисовка текущего состояния"""
|
|
|
|
|
|
|
|
|
|
self.curses_adapter.screen.clear()
|
|
|
|
|
|
|
|
|
|
# Отображение видимой части текста
|
|
|
|
|
for i in range(self.curses_adapter.lines - 1):
|
|
|
|
|
if i + scrollY < len(displayBuffer):
|
|
|
|
|
line = ''.join(displayBuffer[i + scrollY])
|
|
|
|
|
if scrollX < len(line):
|
|
|
|
|
self.curses_adapter.SetString(i, 0, line[scrollX:scrollX + self.curses_adapter.cols])
|
|
|
|
|
else:
|
|
|
|
|
self.curses_adapter.SetString(i, 0, '')
|
|
|
|
|
else:
|
|
|
|
|
self.curses_adapter.SetString(i, 0, '')
|
|
|
|
|
|
|
|
|
|
# Обновление панели режима
|
|
|
|
|
self.SetModeBar(modeBarData)
|
|
|
|
|
|
|
|
|
|
# Установка курсора с учетом прокрутки
|
|
|
|
|
cursor_x = currentLine - scrollY
|
|
|
|
|
cursor_y = currentCol - scrollX
|
|
|
|
|
|
|
|
|
|
# Проверка, чтобы курсор не вышел за пределы экрана
|
|
|
|
|
if 0 <= cursor_x < self.curses_adapter.lines - 1 and 0 <= cursor_y < self.curses_adapter.cols:
|
|
|
|
|
self.curses_adapter.SetCursor(cursor_x, cursor_y)
|
|
|
|
|
|
|
|
|
|
self.curses_adapter.Refresh()
|
|
|
|
|
|
|
|
|
|
def SetModeBar(self, modeBarData: str):
|
|
|
|
|
"""Print edit mode information panel"""
|
|
|
|
|
self.curses_adapter.SetString(self.curses_adapter.lines - 1, 0, modeBarData)
|