Refactor events.
from tuikit.core.widget import Widget
from tuikit.core.signal import Signal
class TextBox(Widget):
"""Multiline text view/edit widget.
Spot is used for text cursor position.
"""
def __init__(self, text=''):
Widget.__init__(self)
self.allow_focus = True
# Text content, splitted as lines
self._lines = []
self.text = text
# Cursor position is same as spot.
# This variable rememberes horizontal position
# for the case when cursor moves to shorter line.
self.cursor_column = 0
# selection - line and column of selection start
self.sel_line = 0
self.sel_column = 0
#self.add_events('scroll', Event)
@property
def text(self):
return '\n'.join(self._lines)
@text.setter
def text(self, value):
self._lines = value.split('\n')
maxlen = max([len(line) for line in self._lines])
self.sizereq.update(w=maxlen, h=len(self._lines))
@property
def cur_line(self):
return self._lines[self._spot.y]
@cur_line.setter
def cur_line(self, value):
self._lines[self._spot.y] = value
def set_theme(self, theme):
Widget.set_theme(self, theme)
self.color = theme.normal
def draw(self, buffer):
exposed = self.exposed(buffer)
with buffer.attr(self.color):
buffer.fill()
end_y = min(len(self._lines), exposed.y + exposed.h)
for j in range(exposed.y, end_y):
line = self._lines[j]
buffer.puts(line, 0, j)
#self.cursor = (self._spot.x, self._spot.y)
def keypress_event(self, ev):
if ev.keyname and not ev.mods:
consumed = True
if ev.keyname == 'left': self.move_left()
elif ev.keyname == 'right': self.move_right()
elif ev.keyname == 'home': self.move_home()
elif ev.keyname == 'end': self.move_end()
elif ev.keyname == 'up': self.move_up()
elif ev.keyname == 'down': self.move_down()
elif ev.keyname == 'pageup': self.move_pageup()
elif ev.keyname == 'pagedown': self.move_pagedown()
elif ev.keyname == 'backspace': self.backspace()
elif ev.keyname == 'delete': self.del_char()
elif ev.keyname == 'enter': self.add_newline(move=True)
else:
consumed = False
if consumed:
return True
if ev.mods:
consumed = True
mk = ev.mod_key()
if mk == 'ctrl+home': self.move_top()
elif mk == 'ctrl+end': self.move_bottom()
else:
consumed = False
if consumed:
return True
if ev.char and not ev.keyname:
self.add_char(ev.char)
self.move_right()
return True
def on_mousedown(self, ev):
y = ev.wy
x = min(ev.wx, len(self._lines[y]))
self._spot.update(x=x, y=y)
self.redraw()
def on_mousewheel(self, ev):
if ev.button == 4:
# wheel up
self.emit('scrollreq', -5)
if ev.button == 5:
# wheel down
self.emit('scrollreq', +5)
self.redraw()
def move_left(self):
if self._spot.x > 0:
self._spot.x -= 1
else:
if self._spot.y > 0:
self._spot.y -= 1
self._spot.x = len(self.cur_line)
self.cursor_column = self._spot.x
def move_right(self):
if self._spot.x < len(self.cur_line):
self._spot.x += 1
else:
if self._spot.y < len(self._lines) - 1:
self._spot.y += 1
self._spot.x = 0
self.cursor_column = self._spot.x
def move_home(self):
self._spot.x = 0
self.cursor_column = self._spot.x
def move_end(self):
self._spot.x = len(self.cur_line)
self.cursor_column = self._spot.x
def move_up(self):
if self._spot.y > 0:
self._spot.y -= 1
self._update_spot_x()
def move_down(self):
if self._spot.y < len(self._lines) - 1:
self._spot.y += 1
self._update_spot_x()
def move_pageup(self):
if self._spot.y >= self.view_height - 1:
self.emit('scrollreq', - (self.view_height - 1))
self._spot.y -= self.view_height - 1
else:
self._spot.y = 0
self._update_spot_x()
def move_pagedown(self):
if len(self._lines) - self._spot.y > (self.view_height - 1):
self.emit('scrollreq', (self.view_height - 1))
self._spot.y += self.view_height - 1
else:
self._spot.y = len(self._lines) - 1
self._update_spot_x()
def move_top(self):
self._spot.y = 0
self._update_spot_x()
def move_bottom(self):
self._spot.y = len(self._lines) - 1
self._update_spot_x()
def add_char(self, c):
ln = self.cur_line
sx = self._spot.x
self.cur_line = ln[:sx] + c + ln[sx:]
self.cursor_column = sx
def add_newline(self, move=False):
ln = self.cur_line
sx = self._spot.x
self.cur_line = ln[sx:]
self._lines.insert(self._spot.y, ln[:sx])
self._default_size.update(h=len(self._lines))
if move:
self.move_right()
def add_line(self, text):
ln = self.cur_line
sx = self._spot.x
self.cur_line = ln[sx:]
self._lines.insert(self._spot.y, ln[:sx] + text)
self.cursor_column = 0
self._spot.x = 0
self._spot.y += 1
w = max(self._default_size.w, len(ln[:sx] + text))
self._default_size.update(w=w, h=len(self._lines))
def backspace(self):
if self._spot.y > 0 or self._spot.x > 0:
self.move_left()
self.del_char()
def del_char(self):
ln = self.cur_line
sx = self._spot.x
if sx == len(self.cur_line):
if self._spot.y + 1 < len(self._lines):
self.cur_line += self._lines[self._spot.y+1]
del self._lines[self._spot.y+1]
self._default_size.update(h=len(self._lines))
else:
self.cur_line = ln[:sx] + ln[sx+1:]
def _update_spot_x(self):
if self.cursor_column > len(self.cur_line):
self._spot.x = len(self.cur_line)
else:
self._spot.x = self.cursor_column