uix:textinput wrap_fix

This commit is contained in:
qua-non 2012-12-19 01:35:06 +05:30
parent e9c5b01231
commit 992f1e1fd4
1 changed files with 93 additions and 58 deletions

View File

@ -340,33 +340,53 @@ class TextInput(Widget):
insert_at_end = True if text[cc:] == '' else False
new_text = text[:cc] + substring + text[cc:]
self._set_line_text(cr, new_text)
if len_str > 1 or substring == '\n':
wrap = (self._get_text_width(new_text,
self.tab_width,
self._label_cached) > self.width)
if len_str > 1 or substring == '\n' or wrap:
# Avoid refreshing text on every keystroke.
# Allows for faster typing of text when the amount of text in
# TextInput gets large.
start = cr
lines, lineflags = self._split_smart(new_text)
len_lines = len(lines)
finish = cr + (len_lines - 1)
start, finish, lines,\
lineflags, len_lines = self._get_line_from_cursor(cr, new_text)
self._trigger_refresh_text('insert', start, finish, lines,
lineflags, len_lines)
# reset cursor
self.cursor = cursor = self.get_cursor_from_index(ci + len_str)
# handle undo and redo
self._set_unredo_insert(cc, cr, ci, sci, substring, cursor, from_undo)
def _set_unredo_insert(self, cc, cr, ci, sci, substring, cursor, from_undo):
self.cursor = self.get_cursor_from_index(ci + len_str)
# handle undo and redo
self._set_unredo_insert(ci, ci + len_str, substring, from_undo)
def _get_line_from_cursor(self, start, new_text):
finish = start
lines = self._lines
linesflags = self._lines_flags
if not linesflags[start]:
start -= 1
new_text = ''.join((lines[start], new_text))
try:
while not linesflags[finish + 1]:
new_text = ''.join((new_text, lines[finish + 1]))
finish += 1
except IndexError:
pass
lines, lineflags = self._split_smart(new_text)
len_lines = len(lines) - 1
return start, finish, lines, lineflags, len_lines
def _set_unredo_insert(self, ci, sci, substring, from_undo):
# handle undo and redo
if from_undo:
return
count = substring.count('\n')
if substring == '\n':
cursor = 0, cursor[1] + 1
elif count > 0:
cursor = cursor[0], cursor[1] + count
#count = substring.count('\n')
#if substring == '\n':
# cursor = 0, cursor[1] + 1
#elif count > 0:
# cursor = cursor[0], cursor[1] + count
self._undo.append({'undo_command': ('insert', cursor, ci, sci()),
'redo_command': (cc, cr, substring)})
self._undo.append({'undo_command': ('insert', ci, sci),
'redo_command': (ci, substring)})
# reset redo when undo is appended to
self._redo = []
@ -389,23 +409,23 @@ class TextInput(Widget):
try:
x_item = self._redo.pop()
undo_type = x_item['undo_command'][0]
_get_cusror_from_index = self.get_cursor_from_index
if undo_type == 'insert':
cc, cr, substring = x_item['redo_command']
self.cursor = cc, cr
ci, substring = x_item['redo_command']
self.cursor = _get_cusror_from_index(ci)
self.insert_text(substring, True)
elif undo_type == 'bkspc':
cc, cr = x_item['redo_command']
self.cursor = cc, cr
self.cursor = _get_cusror_from_index(x_item['redo_command'])
self.do_backspace(True)
else:
# delsel
ci, sci, cc, cr = x_item['redo_command']
ci, sci = x_item['redo_command']
self._selection_from = ci
self._selection_to = sci
self._selection = True
self.delete_selection(True)
self.cursor = (cc, cr)
self.cursor = _get_cusror_from_index(ci)
self._undo.append(x_item)
except IndexError:
# reached at top of undo list
@ -423,10 +443,10 @@ class TextInput(Widget):
try:
x_item = self._undo.pop()
undo_type = x_item['undo_command'][0]
self.cursor = x_item['undo_command'][1]
self.cursor = self.get_cursor_from_index(x_item['undo_command'][1])
if undo_type == 'insert':
ci, sci = x_item['undo_command'][2:]
ci, sci = x_item['undo_command'][1:]
self._selection_from = ci
self._selection_to = sci
self._selection = True
@ -443,7 +463,7 @@ class TextInput(Widget):
# reached at top of undo list
pass
def do_backspace(self, from_undo=False):
def do_backspace(self, from_undo=False, mode='bkspc'):
'''Do backspace operation from the current cursor position.
This action might do several things:
@ -459,33 +479,39 @@ class TextInput(Widget):
cursor_index = self.cursor_index()
if cc == 0 and cr == 0:
return
_lines_flags = self._lines_flags
if cc == 0:
text_last_line = self._lines[cr - 1]
substring = '\n' if _lines_flags[cr] else ' '
self._set_line_text(cr - 1, text_last_line + text)
self._delete_line(cr)
substring = '\n'
new_text = ''
else:
#ch = text[cc-1]
substring = text[cc - 1]
new_text = text[:cc - 1] + text[cc:]
self._set_line_text(cr, new_text)
# refresh_text seems to be unnecessary here
# plus removing it leads to a large improvement in editing text
# where large..ish text is involved.
#self._refresh_text_from_property()
self.cursor = cursor = self.get_cursor_from_index(cursor_index - 1)
# handle undo and redo
self._set_undo_redo_bkspc(cc, cr, cursor, substring, from_undo)
if not self._lines_flags[cr]:
# refresh just the current line instead of the whole text
start, finish, lines, lineflags, len_lines =\
self._get_line_from_cursor(cr, new_text)
self._trigger_refresh_text('del', start, finish, lines,
lineflags, len_lines)
def _set_undo_redo_bkspc(self, cc, cr, cursor, substring, from_undo):
self.cursor = self.get_cursor_from_index(cursor_index - 1)
# handle undo and redo
self._set_undo_redo_bkspc(cursor_index,
cursor_index - 1,
substring, from_undo)
def _set_undo_redo_bkspc(self, ol_index, new_index, substring, from_undo):
# handle undo and redo for backspace
if from_undo:
return
self._undo.append({
'undo_command': ('bkspc', cursor, substring),
'redo_command': (cc, cr)})
'undo_command': ('bkspc', new_index, substring),
'redo_command': ol_index})
#reset redo when undo is appended to
self._redo = []
@ -585,22 +611,25 @@ class TextInput(Widget):
self._lines[finish[1]][finish[0]:]
lines, lineflags = self._split_smart(cur_line)
len_lines = len(lines)
if start[1] == finish[1]:
self._set_line_text(start[1], cur_line)
else:
self._refresh_text(self.text, 'del', start[1], finish[1], lines,
lineflags, len_lines)
self.scroll_x = scrl_x
self.scroll_y = scrl_y
# handle undo and redo for delete selecttion
self._set_unredo_delsel(cc, cr, a, b, cursor, v[a:b], from_undo)
self._set_unredo_delsel(a, b, v[a:b], from_undo)
self.cancel_selection()
def _set_unredo_delsel(self, cc, cr, ci, sci, cursor, substring, from_undo):
def _set_unredo_delsel(self, a, b, substring, from_undo):
# handle undo and redo for backspace
if from_undo:
return
self._undo.append({
'undo_command': ('delsel', cursor, substring),
'redo_command': (ci, sci, cc, cr)})
'undo_command': ('delsel', a, substring),
'redo_command': (a, b)})
# reset redo when undo is appended to
self._redo = []
@ -897,10 +926,8 @@ class TextInput(Widget):
def _trigger_refresh_text(self, *largs):
if len(largs) and largs[0] == self:
largs = ()
Clock.unschedule(
lambda *args: self._refresh_text_from_property(*largs))
Clock.schedule_once(
lambda *args: self._refresh_text_from_property(*largs))
Clock.unschedule(lambda dt: self._refresh_text_from_property(*largs))
Clock.schedule_once(lambda dt: self._refresh_text_from_property(*largs))
def _update_text_options(self, *largs):
Cache_remove('textinput.width')
@ -913,8 +940,9 @@ class TextInput(Widget):
# Refresh all the lines from a new text.
# By using cache in internal functions, this method should be fast.
mode = 'all'
if len(largs):
if len(largs) > 1:
mode, start, finish, _lines, _lines_flags, len_lines = largs
start = max(0, start)
else:
_lines, self._lines_flags = self._split_smart(text)
_lines_labels = []
@ -933,10 +961,16 @@ class TextInput(Widget):
self._lines_labels = _lines_labels
self._lines_rects = _line_rects
elif mode == 'del':
self._insert_lines(start, finish + 1, len_lines, _lines_flags,
self._insert_lines(start,
finish if start == finish else (finish + 1),
len_lines, _lines_flags,
_lines, _lines_labels, _line_rects)
elif mode == 'insert':
self._insert_lines(start, start + 1, len_lines, _lines_flags,
self._insert_lines(start,
finish if (start == finish and not len_lines)
else
(finish + 1),
len_lines, _lines_flags,
_lines, _lines_labels, _line_rects)
line_label = _lines_labels[0]
@ -958,16 +992,17 @@ class TextInput(Widget):
def _insert_lines(self, start, finish, len_lines, _lines_flags, _lines,
_lines_labels, _line_rects):
self_lines_flags = self._lines_flags
_lins_flags = []
_lins_flags.extend(self._lines_flags[:start])
_lins_flags.extend(self_lines_flags[:start])
if len_lines:
# if not inserting at first line then
if start:
# make sure new line is set in line flags cause
# make sure line flags restored for first line
# _split_smart assumes first line to be not a new line
_lines_flags[0] = 1
_lines_flags[0] = self_lines_flags[start]
_lins_flags.extend(_lines_flags)
_lins_flags.extend(self._lines_flags[finish:])
_lins_flags.extend(self_lines_flags[finish:])
self._lines_flags = _lins_flags
_lins = []
@ -1311,7 +1346,7 @@ class TextInput(Widget):
cursor = self.cursor
self.do_cursor_movement('cursor_right')
if cursor != self.cursor:
self.do_backspace()
self.do_backspace(mode='del')
elif internal_action == 'backspace':
self.do_backspace()
elif internal_action == 'enter':