Asciifarm/ratuil/bufferedscreen.py

208 lines
5.6 KiB
Python

import sys
import io
from .constants import INT_INFINITY
from .screen import Screen
from .pad import Pad
from .drawtarget import DrawTarget
from .strwidth import charwidth
class BufferedScreen(DrawTarget):
def __init__(self, out=sys.stdout, *args, **kwargs):
self.screen = RememberingScreen(out, *args, **kwargs)
self.buff = Pad(self.screen.width, self.screen.height)
@property
def width(self):
return self.screen.width
@property
def height(self):
return self.screen.height
def clear(self):
self.screen.clear()
self.buff = Pad(self.screen.width, self.screen.height)
def reset(self):
self.screen.reset()
self.clear()
def update(self):
self.screen.draw_pad(self.buff)
self.screen.update()
def write(self, x, y, text, style=None):
self.buff.write(x, y, text, style)
def draw_pad(self, pad, dest_x=0, dest_y=0, width=INT_INFINITY, height=INT_INFINITY, src_x=0, src_y=0):
self.buff.draw_pad(pad, dest_x, dest_y, width, height, src_x, src_y)
class RememberingScreen(DrawTarget):
def __init__(self, out=sys.stdout, *args, **kwargs):
self.out = out
self.screen = Screen(io.StringIO(), *args, **kwargs)
self.on_screen = Pad(self.screen.width, self.screen.height)
self.style = None
@property
def width(self):
return self.screen.width
@property
def height(self):
return self.screen.height
def clear(self):
self.on_screen = Pad(self.screen.width, self.screen.height)
self.screen.clear()
def reset(self):
self.screen.update_size()
self.clear()
def update(self):
self.out.write(self.screen.out.getvalue())
self.screen.out = io.StringIO()
self.out.flush()
def write(self, x, y, text, style=None):
text = crop(text, self.screen.width-x)
self.on_screen.write(x, y, text, style)
#self.screen.write(x, y, text, style)
self.screen.move(x, y)
self.screen.style(style, self.style)
self.style = style
self.screen.addstr(text)
def draw_pad_direct(self, *args, **kwargs):
self.on_screen.draw_pad(*args, **kwargs)
self.screen.draw_pad(*args, **kwargs)
def draw_pad(self, pad, dest_x=0, dest_y=0, width=INT_INFINITY, height=INT_INFINITY, src_x=0, src_y=0):
# Optimizes on the amount of characters to write to the terminal, which is more crucial in applications running over a network connection (like ssh)
# This will only draw the changed characters
width = min(width, self.screen.width - dest_x, pad.width - src_x)
height = min(height, self.screen.height - dest_y, pad.height - src_y)
BEGIN = "BEGIN" # before anything on the line has been done
RUNNING = "RUNNING" # while changing current characters
POSTRUN = "POSTRUN" # after changing some characters. Unsure whether to jump to next place or just continue
POSTPOSTRUN = "POSTPOSTRUN" # same, but now the style has been changed
BETWEEN = "BETWEEN" # run finished, but not worth to continue. Looking for the next changes
for y in range(height):
#runs = []
#current_run = None
running = False
last_run = None
post_run = ""
postpost_run = ""
post_style = None
extra = 0
skip = 0
state = BEGIN
#self.style = None
#cursor_x = None
for x, (buff_cell) in enumerate(pad.get_line(src_x, src_y + y, width)):#zip(
#self.on_screen.get_line(dest_x, dest_y + y, width),
#pad.get_line(src_x, src_y + y, width))):
scr_cell = self.on_screen.get(dest_x + x, dest_y + y)
if scr_cell is None:
scr_cell = (None, None)
scr_style, scr_char = scr_cell
if buff_cell is None:
if state == BEGIN:
continue
if state == RUNNING:
cursor_x = x
skip += 1
state = BETWEEN
continue
buff_style, buff_char = buff_cell
while True:
if state == BEGIN or state == BETWEEN:
if scr_cell == buff_cell:
skip += 1
break
# start the first run
if state == BEGIN:
skip = 0
self.screen.move(dest_x + x, dest_y + y)
#else:
#self.screen.skip(skip)#x-cursor_x)
#skip = 0
state = RUNNING
if state == RUNNING:
if scr_cell != buff_cell:
self.screen.skip(skip)
skip = 0
# continuing the same run
self.screen.style(buff_style, self.style)
self.style = buff_style
self.screen.addstr(buff_char)
self.on_screen.set_char(x, y, buff_char, buff_style)
w = charwidth(buff_char)
if w == 2:
skip -= 1
#self.on_screen.set_char(x + 2, y, None)
self.on_screen.set_char(x + 1, y, None)
#self.on_screen.set_char(x, y, None)
break
#cursor_x = x #+ char_width(buf_char) - 1
state = BETWEEN#POSTRUN
extra = 0
post_run = ""
postpost_run = ""
#if state == POSTRUN:
#if buff_cell != scr_cell:
#self.screen.skip(skip)
#skip = 0
#self.screen.addstr(post_run)
#state = RUNNING
#continue
#elif extra >= 4:
#skip += extra
#state = BETWEEN
#break
#elif buff_style == self.style:
#extra += 1
#post_run += buff_char
#break
#else:
#new_style = buff_style
#state = POSTPOSTRUN
#if state == POSTPOSTRUN:
#if buff_style != new_style:
#state = BETWEEN
#break
#if buff_cell != scr_cell:
#self.screen.addstr(post_run)
#self.screen.style(new_style, self.style)
#self.screen.addstr(postpost_run)
#self.style = new_style
#state = RUNNING
#elif extra >= 4:
#skip += extra
#state = BETWEEN
#break
#else:
#extra += 1
#postpost_run += buff_char
#break
#self.on_screen.draw_pad(pad, dest_x, dest_y, width, height, src_x, src_y)