js代码下载网站,怎样看网站的建设时间表,萍乡市建设局网站,2016年做网站能赚钱吗一个小小的输入框#xff0c;纵上下数页文档已不能全不概括#xff0c;当去源码慢慢寻找#xff0c;才知道其中作用#xff0c;才能运用灵活。
Text Input — Kivy 2.3.0 documentation # -*- encoding: utf-8 -*-Text Input
.. versionadded:: 1.0.4.. image:: images/te…一个小小的输入框纵上下数页文档已不能全不概括当去源码慢慢寻找才知道其中作用才能运用灵活。
Text Input — Kivy 2.3.0 documentation # -*- encoding: utf-8 -*-Text Input
.. versionadded:: 1.0.4.. image:: images/textinput-mono.jpg
.. image:: images/textinput-multi.jpgThe :class:TextInput widget provides a box for editable plain text.Unicode, multiline, cursor navigation, selection and clipboard features
are supported.The :class:TextInput uses two different coordinate systems:* (x, y) - coordinates in pixels, mostly used for rendering on screen.
* (col, row) - cursor index in characters / lines, used for selectionand cursor movement.Usage example
-------------To create a multiline :class:TextInput (the enter key adds a new line)::from kivy.uix.textinput import TextInputtextinput TextInput(textHello world)To create a singleline :class:TextInput, set the :class:TextInput.multiline
property to False (the enter key will defocus the TextInput and emit an
:meth:TextInput.on_text_validate event)::def on_enter(instance, value):print(User pressed enter in, instance)textinput TextInput(textHello world, multilineFalse)textinput.bind(on_text_validateon_enter)The textinputs text is stored in its :attr:TextInput.text property. To run a
callback when the text changes::def on_text(instance, value):print(The widget, instance, have:, value)textinput TextInput()textinput.bind(texton_text)You can set the :class:focus kivy.uix.behaviors.FocusBehavior to a
Textinput, meaning that the input box will be highlighted and keyboard focus
will be requested::textinput TextInput(focusTrue)The textinput is defocused if the escape key is pressed, or if another
widget requests the keyboard. You can bind a callback to the focus property to
get notified of focus changes::def on_focus(instance, value):if value:print(User focused, instance)else:print(User defocused, instance)textinput TextInput()textinput.bind(focuson_focus)See :class:~kivy.uix.behaviors.FocusBehavior, from which the
:class:TextInput inherits, for more details.Selection
---------The selection is automatically updated when the cursor position changes.
You can get the currently selected text from the
:attr:TextInput.selection_text property.Filtering
---------You can control which text can be added to the :class:TextInput by
overwriting :meth:TextInput.insert_text. Every string that is typed, pasted
or inserted by any other means into the :class:TextInput is passed through
this function. By overwriting it you can reject or change unwanted characters.For example, to write only in capitalized characters::class CapitalInput(TextInput):def insert_text(self, substring, from_undoFalse):s substring.upper()return super(CapitalInput, self).insert_text(s,\from_undofrom_undo)Or to only allow floats (0 - 9 and a single period)::class FloatInput(TextInput):pat re.compile([^0-9])def insert_text(self, substring, from_undoFalse):pat self.patif . in self.text:s re.sub(pat, , substring)else:s ..join([re.sub(pat, , s) for s in\substring.split(., 1)])return super(FloatInput, self).insert_text(s, from_undofrom_undo)Default shortcuts
----------------- Shortcuts Description
--------------- --------------------------------------------------------
Left Move cursor to left
Right Move cursor to right
Up Move cursor to up
Down Move cursor to down
Home Move cursor at the beginning of the line
End Move cursor at the end of the line
PageUp Move cursor to 3 lines before
PageDown Move cursor to 3 lines after
Backspace Delete the selection or character before the cursor
Del Delete the selection of character after the cursor
Shift dir Start a text selection. Dir can be Up, Down, Left orRight
Control c Copy selection
Control x Cut selection
Control v Paste clipboard content
Control a Select all the content
Control z undo
Control r redo.. note::To enable Emacs-style keyboard shortcuts, you can use:class:~kivy.uix.behaviors.emacs.EmacsBehavior.__all__ (TextInput, )import re
import sys
from os import environ
from weakref import reffrom kivy.animation import Animation
from kivy.base import EventLoop
from kivy.cache import Cache
from kivy.clock import Clock
from kivy.config import Config
from kivy.core.window import Window
from kivy.metrics import inch
from kivy.utils import boundary, platform
from kivy.uix.behaviors import FocusBehaviorfrom kivy.core.text import Label, DEFAULT_FONT
from kivy.graphics import Color, Rectangle, PushMatrix, PopMatrix, Callback
from kivy.graphics.context_instructions import Transform
from kivy.graphics.texture import Texturefrom kivy.uix.widget import Widget
from kivy.uix.bubble import Bubble
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Imagefrom kivy.properties import StringProperty, NumericProperty, \BooleanProperty, AliasProperty, OptionProperty, \ListProperty, ObjectProperty, VariableListProperty, ColorPropertyCache_register Cache.register
Cache_append Cache.append
Cache_get Cache.get
Cache_remove Cache.remove
Cache_register(textinput.label, timeout60.)
Cache_register(textinput.width, timeout60.)FL_IS_LINEBREAK 0x01
FL_IS_WORDBREAK 0x02
FL_IS_NEWLINE FL_IS_LINEBREAK | FL_IS_WORDBREAK# late binding
Clipboard None
CutBuffer None
MarkupLabel None
_platform platform# for reloading, we need to keep a list of textinput to retrigger the rendering
_textinput_list []# cache the result
_is_osx sys.platform darwin# When we are generating documentation, Config doesnt exist
_is_desktop False
if Config:_is_desktop Config.getboolean(kivy, desktop)# register an observer to clear the textinput cache when OpenGL will reload
if KIVY_DOC not in environ:def _textinput_clear_cache(*l):Cache_remove(textinput.label)Cache_remove(textinput.width)for wr in _textinput_list[:]:textinput wr()if textinput is None:_textinput_list.remove(wr)else:textinput._trigger_refresh_text()textinput._refresh_hint_text()from kivy.graphics.context import get_contextget_context().add_reload_observer(_textinput_clear_cache, True)class Selector(ButtonBehavior, Image):# Internal class for managing the selection Handles.window ObjectProperty()target ObjectProperty()matrix ObjectProperty()def __init__(self, **kwargs):super(Selector, self).__init__(**kwargs)self.matrix self.target.get_window_matrix()with self.canvas.before:Callback(self.update_transform)PushMatrix()self.transform Transform()with self.canvas.after:PopMatrix()def update_transform(self, cb):m self.target.get_window_matrix()if self.matrix ! m:self.matrix mself.transform.identity()self.transform.transform(self.matrix)def transform_touch(self, touch):matrix self.matrix.inverse()touch.apply_transform_2d(lambda x, y: matrix.transform_point(x, y, 0)[:2])def on_touch_down(self, touch):if self.parent is not EventLoop.window:returntry:touch.push()self.transform_touch(touch)self._touch_diff self.top - touch.yif self.collide_point(*touch.pos):FocusBehavior.ignored_touch.append(touch)return super(Selector, self).on_touch_down(touch)finally:touch.pop()class TextInputCutCopyPaste(Bubble):# Internal class used for showing the little bubble popup when# copy/cut/paste happen.textinput ObjectProperty(None) Holds a reference to the TextInput this Bubble belongs to.but_cut ObjectProperty(None)but_copy ObjectProperty(None)but_paste ObjectProperty(None)but_selectall ObjectProperty(None)matrix ObjectProperty(None)_check_parent_ev Nonedef __init__(self, **kwargs):self.mode normalsuper(TextInputCutCopyPaste, self).__init__(**kwargs)self._check_parent_ev Clock.schedule_interval(self._check_parent, .5)self.matrix self.textinput.get_window_matrix()with self.canvas.before:Callback(self.update_transform)PushMatrix()self.transform Transform()with self.canvas.after:PopMatrix()def update_transform(self, cb):m self.textinput.get_window_matrix()if self.matrix ! m:self.matrix mself.transform.identity()self.transform.transform(self.matrix)def transform_touch(self, touch):matrix self.matrix.inverse()touch.apply_transform_2d(lambda x, y: matrix.transform_point(x, y, 0)[:2])def on_touch_down(self, touch):try:touch.push()self.transform_touch(touch)if self.collide_point(*touch.pos):FocusBehavior.ignored_touch.append(touch)return super(TextInputCutCopyPaste, self).on_touch_down(touch)finally:touch.pop()def on_touch_up(self, touch):try:touch.push()self.transform_touch(touch)for child in self.content.children:if ref(child) in touch.grab_list:touch.grab_current childbreakreturn super(TextInputCutCopyPaste, self).on_touch_up(touch)finally:touch.pop()def on_textinput(self, instance, value):global Clipboardif value and not Clipboard and not _is_desktop:value._ensure_clipboard()def _check_parent(self, dt):# this is a prevention to get the Bubble staying on the screen, if the# attached textinput is not on the screen anymore.parent self.textinputwhile parent is not None:if parent parent.parent:breakparent parent.parentif parent is None:self._check_parent_ev.cancel()if self.textinput:self.textinput._hide_cut_copy_paste()def on_parent(self, instance, value):parent self.textinputmode self.modeif parent:self.clear_widgets()if mode paste:# show only paste on long touchself.but_selectall.opacity 1widget_list [self.but_selectall, ]if not parent.readonly:widget_list.append(self.but_paste)elif parent.readonly:# show only copy for read only text inputwidget_list (self.but_copy, )else:# normal modewidget_list (self.but_cut, self.but_copy, self.but_paste)for widget in widget_list:self.add_widget(widget)def do(self, action):textinput self.textinputif action cut:textinput._cut(textinput.selection_text)elif action copy:textinput.copy()elif action paste:textinput.paste()elif action selectall:textinput.select_all()self.mode anim Animation(opacity0, d.333)anim.bind(on_completelambda *args:self.on_parent(self, self.parent))anim.start(self.but_selectall)returnself.hide()def hide(self):parent self.parentif not parent:returnanim Animation(opacity0, d.225)anim.bind(on_completelambda *args: parent.remove_widget(self))anim.start(self)class TextInput(FocusBehavior, Widget):TextInput class. See module documentation for more information.:Events:on_text_validateFired only in multilineFalse mode when the user hits enter.This will also unfocus the textinput.on_double_tapFired when a double tap happens in the text input. The defaultbehavior selects the text around the cursor position. More info at:meth:on_double_tap.on_triple_tapFired when a triple tap happens in the text input. The defaultbehavior selects the line around the cursor position. More info at:meth:on_triple_tap.on_quad_touchFired when four fingers are touching the text input. The defaultbehavior selects the whole text. More info at:meth:on_quad_touch... warning::When changing a :class:TextInput property that requires re-drawing,e.g. modifying the :attr:text, the updates occur on the nextclock cycle and not instantly. This might cause any changes to the:class:TextInput that occur between the modification and the nextcycle to be ignored, or to use previous values. For example, aftera update to the :attr:text, changing the cursor in the same clockframe will move it using the previous text and will likely end up in anincorrect position. The solution is to schedule any updates to occuron the next clock cycle using:meth:~kivy.clock.ClockBase.schedule_once... Note::Selection is cancelled when TextInput is focused. If you need toshow selection when TextInput is focused, you should delay(use Clock.schedule) the call to the functions for selectingtext (select_all, select_text)... versionchanged:: 1.10.0background_disabled_active has been removed... versionchanged:: 1.9.0:class:TextInput now inherits from:class:~kivy.uix.behaviors.FocusBehavior.:attr:~kivy.uix.behaviors.FocusBehavior.keyboard_mode,:meth:~kivy.uix.behaviors.FocusBehavior.show_keyboard,:meth:~kivy.uix.behaviors.FocusBehavior.hide_keyboard,:meth:~kivy.uix.behaviors.FocusBehavior.focus,and :attr:~kivy.uix.behaviors.FocusBehavior.input_typehave been removed since they are now inheritedfrom :class:~kivy.uix.behaviors.FocusBehavior... versionchanged:: 1.7.0on_double_tap, on_triple_tap and on_quad_touch events added.__events__ (on_text_validate, on_double_tap, on_triple_tap,on_quad_touch)_resolved_base_dir Nonedef __init__(self, **kwargs):self._update_graphics_ev Clock.create_trigger(self._update_graphics, -1)self.is_focusable kwargs.get(is_focusable, True)self._cursor [0, 0]self._selection Falseself._selection_finished Trueself._selection_touch Noneself.selection_text uself._selection_from Noneself._selection_to Noneself._selection_callback Noneself._handle_left Noneself._handle_right Noneself._handle_middle Noneself._bubble Noneself._lines_flags []self._lines_labels []self._lines_rects []self._hint_text_flags []self._hint_text_labels []self._hint_text_rects []self._label_cached Noneself._line_options Noneself._keyboard_mode Config.get(kivy, keyboard_mode)self._command_mode Falseself._command self.reset_undo()self._touch_count 0self._ctrl_l Falseself._ctrl_r Falseself._alt_l Falseself._alt_r Falseself._refresh_text_from_property_ev Noneself._long_touch_ev Noneself._do_blink_cursor_ev Clock.create_trigger(self._do_blink_cursor, .5, intervalTrue)self._refresh_line_options_ev None# [from; to) range of lines being partially or fully rendered# in TextInputs viewportself._visible_lines_range 0, 0self.interesting_keys {8: backspace,13: enter,127: del,271: enter,273: cursor_up,274: cursor_down,275: cursor_right,276: cursor_left,278: cursor_home,279: cursor_end,280: cursor_pgup,281: cursor_pgdown,303: shift_L,304: shift_R,305: ctrl_L,306: ctrl_R,308: alt_L,307: alt_R}super(TextInput, self).__init__(**kwargs)fbind self.fbindrefresh_line_options self._trigger_refresh_line_optionsupdate_text_options self._update_text_optionstrigger_update_graphics self._trigger_update_graphicsfbind(font_size, refresh_line_options)fbind(font_name, refresh_line_options)fbind(font_context, refresh_line_options)fbind(font_family, refresh_line_options)fbind(base_direction, refresh_line_options)fbind(text_language, refresh_line_options)def handle_readonly(instance, value):if value and (not _is_desktop or not self.allow_copy):self.is_focusable Falseif (not (value or self.disabled) or _is_desktop andself._keyboard_mode system):self._editable Trueelse:self._editable Falsefbind(padding, update_text_options)fbind(tab_width, update_text_options)fbind(font_size, update_text_options)fbind(font_name, update_text_options)fbind(size, update_text_options)fbind(password, update_text_options)fbind(password_mask, update_text_options)fbind(pos, trigger_update_graphics)fbind(halign, trigger_update_graphics)fbind(readonly, handle_readonly)fbind(focus, self._on_textinput_focused)handle_readonly(self, self.readonly)handles self._trigger_position_handles Clock.create_trigger(self._position_handles)self._trigger_show_handles Clock.create_trigger(self._show_handles, .05)self._trigger_cursor_reset Clock.create_trigger(self._reset_cursor_blink)self._trigger_update_cutbuffer Clock.create_trigger(self._update_cutbuffer)refresh_line_options()self._trigger_refresh_text()fbind(pos, handles)fbind(size, handles)# when the gl context is reloaded, trigger the text rendering again._textinput_list.append(ref(self, TextInput._reload_remove_observer))if platform linux:self._ensure_clipboard()def on_text_validate(self):passdef cursor_index(self, cursorNone):Return the cursor index in the text/value.if not cursor:cursor self.cursortry:l self._linesif len(l) 0:return 0lf self._lines_flagsindex, cr cursorfor row in range(cr):if row len(l):continueindex len(l[row])if lf[row] FL_IS_LINEBREAK:index 1if lf[cr] FL_IS_LINEBREAK:index 1return indexexcept IndexError:return 0def cursor_offset(self):Get the cursor x offset on the current line.offset 0row int(self.cursor_row)col int(self.cursor_col)_lines self._linesif col and row len(_lines):offset self._get_text_width(_lines[row][:col],self.tab_width,self._label_cached)return offsetdef get_cursor_from_index(self, index):Return the (col, row) of the cursor from text index.index boundary(index, 0, len(self.text))if index 0:return 0, 0lf self._lines_flagsl self._linesi 0for row in range(len(l)):ni i len(l[row])if lf[row] FL_IS_LINEBREAK:ni 1i 1if ni index:return index - i, rowi nireturn index, rowdef select_text(self, start, end): Select a portion of text displayed in this TextInput... versionadded:: 1.4.0:Parameters:startIndex of textinput.text from where to start selectionendIndex of textinput.text till which the selection should bedisplayedif end start:raise Exception(end must be superior to start)m len(self.text)self._selection_from boundary(start, 0, m)self._selection_to boundary(end, 0, m)self._selection_finished Trueself._update_selection(True)self._update_graphics_selection()def select_all(self): Select all of the text displayed in this TextInput... versionadded:: 1.4.0self.select_text(0, len(self.text))re_indent re.compile(r^(\s*|))def _auto_indent(self, substring):index self.cursor_index()if index 0:_text self.textline_start _text.rfind(\n, 0, index)if line_start -1:line _text[line_start 1:index]indent self.re_indent.match(line).group()substring indentreturn substringdef insert_text(self, substring, from_undoFalse):Insert new text at the current cursor position. Override thisfunction in order to pre-process text for input validation.if self.readonly or not substring or not self._lines:returnif isinstance(substring, bytes):substring substring.decode(utf8)if self.replace_crlf:substring substring.replace(u\r\n, u\n)self._hide_handles(EventLoop.window)if not from_undo and self.multiline and self.auto_indent \and substring u\n:substring self._auto_indent(substring)mode self.input_filterif mode not in (None, int, float):substring mode(substring, from_undo)if not substring:returncc, cr self.cursorsci self.cursor_indexci sci()text self._lines[cr]len_str len(substring)new_text text[:cc] substring text[cc:]if mode is not None:if mode int:if not re.match(self._insert_int_pat, new_text):returnelif mode float:if not re.match(self._insert_float_pat, new_text):returnself._set_line_text(cr, new_text)wrap (self._get_text_width(new_text,self.tab_width,self._label_cached) (self.width - self.padding[0] -self.padding[2]))if len_str 1 or substring u\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, finish, lines,\lineflags, len_lines self._get_line_from_cursor(cr, new_text)# calling trigger here could lead to wrong cursor positioning# and repeating of text when keys are added rapidly in a automated# fashion. From Android Keyboard for example.self._refresh_text_from_property(insert, start, finish, lines,lineflags, len_lines)self.cursor self.get_cursor_from_index(ci len_str)# handle undo and redoself._set_unredo_insert(ci, ci len_str, substring, from_undo)def _get_line_from_cursor(self, start, new_text):# get current paragraph from cursor positionfinish startlines self._lineslinesflags self._lines_flagsif start and not linesflags[start]:start - 1new_text u.join((lines[start], new_text))try:while not linesflags[finish 1]:new_text u.join((new_text, lines[finish 1]))finish 1except IndexError:passlines, lineflags self._split_smart(new_text)len_lines max(1, len(lines))return start, finish, lines, lineflags, len_linesdef _set_unredo_insert(self, ci, sci, substring, from_undo):# handle undo and redoif from_undo:returnself._undo.append({undo_command: (insert, ci, sci),redo_command: (ci, substring)})# reset redo when undo is appended toself._redo []def reset_undo(self):Reset undo and redo lists from memory... versionadded:: 1.3.0self._redo self._undo []def do_redo(self):Do redo operation... versionadded:: 1.3.0This action re-does any command that has been un-done bydo_undo/ctrlz. This function is automatically called whenctrlr keys are pressed.try:x_item self._redo.pop()undo_type x_item[undo_command][0]_get_cusror_from_index self.get_cursor_from_indexif undo_type insert:ci, substring x_item[redo_command]self.cursor _get_cusror_from_index(ci)self.insert_text(substring, True)elif undo_type bkspc:self.cursor _get_cusror_from_index(x_item[redo_command])self.do_backspace(from_undoTrue)elif undo_type shiftln:direction, rows, cursor x_item[redo_command][1:]self._shift_lines(direction, rows, cursor, True)else:# delselci, sci x_item[redo_command]self._selection_from ciself._selection_to sciself._selection Trueself.delete_selection(True)self.cursor _get_cusror_from_index(ci)self._undo.append(x_item)except IndexError:# reached at top of undo listpassdef do_undo(self):Do undo operation... versionadded:: 1.3.0This action un-does any edits that have been made since the lastcall to reset_undo().This function is automatically called when ctrlz keys are pressed.try:x_item self._undo.pop()undo_type x_item[undo_command][0]self.cursor self.get_cursor_from_index(x_item[undo_command][1])if undo_type insert:ci, sci x_item[undo_command][1:]self._selection_from ciself._selection_to sciself._selection Trueself.delete_selection(True)elif undo_type bkspc:substring x_item[undo_command][2:][0]self.insert_text(substring, True)elif undo_type shiftln:direction, rows, cursor x_item[undo_command][1:]self._shift_lines(direction, rows, cursor, True)else:# delselsubstring x_item[undo_command][2:][0]self.insert_text(substring, True)self._redo.append(x_item)except IndexError:# reached at top of undo listpassdef do_backspace(self, from_undoFalse, modebkspc):Do backspace operation from the current cursor position.This action might do several things:- removing the current selection if available.- removing the previous char and move the cursor back.- do nothing, if we are at the start.# IME system handles its own backspacesif self.readonly or self._ime_composition:returncc, cr self.cursor_lines self._linestext _lines[cr]cursor_index self.cursor_index()text_last_line _lines[cr - 1]if cc 0 and cr 0:return_lines_flags self._lines_flagsstart crif cc 0:substring u\n if _lines_flags[cr] else u new_text text_last_line textself._set_line_text(cr - 1, new_text)self._delete_line(cr)start cr - 1else:# ch text[cc-1]substring text[cc - 1]new_text text[:cc - 1] text[cc:]self._set_line_text(cr, new_text)# refresh just the current line instead of the whole textstart, finish, lines, lineflags, len_lines \self._get_line_from_cursor(start, new_text)# avoid trigger refresh, leads to issue with# keys/text send rapidly through code.self._refresh_text_from_property(del, start, finish, lines,lineflags, len_lines)self.cursor self.get_cursor_from_index(cursor_index - 1)# handle undo and redoself._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 backspaceif from_undo:returnself._undo.append({undo_command: (bkspc, new_index, substring),redo_command: ol_index})# reset redo when undo is appended toself._redo []_re_whitespace re.compile(r\s)def _move_cursor_word_left(self, indexNone):pos index or self.cursor_index()if pos 0:return self.cursorlines self._linescol, row self.get_cursor_from_index(pos)if col 0:row - 1col len(lines[row])while True:matches list(self._re_whitespace.finditer(lines[row], 0, col))if not matches:if col 0:if row 0:return 0, 0row - 1col len(lines[row])continuereturn 0, rowmatch matches[-1]mpos match.end()if mpos col:if len(matches) 1:match matches[-2]mpos match.end()else:if match.start() 0:if row 0:return 0, 0row - 1col len(lines[row])continuereturn 0, rowcol mposreturn col, rowdef _move_cursor_word_right(self, indexNone):pos index or self.cursor_index()col, row self.get_cursor_from_index(pos)lines self._linesmrow len(lines) - 1if row mrow and col len(lines[row]):return col, rowif col len(lines[row]):row 1col 0while True:matches list(self._re_whitespace.finditer(lines[row], col))if not matches:if col len(lines[row]):if row mrow:return col, rowrow 1col 0continuereturn len(lines[row]), rowmatch matches[0]mpos match.start()if mpos col:if len(matches) 1:match matches[1]mpos match.start()else:if match.end() len(lines[row]):if row mrow:return col, rowrow 1col 0continuereturn len(lines[row]), rowcol mposreturn col, rowdef _expand_range(self, ifrom, itoNone):if ito is None:ito ifromrfrom self.get_cursor_from_index(ifrom)[1]rtcol, rto self.get_cursor_from_index(ito)rfrom, rto self._expand_rows(rfrom, rto 1 if rtcol else rto)return (self.cursor_index((0, rfrom)),self.cursor_index((0, rto)))def _expand_rows(self, rfrom, rtoNone):if rto is None or rto rfrom:rto rfrom 1lines self._linesflags list(reversed(self._lines_flags))while rfrom 0 and not (flags[rfrom - 1] FL_IS_NEWLINE):rfrom - 1rmax len(lines) - 1while 0 rto rmax and not (flags[rto - 1] FL_IS_NEWLINE):rto 1return max(0, rfrom), min(rmax, rto)def _shift_lines(self, direction, rowsNone, old_cursorNone,from_undoFalse):if self._selection_callback:if from_undo:self._selection_callback.cancel()else:returnlines self._linesflags list(reversed(self._lines_flags))labels self._lines_labelsrects self._lines_rectsorig_cursor self.cursorsel Noneif old_cursor is not None:self.cursor old_cursorif not rows:sindex self.selection_fromeindex self.selection_toif (sindex or eindex) and sindex ! eindex:sindex, eindex tuple(sorted((sindex, eindex)))sindex, eindex self._expand_range(sindex, eindex)else:sindex, eindex self._expand_range(self.cursor_index())srow self.get_cursor_from_index(sindex)[1]erow self.get_cursor_from_index(eindex)[1]sel sindex, eindexif direction 0 and srow 0:psrow, perow self._expand_rows(srow - 1)rows ((srow, erow), (psrow, perow))elif direction 0 and erow len(lines) - 1:psrow, perow self._expand_rows(erow)rows ((srow, erow), (psrow, perow))if rows:(srow, erow), (psrow, perow) rowsif direction 0:m1srow, m1erow psrow, perowm2srow, m2erow srow, erowcdiff psrow - perowxdiff srow - erowelse:m1srow, m1erow srow, erowm2srow, m2erow psrow, perowcdiff perow - psrowxdiff erow - srowself._lines_flags list(reversed(flags[:m1srow] flags[m2srow:m2erow] flags[m1srow:m1erow] flags[m2erow:]))self._lines[:] (lines[:m1srow] lines[m2srow:m2erow] lines[m1srow:m1erow] lines[m2erow:])self._lines_labels (labels[:m1srow] labels[m2srow:m2erow] labels[m1srow:m1erow] labels[m2erow:])self._lines_rects (rects[:m1srow] rects[m2srow:m2erow] rects[m1srow:m1erow] rects[m2erow:])self._trigger_update_graphics()csrow srow cdiffcerow erow cdiffsel (self.cursor_index((0, csrow)),self.cursor_index((0, cerow)))self.cursor self.cursor_col, self.cursor_row cdiffif not from_undo:undo_rows ((srow cdiff, erow cdiff),(psrow - xdiff, perow - xdiff))self._undo.append({undo_command: (shiftln, direction * -1, undo_rows,self.cursor),redo_command: (shiftln, direction, rows, orig_cursor),})self._redo []if sel:def cb(dt):self.select_text(*sel)self._selection_callback Noneself._selection_callback Clock.schedule_once(cb)def do_cursor_movement(self, action, controlFalse, altFalse):Move the cursor relative to its current position.Action can be one of :- cursor_left: move the cursor to the left- cursor_right: move the cursor to the right- cursor_up: move the cursor on the previous line- cursor_down: move the cursor on the next line- cursor_home: move the cursor at the start of the current line- cursor_end: move the cursor at the end of current line- cursor_pgup: move one page before- cursor_pgdown: move one page afterIn addition, the behavior of certain actions can be modified:- control cursor_left: move the cursor one word to the left- control cursor_right: move the cursor one word to the right- control cursor_up: scroll up one line- control cursor_down: scroll down one line- control cursor_home: go to beginning of text- control cursor_end: go to end of text- alt cursor_up: shift line(s) up- alt cursor_down: shift line(s) down.. versionchanged:: 1.9.1if not self._lines:returnpgmove_speed int(self.height /(self.line_height self.line_spacing) - 1)col, row self.cursorif action cursor_up:if self.multiline and control:self.scroll_y max(0, self.scroll_y - self.line_height)elif not self.readonly and self.multiline and alt:self._shift_lines(-1)returnelse:row max(row - 1, 0)col min(len(self._lines[row]), col)elif action cursor_down:if self.multiline and control:maxy self.minimum_height - self.heightself.scroll_y max(0, min(maxy,self.scroll_y self.line_height))elif not self.readonly and self.multiline and alt:self._shift_lines(1)returnelse:row min(row 1, len(self._lines) - 1)col min(len(self._lines[row]), col)elif action cursor_home:col 0if control:row 0elif action cursor_end:if control:row len(self._lines) - 1col len(self._lines[row])elif action cursor_pgup:row max(0, row - pgmove_speed)col min(len(self._lines[row]), col)elif action cursor_pgdown:row min(row pgmove_speed, len(self._lines) - 1)col min(len(self._lines[row]), col)elif (self._selection and self._selection_finished andself._selection_from self._selection_to andaction cursor_left):current_selection_to self._selection_towhile self._selection_from ! current_selection_to:current_selection_to - 1if col:col - 1else:row - 1col len(self._lines[row])elif (self._selection and self._selection_finished andself._selection_from self._selection_to andaction cursor_right):current_selection_to self._selection_towhile self._selection_from ! current_selection_to:current_selection_to 1if len(self._lines[row]) col:col 1else:row 1col 0elif action cursor_left:if not self.password and control:col, row self._move_cursor_word_left()else:if col 0:if row:row - 1col len(self._lines[row])else:col, row col - 1, rowelif action cursor_right:if not self.password and control:col, row self._move_cursor_word_right()else:if col len(self._lines[row]):if row len(self._lines) - 1:col 0row 1else:col, row col 1, rowdont_move_cursor control and action in [cursor_up, cursor_down]if dont_move_cursor:self._trigger_update_graphics()else:self.cursor (col, row)def get_cursor_from_xy(self, x, y):Return the (col, row) of the cursor from an (x, y) position.padding_left self.padding[0]padding_top self.padding[1]l self._linesdy self.line_height self.line_spacingcx x - self.xscrl_y self.scroll_yscrl_x self.scroll_xscrl_y scrl_y / dy if scrl_y 0 else 0cy (self.top - padding_top scrl_y * dy) - ycy int(boundary(round(cy / dy - 0.5), 0, len(l) - 1))_get_text_width self._get_text_width_tab_width self.tab_width_label_cached self._label_cached# Offset for horizontal text alignmentxoff 0halign self.halignbase_dir self.base_direction or self._resolved_base_dirauto_halign_r halign auto and base_dir and rtl in base_dirif halign center:viewport_width self.width - padding_left - self.padding[2] # _rxoff int((viewport_width - self._get_row_width(cy)) / 2)elif halign right or auto_halign_r:viewport_width self.width - padding_left - self.padding[2] # _rxoff viewport_width - self._get_row_width(cy)for i in range(0, len(l[cy])):if xoff _get_text_width(l[cy][:i], _tab_width, _label_cached) \_get_text_width(l[cy][i], _tab_width, _label_cached) * 0.6 \padding_left cx scrl_x:cx ibreakreturn cx, cy## Selection control#def cancel_selection(self):Cancel current selection (if any).self._selection_from self._selection_to self.cursor_index()self._selection Falseself._selection_finished Trueself._selection_touch Noneself.selection_text uself._trigger_update_graphics()def delete_selection(self, from_undoFalse):Delete the current text selection (if any).if self.readonly:returnself._hide_handles(EventLoop.window)scrl_x self.scroll_xscrl_y self.scroll_ycc, cr self.cursorif not self._selection:returntext self.texta, b self._selection_from, self._selection_toif a b:a, b b, aself.cursor cursor self.get_cursor_from_index(a)start cursorfinish self.get_cursor_from_index(b)cur_line self._lines[start[1]][:start[0]] \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_from_property(del, start[1], finish[1], lines,lineflags, len_lines)self.scroll_x scrl_xself.scroll_y scrl_y# handle undo and redo for delete selectionself._set_unredo_delsel(a, b, text[a:b], from_undo)self.cancel_selection()def _set_unredo_delsel(self, a, b, substring, from_undo):# handle undo and redo for backspaceif from_undo:returnself._undo.append({undo_command: (delsel, a, substring),redo_command: (a, b)})# reset redo when undo is appended toself._redo []def _update_selection(self, finishedFalse):Update selection text and order of from/to if finished is True.Can be called multiple times until finished is True.a, b int(self._selection_from), int(self._selection_to)if a b:a, b b, aself._selection_finished finished_selection_text self.text[a:b]self.selection_text ( if not self.allow_copy else((self.password_mask * (b - a)) ifself.password else _selection_text))if not finished:self._selection Trueelse:self._selection bool(len(_selection_text))self._selection_touch Noneif a 0:# update graphics only on new line# allows smoother scrolling, noticeably# faster when dealing with large text.self._update_graphics_selection()# self._trigger_update_graphics()## Touch control#def long_touch(self, dt):self._long_touch_ev Noneif self._selection_to self._selection_from:pos self.to_local(*self._long_touch_pos, relativeFalse)self._show_cut_copy_paste(pos, EventLoop.window, modepaste)def on_double_tap(self):This event is dispatched when a double tap happensinside TextInput. The default behavior is to select theword around the current cursor position. Override this to providedifferent behavior. Alternatively, you can bind to thisevent to provide additional functionality.ci int(self.cursor_index())cc int(self.cursor_col)line self._lines[self.cursor_row]len_line len(line)start max(0, len(line[:cc]) - line[:cc].rfind(u ) - 1)end line[cc:].find(u )end end if end - 1 else (len_line - cc)Clock.schedule_once(lambda dt: self.select_text(ci - start, ci end))def on_triple_tap(self):This event is dispatched when a triple tap happensinside TextInput. The default behavior is to select theline around current cursor position. Override this to providedifferent behavior. Alternatively, you can bind to thisevent to provide additional functionality.ci self.cursor_index()sindex, eindex self._expand_range(ci)Clock.schedule_once(lambda dt: self.select_text(sindex, eindex))def on_quad_touch(self):This event is dispatched when four fingers are touchinginside TextInput. The default behavior is to select all text.Override this to provide different behavior. Alternatively,you can bind to this event to provide additional functionality.Clock.schedule_once(lambda dt: self.select_all())def on_touch_down(self, touch):if self.disabled:returntouch_pos touch.posif not self.collide_point(*touch_pos):return Falseif super(TextInput, self).on_touch_down(touch):return Trueif self.focus:self._trigger_cursor_reset()# Check for scroll wheelif button in touch.profile and touch.button.startswith(scroll):# TODO: implement scrollleft and scrollrightscroll_type touch.button[6:]if scroll_type down:if self.multiline:if self.scroll_y 0:self.scroll_y - self.line_heightself._trigger_update_graphics()else:if self.scroll_x 0:self.scroll_x - self.line_heightself._trigger_update_graphics()if scroll_type up:if self.multiline:viewport_height self.height\- self.padding[1] - self.padding[3]text_height len(self._lines) * (self.line_height self.line_spacing)if viewport_height text_height - self.scroll_y:self.scroll_y self.line_heightself._trigger_update_graphics()else:if (self.scroll_x self.width self._lines_rects[-1].texture.size[0]):self.scroll_x self.line_heightself._trigger_update_graphics()return Truetouch.grab(self)self._touch_count 1if touch.is_double_tap:self.dispatch(on_double_tap)if touch.is_triple_tap:self.dispatch(on_triple_tap)if self._touch_count 4:self.dispatch(on_quad_touch)self._hide_cut_copy_paste(EventLoop.window)# schedule long touch for pasteself._long_touch_pos touch.posself._long_touch_ev Clock.schedule_once(self.long_touch, .5)self.cursor self.get_cursor_from_xy(*touch_pos)if not self._selection_touch:self.cancel_selection()self._selection_touch touchself._selection_from self._selection_to self.cursor_index()self._update_selection()if CutBuffer and button in touch.profile and \touch.button middle:self.insert_text(CutBuffer.get_cutbuffer())return Truereturn Truedef on_touch_move(self, touch):if touch.grab_current is not self:returnif not self.focus:touch.ungrab(self)if self._selection_touch is touch:self._selection_touch Nonereturn Falseif self._selection_touch is touch:self.cursor self.get_cursor_from_xy(touch.x, touch.y)self._selection_to self.cursor_index()self._update_selection()return Truedef on_touch_up(self, touch):if touch.grab_current is not self:returntouch.ungrab(self)self._touch_count - 1# schedule long touch for pasteif self._long_touch_ev is not None:self._long_touch_ev.cancel()self._long_touch_ev Noneif not self.focus:return Falseif self._selection_touch is touch:self._selection_to self.cursor_index()self._update_selection(True)# show Bubblewin EventLoop.windowif self._selection_to ! self._selection_from:self._show_cut_copy_paste(touch.pos, win)elif self.use_handles:self._hide_handles()handle_middle self._handle_middleif handle_middle is None:self._handle_middle handle_middle Selector(sourceself.handle_image_middle,windowwin,targetself,size_hint(None, None),size(45dp, 45dp))handle_middle.bind(on_pressself._handle_pressed,on_touch_moveself._handle_move,on_releaseself._handle_released)if not self._handle_middle.parent and self.text:EventLoop.window.add_widget(handle_middle, canvasafter)self._position_handles(modemiddle)return Truedef _handle_pressed(self, instance):self._hide_cut_copy_paste()sf, st self._selection_from, self.selection_toif sf st:self._selection_from, self._selection_to st, sfdef _handle_released(self, instance):sf, st self._selection_from, self.selection_toif sf st:returnself._update_selection()self._show_cut_copy_paste((instance.right if instance is self._handle_left else instance.x,instance.top self.line_height),EventLoop.window)def _handle_move(self, instance, touch):if touch.grab_current ! instance:returnget_cursor self.get_cursor_from_xyhandle_right self._handle_righthandle_left self._handle_lefthandle_middle self._handle_middletry:touch.push()touch.apply_transform_2d(self.to_widget)x, y touch.posfinally:touch.pop()cursor get_cursor(x,y instance._touch_diff (self.line_height / 2))if instance ! touch.grab_current:returnif instance handle_middle:self.cursor cursorself._position_handles(modemiddle)returnci self.cursor_index(cursorcursor)sf, st self._selection_from, self.selection_toif instance handle_left:self._selection_from cielif instance handle_right:self._selection_to ciself._trigger_update_graphics()self._trigger_position_handles()def _position_handles(self, *args, **kwargs):if not self.text:returnmode kwargs.get(mode, both)lh self.line_heighthandle_middle self._handle_middleif handle_middle:hp_mid self.cursor_pospos self.to_local(*hp_mid, relativeTrue)handle_middle.x pos[0] - handle_middle.width / 2handle_middle.top pos[1] - lhif mode[0] m:returngroup self.canvas.get_group(selection)if not group:returnEventLoop.window.remove_widget(self._handle_middle)handle_left self._handle_leftif not handle_left:returnhp_left group[2].poshandle_left.pos self.to_local(*hp_left, relativeTrue)handle_left.x - handle_left.widthhandle_left.y - handle_left.heighthandle_right self._handle_rightlast_rect group[-1]hp_right last_rect.pos[0], last_rect.pos[1]x, y self.to_local(*hp_right, relativeTrue)handle_right.x x last_rect.size[0]handle_right.y y - handle_right.heightdef _hide_handles(self, winNone):win win or EventLoop.windowif win is None:returnwin.remove_widget(self._handle_right)win.remove_widget(self._handle_left)win.remove_widget(self._handle_middle)def _show_handles(self, dt):if not self.use_handles or not self.text:returnwin EventLoop.windowhandle_right self._handle_righthandle_left self._handle_leftif self._handle_left is None:self._handle_left handle_left Selector(sourceself.handle_image_left,targetself,windowwin,size_hint(None, None),size(45dp, 45dp))handle_left.bind(on_pressself._handle_pressed,on_touch_moveself._handle_move,on_releaseself._handle_released)self._handle_right handle_right Selector(sourceself.handle_image_right,targetself,windowwin,size_hint(None, None),size(45dp, 45dp))handle_right.bind(on_pressself._handle_pressed,on_touch_moveself._handle_move,on_releaseself._handle_released)else:if self._handle_left.parent:self._position_handles()returnif not self.parent:returnself._trigger_position_handles()if self.selection_from ! self.selection_to:self._handle_left.opacity self._handle_right.opacity 0win.add_widget(self._handle_left, canvasafter)win.add_widget(self._handle_right, canvasafter)anim Animation(opacity1, d.4)anim.start(self._handle_right)anim.start(self._handle_left)def _show_cut_copy_paste(self, pos, win, parent_changedFalse,mode, pos_in_windowFalse, *l):# Show a bubble with cut copy and paste buttonsif not self.use_bubble:returnbubble self._bubbleif bubble is None:self._bubble bubble TextInputCutCopyPaste(textinputself)self.fbind(parent, self._show_cut_copy_paste, pos, win, True)self.bind(focuslambda *args: self._hide_cut_copy_paste(win))self.bind(cursor_poslambda *args: self._hide_cut_copy_paste(win))else:win.remove_widget(bubble)if not self.parent:returnif parent_changed:return# Search the position from the touch to the windowlh, ls self.line_height, self.line_spacingx, y post_pos (x, y) if pos_in_window else self.to_window(x, y)bubble_size bubble.sizebubble_hw bubble_size[0] / 2.win_size win.sizebubble_pos (t_pos[0], t_pos[1] inch(.25))if (bubble_pos[0] - bubble_hw) 0:# bubble beyond left of windowif bubble_pos[1] (win_size[1] - bubble_size[1]):# bubble above window heightbubble_pos (bubble_hw, (t_pos[1]) - (lh ls inch(.25)))bubble.arrow_pos top_leftelse:bubble_pos (bubble_hw, bubble_pos[1])bubble.arrow_pos bottom_leftelif (bubble_pos[0] bubble_hw) win_size[0]:# bubble beyond right of windowif bubble_pos[1] (win_size[1] - bubble_size[1]):# bubble above window heightbubble_pos (win_size[0] - bubble_hw,(t_pos[1]) - (lh ls inch(.25)))bubble.arrow_pos top_rightelse:bubble_pos (win_size[0] - bubble_hw, bubble_pos[1])bubble.arrow_pos bottom_rightelse:if bubble_pos[1] (win_size[1] - bubble_size[1]):# bubble above window heightbubble_pos (bubble_pos[0],(t_pos[1]) - (lh ls inch(.25)))bubble.arrow_pos top_midelse:bubble.arrow_pos bottom_midbubble_pos self.to_widget(*bubble_pos, relativeTrue)bubble.center_x bubble_pos[0]if bubble.arrow_pos[0] t:bubble.top bubble_pos[1]else:bubble.y bubble_pos[1]bubble.mode modeAnimation.cancel_all(bubble)bubble.opacity 0win.add_widget(bubble, canvasafter)Animation(opacity1, d.225).start(bubble)def _hide_cut_copy_paste(self, winNone):bubble self._bubbleif not bubble:returnbubble.hide()## Private#staticmethoddef _reload_remove_observer(wr):# called when the textinput is deletedif wr in _textinput_list:_textinput_list.remove(wr)def _on_textinput_focused(self, instance, value, *largs):win EventLoop.windowself.cancel_selection()self._hide_cut_copy_paste(win)if value:if (not (self.readonly or self.disabled) or _is_desktop andself._keyboard_mode system):self._trigger_cursor_reset()self._editable Trueelse:self._editable Falseelse:self._do_blink_cursor_ev.cancel()self._hide_handles(win)def _ensure_clipboard(self):global Clipboard, CutBufferif not Clipboard:from kivy.core.clipboard import Clipboard, CutBufferdef cut(self): Copy current selection to clipboard then delete it from TextInput... versionadded:: 1.8.0self._cut(self.selection_text)def _cut(self, data):self._ensure_clipboard()Clipboard.copy(data)self.delete_selection()def copy(self, data): Copy the value provided in argument data into current clipboard.If data is not of type string it will be converted to string.If no data is provided then current selection if present is copied... versionadded:: 1.8.0self._ensure_clipboard()if data:return Clipboard.copy(data)if self.selection_text:return Clipboard.copy(self.selection_text)def paste(self): Insert text from system :class:~kivy.core.clipboard.Clipboardinto the :class:~kivy.uix.textinput.TextInput at current cursorposition... versionadded:: 1.8.0self._ensure_clipboard()data Clipboard.paste()self.delete_selection()self.insert_text(data)def _update_cutbuffer(self, *args):CutBuffer.set_cutbuffer(self.selection_text)def _get_text_width(self, text, tab_width, _label_cached):# Return the width of a text, according to the current line optionskw self._get_line_options()try:cid u{}\0{}\0{}.format(text, self.password, kw)except UnicodeDecodeError:cid {}\0{}\0{}.format(text, self.password, kw)width Cache_get(textinput.width, cid)if width:return widthif not _label_cached:_label_cached self._label_cachedtext text.replace(\t, * tab_width)if not self.password:width _label_cached.get_extents(text)[0]else:width _label_cached.get_extents(self.password_mask * len(text))[0]Cache_append(textinput.width, cid, width)return widthdef on_cursor_blink(self, instance, value):# trigger blink event reset to switch blinking while focusedself._reset_cursor_blink()def _do_blink_cursor(self, dt):if not self.cursor_blink:# ignore event if not triggered,# stop if cursor_blink value changed right nowif self._do_blink_cursor_ev.is_triggered:self._do_blink_cursor_ev.cancel()# dont blink, make cursor visibleself._cursor_blink Falsereturn# Callback for blinking the cursor.self._cursor_blink not self._cursor_blinkdef _reset_cursor_blink(self, *args):self._do_blink_cursor_ev.cancel()self._cursor_blink Falseself._do_blink_cursor_ev()def on_cursor(self, instance, value):# When the cursor is moved, reset cursor blinking to keep it showing,# and update all the graphics.if self.focus:self._trigger_cursor_reset()self._trigger_update_graphics()def _delete_line(self, idx):# Delete current line, and fix cursor positionassert(idx len(self._lines))self._lines_flags.pop(idx)self._lines_labels.pop(idx)self._lines.pop(idx)self.cursor self.cursordef _set_line_text(self, line_num, text):# Set current line with other text than the default one.self._lines_labels[line_num] self._create_line_label(text)self._lines[line_num] textdef _trigger_refresh_line_options(self, *largs):if self._refresh_line_options_ev is not None:self._refresh_line_options_ev.cancel()else:self._refresh_line_options_ev Clock.create_trigger(self._refresh_line_options, 0)self._refresh_line_options_ev()def _refresh_line_options(self, *largs):self._line_options Noneself._get_line_options()self._refresh_text_from_property()self._refresh_hint_text()self.cursor self.get_cursor_from_index(len(self.text))def _trigger_refresh_text(self, *largs):if len(largs) and largs[0] self:largs ()if self._refresh_text_from_property_ev is not None:self._refresh_text_from_property_ev.cancel()self._refresh_text_from_property_ev Clock.schedule_once(lambda dt: self._refresh_text_from_property(*largs))def _update_text_options(self, *largs):Cache_remove(textinput.width)self._trigger_refresh_text()def _refresh_text_from_trigger(self, dt, *largs):self._refresh_text_from_property(*largs)def _refresh_text_from_property(self, *largs):self._refresh_text(self.text, *largs)def _refresh_text(self, text, *largs):# Refresh all the lines from a new text.# By using cache in internal functions, this method should be fast.mode allif len(largs) 1:mode, start, finish, _lines, _lines_flags, len_lines largs# start max(0, start)cursor Noneelse:cursor self.cursor_index()_lines, self._lines_flags self._split_smart(text)_lines_labels []_line_rects []_create_label self._create_line_labelfor x in _lines:lbl _create_label(x)_lines_labels.append(lbl)_line_rects.append(Rectangle(sizelbl.size))if mode all:self._lines_labels _lines_labelsself._lines_rects _line_rectsself._lines[:] _lineselif mode del:if finish start: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,finish if (start finish and not len_lines)else (finish 1),len_lines, _lines_flags, _lines, _lines_labels,_line_rects)min_line_ht self._label_cached.get_extents(_)[1]# with markup texture can be of height 1self.line_height max(_lines_labels[0].height, min_line_ht)# self.line_spacing 2# now, if the text change, maybe the cursor is not at the same place as# before. so, try to set the cursor on the good placerow self.cursor_rowself.cursor self.get_cursor_from_index(self.cursor_index()if cursor is None else cursor)# if we back to a new line, reset the scroll, otherwise, the effect is# uglyif self.cursor_row ! row:self.scroll_x 0# with the new text dont forget to update graphics againself._trigger_update_graphics()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])if len_lines:# if not inserting at first line thenif start:# make sure line flags restored for first line# _split_smart assumes first line to be not a new line_lines_flags[0] self_lines_flags[start]_lins_flags.extend(_lines_flags)_lins_flags.extend(self_lines_flags[finish:])self._lines_flags _lins_flags_lins_lbls []_lins_lbls.extend(self._lines_labels[:start])if len_lines:_lins_lbls.extend(_lines_labels)_lins_lbls.extend(self._lines_labels[finish:])self._lines_labels _lins_lbls_lins_rcts []_lins_rcts.extend(self._lines_rects[:start])if len_lines:_lins_rcts.extend(_line_rects)_lins_rcts.extend(self._lines_rects[finish:])self._lines_rects _lins_rcts_lins []_lins.extend(self._lines[:start])if len_lines:_lins.extend(_lines)_lins.extend(self._lines[finish:])self._lines[:] _linsdef _trigger_update_graphics(self, *largs):self._update_graphics_ev.cancel()self._update_graphics_ev()def _update_graphics(self, *largs):# Update all the graphics according to the current internal values.## This is a little bit complex, cause we have to :# - handle scroll_x# - handle padding# - create rectangle for the lines matching the viewport# - crop the texture coordinates to match the viewport## This is the first step of graphics, the second is the selection.self.canvas.clear()add self.canvas.addlh self.line_heightdy lh self.line_spacing# adjust view if the cursor is going outside the boundssx self.scroll_xsy self.scroll_y# draw labelsif not self._lines or (not self._lines[0] and len(self._lines) 1):rects self._hint_text_rectslabels self._hint_text_labelslines self._hint_text_lineselse:rects self._lines_rectslabels self._lines_labelslines self._linespadding_left, padding_top, padding_right, padding_bottom self.paddingx self.x padding_lefty self.top - padding_top syminy self.y padding_bottommaxy self.top - padding_tophalign self.halignbase_dir self.base_directionfind_base_dir Label.find_base_directionauto_halign_r halign auto and base_dir and rtl in base_dirfst_visible_ln Nonefor line_num, value in enumerate(lines):if miny y maxy dy:if fst_visible_ln is None:fst_visible_ln line_numtexture labels[line_num]size list(texture.size)texc texture.tex_coords[:]# calcul coordinateviewport_pos sx, 0vw self.width - padding_left - padding_rightvh self.height - padding_top - padding_bottomtw, th list(map(float, size))oh, ow tch, tcw texc[1:3]tcx, tcy 0, 0# adjust size/texcoord according to viewportif viewport_pos:tcx, tcy viewport_postcx tcx / tw * (ow)tcy tcy / th * ohif tw - viewport_pos[0] vw:tcw tcw - tcxsize[0] tcw * size[0]elif vw tw:tcw (vw / tw) * tcwsize[0] vwif vh th:tch (vh / th) * tchsize[1] vh# croppingmlh lhif y maxy:vh (maxy - y lh)tch (vh / float(lh)) * ohtcy oh - tchsize[1] vhif y - lh miny:diff miny - (y - lh)y diffvh lh - difftch (vh / float(lh)) * ohsize[1] vhtexc (tcx,tcy tch,tcx tcw,tcy tch,tcx tcw,tcy,tcx,tcy)# Horizontal alignmentxoffset 0if not base_dir:base_dir self._resolved_base_dir find_base_dir(value)if base_dir and halign auto:auto_halign_r rtl in base_dirif halign center:xoffset int((vw - size[0]) / 2.)elif halign right or auto_halign_r:xoffset max(0, int(vw - size[0]))# add rectangle.r rects[line_num]r.pos int(xoffset x), int(y - mlh)r.size sizer.texture texturer.tex_coords texcadd(r)elif y miny:line_num - 1breaky - dyif fst_visible_ln is not None:self._visible_lines_range (fst_visible_ln, line_num 1)else:self._visible_lines_range 0, 0self._update_graphics_selection()def _update_graphics_selection(self):if not self._selection:returnself.canvas.remove_group(selection)dy self.line_height self.line_spacingrects self._lines_rectspadding_top self.padding[1]padding_bottom self.padding[3]_top self.topy _top - padding_top self.scroll_yminy self.y padding_bottommaxy _top - padding_topdraw_selection self._draw_selectiona, b self._selection_from, self._selection_toif a b:a, b b, aget_cursor_from_index self.get_cursor_from_indexs1c, s1r get_cursor_from_index(a)s2c, s2r get_cursor_from_index(b)s2r 1# pass only the selection lines[]# passing all the lines can get slow when dealing with a lot of texty - s1r * dy_lines self._lines_get_text_width self._get_text_widthtab_width self.tab_width_label_cached self._label_cachedwidth self.widthpadding_left self.padding[0]padding_right self.padding[2]x self.xcanvas_add self.canvas.addselection_color self.selection_colorfor line_num, value in enumerate(_lines[s1r:s2r], starts1r):if miny y maxy dy:r rects[line_num]draw_selection(r.pos, r.size, line_num, (s1c, s1r),(s2c, s2r - 1), _lines, _get_text_width,tab_width, _label_cached, width,padding_left, padding_right, x,canvas_add, selection_color)y - dyself._position_handles(both)def _draw_selection(self, *largs):pos, size, line_num, (s1c, s1r), (s2c, s2r),\_lines, _get_text_width, tab_width, _label_cached, width,\padding_left, padding_right, x, canvas_add, selection_color largs# Draw the current selection on the widget.if line_num s1r or line_num s2r:returnx, y posw, h sizex1 xx2 x wif line_num s1r:lines _lines[line_num]x1 - self.scroll_xx1 _get_text_width(lines[:s1c], tab_width, _label_cached)if line_num s2r:lines _lines[line_num]x2 (x - self.scroll_x) _get_text_width(lines[:s2c],tab_width,_label_cached)width_minus_padding width - (padding_right padding_left)maxx x width_minus_paddingif x1 maxx:returnx1 max(x1, x)x2 min(x2, x width_minus_padding)canvas_add(Color(*selection_color, groupselection))canvas_add(Rectangle(pos(x1, pos[1]), size(x2 - x1, size[1]), groupselection))def on_size(self, instance, value):# if the size change, we might do invalid scrolling / text split# size the text maybe be put after size_hint have been resolved.self._trigger_refresh_text()self._refresh_hint_text()self.scroll_x self.scroll_y 0def _get_row_width(self, row):# Get the pixel width of the given row._labels self._lines_labelsif row len(_labels):return _labels[row].widthreturn 0def _get_cursor_pos(self):# return the current cursor x/y from the row/coldy self.line_height self.line_spacingpadding_left self.padding[0]padding_top self.padding[1]padding_right self.padding[2]left self.x padding_lefttop self.top - padding_topy top self.scroll_yy - self.cursor_row * dy# Horizontal alignmenthalign self.halignviewport_width self.width - padding_left - padding_rightcursor_offset self.cursor_offset()base_dir self.base_direction or self._resolved_base_dirauto_halign_r halign auto and base_dir and rtl in base_dirif halign center:row_width self._get_row_width(self.cursor_row)x left int((viewport_width - row_width) / 2) \ cursor_offset - self.scroll_xelif halign right or auto_halign_r:row_width self._get_row_width(self.cursor_row)x left viewport_width - row_width \ cursor_offset - self.scroll_xelse:x left cursor_offset - self.scroll_xreturn x, ydef _get_cursor_visual_height(self):# Return the height of the cursors visible part_, cy map(int, self.cursor_pos)max_y self.top - self.padding[1]min_y self.y self.padding[3]lh self.line_heightif cy max_y:return lh - min(lh, cy - max_y)else:return min(lh, max(0, cy - min_y))def _get_cursor_visual_pos(self):# Return the position of the cursors top visible pointcx, cy map(int, self.cursor_pos)max_y self.top - self.padding[3]return [cx, min(max_y, cy)]def _get_line_options(self):# Get or create line options, to be used for Label creationif self._line_options is None:self._line_options kw {font_size: self.font_size,font_name: self.font_name,font_context: self.font_context,font_family: self.font_family,text_language: self.text_language,base_direction: self.base_direction,anchor_x: left,anchor_y: top,padding_x: 0,padding_y: 0,padding: (0, 0)}self._label_cached Label(**kw)return self._line_optionsdef _create_line_label(self, text, hintFalse):# Create a label from a text, using line optionsntext text.replace(u\n, u).replace(u\t, u * self.tab_width)if self.password and not hint: # Dont replace hint_text with *ntext self.password_mask * len(ntext)kw self._get_line_options()cid %s\0%s % (ntext, str(kw))texture Cache_get(textinput.label, cid)if texture is None:# FIXME right now, we cant render very long line...# if we move on VBO version as fallback, we wont need to# do this. try to found the maximum text we can handlelabel Nonelabel_len len(ntext)ld None# check for blank lineif not ntext:texture Texture.create(size(1, 1))Cache_append(textinput.label, cid, texture)return texturewhile True:try:label Label(textntext[:label_len], **kw)label.refresh()if ld is not None and ld 2:ld int(ld / 2)label_len ldelse:breakexcept:# exception happen when we tried to render the text# reduce it...if ld is None:ld len(ntext)ld int(ld / 2)if ld 2 and label_len:label_len - 1label_len - ldcontinue# ok, we found it.texture label.textureCache_append(textinput.label, cid, texture)return texturedef _tokenize(self, text):# Tokenize a text string from some delimitersif text is None:returndelimiters u ,\.;:\n\r\toldindex 0for index, char in enumerate(text):if char not in delimiters:continueif oldindex ! index:yield text[oldindex:index]yield text[index:index 1]oldindex index 1yield text[oldindex:]def _split_smart(self, text):# Do a smart split. If autowidth or autosize is set,# we are not doing smart split, just a split on line break.# Otherwise, we are trying to split as soon as possible, to prevent# overflow on the widget.# depend of the options, split the text on line, or wordif not self.multiline:lines text.split(u\n)lines_flags [0] [FL_IS_LINEBREAK] * (len(lines) - 1)return lines, lines_flags# no autosize, do wordwrap.x flags 0line []lines []lines_flags []_join u.joinlines_append, lines_flags_append lines.append, lines_flags.appendpadding_left self.padding[0]padding_right self.padding[2]width self.width - padding_left - padding_righttext_width self._get_text_width_tab_width, _label_cached self.tab_width, self._label_cached# try to add each word on current line.for word in self._tokenize(text):is_newline (word u\n)w text_width(word, _tab_width, _label_cached)# if we have more than the width, or if its a newline,# push the current line, and create a new oneif (x w width and line) or is_newline:lines_append(_join(line))lines_flags_append(flags)flags 0line []x 0if is_newline:flags | FL_IS_LINEBREAKelif width 1 and w width:while w width:split_width split_pos 0# split the wordfor c in word:cw self._get_text_width(c, self.tab_width, self._label_cached)if split_width cw width:breaksplit_width cwsplit_pos 1if split_width split_pos 0:# cant fit the word in, give upbreaklines_append(word[:split_pos])lines_flags_append(flags)flags FL_IS_WORDBREAKword word[split_pos:]w - split_widthx wline.append(word)else:x wline.append(word)if line or flags FL_IS_LINEBREAK:lines_append(_join(line))lines_flags_append(flags)return lines, lines_flagsdef _key_down(self, key, repeatFalse):displayed_str, internal_str, internal_action, scale key# handle deletionif (self._selection andinternal_action in (None, del, backspace, enter)):if internal_action ! enter or self.multiline:self.delete_selection()elif internal_action del:# Move cursor one char to the right. If that was successful,# do a backspace (effectively deleting char right of cursor)cursor self.cursorself.do_cursor_movement(cursor_right)if cursor ! self.cursor:self.do_backspace(modedel)elif internal_action backspace:self.do_backspace()# handle action keys and text insertionif internal_action is None:self.insert_text(displayed_str)elif internal_action in (shift, shift_L, shift_R):if not self._selection:self._selection_from self._selection_to self.cursor_index()self._selection Trueself._selection_finished Falseelif internal_action ctrl_L:self._ctrl_l Trueelif internal_action ctrl_R:self._ctrl_r Trueelif internal_action alt_L:self._alt_l Trueelif internal_action alt_R:self._alt_r Trueelif internal_action.startswith(cursor_):cc, cr self.cursorself.do_cursor_movement(internal_action,self._ctrl_l or self._ctrl_r,self._alt_l or self._alt_r)if self._selection and not self._selection_finished:self._selection_to self.cursor_index()self._update_selection()else:self.cancel_selection()elif internal_action enter:if self.multiline:self.insert_text(u\n)else:self.dispatch(on_text_validate)if self.text_validate_unfocus:self.focus Falseelif internal_action escape:self.focus Falsedef _key_up(self, key, repeatFalse):displayed_str, internal_str, internal_action, scale keyif internal_action in (shift, shift_L, shift_R):if self._selection:self._update_selection(True)elif internal_action ctrl_L:self._ctrl_l Falseelif internal_action ctrl_R:self._ctrl_r Falseelif internal_action alt_L:self._alt_l Falseelif internal_action alt_R:self._alt_r Falsedef keyboard_on_key_down(self, window, keycode, text, modifiers):# Keycodes on OS X:ctrl, cmd 64, 1024key, key_str keycodewin EventLoop.window# This allows *either* ctrl *or* cmd, but not both.modifiers set(modifiers) - {capslock, numlock}is_shortcut (modifiers {ctrl} or (_is_osx and modifiers {meta}))is_interesting_key key in (list(self.interesting_keys.keys()) [27])if not self.write_tab and super(TextInput,self).keyboard_on_key_down(window, keycode, text, modifiers):return Trueif not self._editable:# duplicated but faster testing for non-editable keysif text and not is_interesting_key:if is_shortcut and key ord(c):self.copy()elif key 27:self.focus Falsereturn Trueif text and not is_interesting_key:self._hide_handles(win)self._hide_cut_copy_paste(win)win.remove_widget(self._handle_middle)# check for command modes# we use \x01INFO\x02 to get info from IME on mobiles# pygame seems to pass \x01 as the unicode for ctrla# checking for modifiers ensures conflict resolution.first_char ord(text[0])if not modifiers and first_char 1:self._command_mode Trueself._command if not modifiers and first_char 2:self._command_mode Falseself._command self._command[1:]if self._command_mode:self._command textreturn_command self._commandif _command and first_char 2:from_undo True_command, data _command.split(:)self._command if self._selection:self.delete_selection()if _command DEL:count int(data)if not count:self.delete_selection(from_undoTrue)end self.cursor_index()self._selection_from max(end - count, 0)self._selection_to endself._selection Trueself.delete_selection(from_undoTrue)returnelif _command INSERT:self.insert_text(data, from_undo)elif _command INSERTN:from_undo Falseself.insert_text(data, from_undo)elif _command SELWORD:self.dispatch(on_double_tap)elif _command SEL:if data 0:Clock.schedule_once(lambda dt: self.cancel_selection())elif _command CURCOL:self.cursor int(data), self.cursor_rowreturnif is_shortcut:if key ord(x): # cut selectionself._cut(self.selection_text)elif key ord(c): # copy selectionself.copy()elif key ord(v): # paste clipboard contentself.paste()elif key ord(a): # select allself.select_all()elif key ord(z): # undoself.do_undo()elif key ord(r): # redoself.do_redo()else:is_sdl2 (EventLoop.window.__class__.__module__ kivy.core.window.window_sdl2)if is_sdl2:# we expect to get managed key input via on_textinputreturnif self._selection:self.delete_selection()self.insert_text(text)# self._recalc_size()returnif is_interesting_key:self._hide_cut_copy_paste(win)self._hide_handles(win)if key 27: # escapeself.focus Falsereturn Trueelif key 9: # tabself.insert_text(u\t)return Truek self.interesting_keys.get(key)if k:key (None, None, k, 1)self._key_down(key)def keyboard_on_key_up(self, window, keycode):key, key_str keycodek self.interesting_keys.get(key)if k:key (None, None, k, 1)self._key_up(key)def keyboard_on_textinput(self, window, text):if self._selection:self.delete_selection()self.insert_text(text, False)# current IME composition in progress by the IME system, or if nothing_ime_composition StringProperty()# cursor position of last IME event_ime_cursor ListProperty(None, allownoneTrue)def _bind_keyboard(self):super()._bind_keyboard()Window.bind(on_texteditself.window_on_textedit)def _unbind_keyboard(self):super()._unbind_keyboard()Window.unbind(on_texteditself.window_on_textedit)def window_on_textedit(self, window, ime_input):text_lines self._lines or []if self._ime_composition:pcc, pcr self._ime_cursortext text_lines[pcr]len_ime len(self._ime_composition)if text[pcc - len_ime:pcc] self._ime_composition: # always?remove_old_ime_text text[:pcc - len_ime] text[pcc:]ci self.cursor_index()self._refresh_text_from_property(insert,*self._get_line_from_cursor(pcr, remove_old_ime_text))self.cursor self.get_cursor_from_index(ci - len_ime)if ime_input:if self._selection:self.delete_selection()cc, cr self.cursortext text_lines[cr]new_text text[:cc] ime_input text[cc:]self._refresh_text_from_property(insert, *self._get_line_from_cursor(cr, new_text))self.cursor self.get_cursor_from_index(self.cursor_index() len(ime_input))self._ime_composition ime_inputself._ime_cursor self.cursordef on__hint_text(self, instance, value):self._refresh_hint_text()def _refresh_hint_text(self):_lines, self._hint_text_flags self._split_smart(self.hint_text)_hint_text_labels []_hint_text_rects []_create_label self._create_line_labelfor x in _lines:lbl _create_label(x, hintTrue)_hint_text_labels.append(lbl)_hint_text_rects.append(Rectangle(sizelbl.size))self._hint_text_lines[:] _linesself._hint_text_labels _hint_text_labelsself._hint_text_rects _hint_text_rects# Remember to update graphicsself._trigger_update_graphics()## Properties#_lines ListProperty([])_hint_text_lines ListProperty([])_editable BooleanProperty(True)_insert_int_pat re.compile(u^-?[0-9]*$)_insert_float_pat re.compile(u^-?[0-9]*\\.?[0-9]*$)_cursor_blink BooleanProperty(False)_cursor_visual_pos AliasProperty(_get_cursor_visual_pos, None, bind[cursor_pos])_cursor_visual_height AliasProperty(_get_cursor_visual_height, None, bind[cursor_pos])readonly BooleanProperty(False)If True, the user will not be able to change the content of a textinput... versionadded:: 1.3.0:attr:readonly is a :class:~kivy.properties.BooleanProperty anddefaults to False.text_validate_unfocus BooleanProperty(True)If True, the :meth:TextInput.on_text_validate event will unfocus thewidget, therefore make it stop listening to the keyboard. When disabled,the :meth:TextInput.on_text_validate event can be fired multiple timesas the result of TextInput keeping the focus enabled... versionadded:: 1.10.1:attr:text_validate_unfocus isa :class:~kivy.properties.BooleanProperty and defaults to True.multiline BooleanProperty(True)If True, the widget will be able show multiple lines of text. If False,the enter keypress will defocus the textinput instead of adding a newline.:attr:multiline is a :class:~kivy.properties.BooleanProperty anddefaults to True.password BooleanProperty(False)If True, the widget will display its characters as the characterset in :attr:password_mask... versionadded:: 1.2.0:attr:password is a :class:~kivy.properties.BooleanProperty anddefaults to False.password_mask StringProperty(*)Sets the character used to mask the text when :attr:password is True... versionadded:: 1.10.0:attr:password_mask is a :class:~kivy.properties.StringProperty anddefaults to *.keyboard_suggestions BooleanProperty(True)If True provides auto suggestions on top of keyboard.This will only work if :attr:input_type is set to text... versionadded:: 1.8.0:attr:keyboard_suggestions is a :class:~kivy.properties.BooleanPropertyand defaults to True.cursor_blink BooleanProperty(True)This property is used to set whether the graphic cursor should blinkor not... versionchanged:: 1.10.1cursor_blink has been refactored to enable switching the blinkingon/off and the previous behavior has been moved to a private_cursor_blink property. The previous default value False has beenchanged to True.:attr:cursor_blink is a :class:~kivy.properties.BooleanProperty anddefaults to True.def _get_cursor(self):return self._cursordef _set_cursor(self, pos):if not self._lines:self._trigger_refresh_text()returnl self._linescr boundary(pos[1], 0, len(l) - 1)cc boundary(pos[0], 0, len(l[cr]))cursor cc, cr# adjust scrollview to ensure that the cursor will be always inside our# viewport.padding_left self.padding[0]padding_right self.padding[2]viewport_width self.width - padding_left - padding_rightsx self.scroll_xoffset self.cursor_offset()# if offset is outside the current bounds, readjustif offset viewport_width sx:self.scroll_x offset - viewport_widthif offset sx:self.scroll_x offset# do the same for Y# this algo try to center the cursor as much as possibledy self.line_height self.line_spacingoffsety cr * dysy self.scroll_ypadding_top self.padding[1]padding_bottom self.padding[3]viewport_height self.height - padding_top - padding_bottom - dyif offsety viewport_height sy:sy offsety - viewport_heightif offsety sy:sy offsetyself.scroll_y syif self._cursor cursor:returnself._cursor cursorreturn Truecursor AliasProperty(_get_cursor, _set_cursor)Tuple of (col, row) values indicating the current cursor position.You can set a new (col, row) if you want to move the cursor. The scrollingarea will be automatically updated to ensure that the cursor isvisible inside the viewport.:attr:cursor is an :class:~kivy.properties.AliasProperty.def _get_cursor_col(self):return self._cursor[0]cursor_col AliasProperty(_get_cursor_col, None, bind(cursor, ))Current column of the cursor.:attr:cursor_col is an :class:~kivy.properties.AliasProperty tocursor[0], read-only.def _get_cursor_row(self):return self._cursor[1]cursor_row AliasProperty(_get_cursor_row, None, bind(cursor, ))Current row of the cursor.:attr:cursor_row is an :class:~kivy.properties.AliasProperty tocursor[1], read-only.cursor_pos AliasProperty(_get_cursor_pos, None,bind(cursor, padding, pos, size,focus, scroll_x, scroll_y,line_height, line_spacing),cacheFalse)Current position of the cursor, in (x, y).:attr:cursor_pos is an :class:~kivy.properties.AliasProperty,read-only.cursor_color ColorProperty([1, 0, 0, 1])Current color of the cursor, in (r, g, b, a) format... versionadded:: 1.9.0:attr:cursor_color is a :class:~kivy.properties.ColorProperty anddefaults to [1, 0, 0, 1]... versionchanged:: 2.0.0Changed from :class:~kivy.properties.ListProperty to:class:~kivy.properties.ColorProperty.cursor_width NumericProperty(1sp)Current width of the cursor... versionadded:: 1.10.0:attr:cursor_width is a :class:~kivy.properties.NumericProperty anddefaults to 1sp.line_height NumericProperty(1)Height of a line. This property is automatically computed from the:attr:font_name, :attr:font_size. Changing the line_height will haveno impact... note:::attr:line_height is the height of a single line of text.Use :attr:minimum_height, which also includes padding, toget the height required to display the text properly.:attr:line_height is a :class:~kivy.properties.NumericProperty,read-only.tab_width NumericProperty(4)By default, each tab will be replaced by four spaces on the textinput widget. You can set a lower or higher value.:attr:tab_width is a :class:~kivy.properties.NumericProperty anddefaults to 4.padding_x VariableListProperty([0, 0], length2, deprecatedTrue)Horizontal padding of the text: [padding_left, padding_right].padding_x also accepts a one argument form [padding_horizontal].:attr:padding_x is a :class:~kivy.properties.VariableListProperty anddefaults to [0, 0]. This might be changed by the current theme... deprecated:: 1.7.0Use :attr:padding instead.def on_padding_x(self, instance, value):self.padding[0] value[0]self.padding[2] value[1]padding_y VariableListProperty([0, 0], length2, deprecatedTrue)Vertical padding of the text: [padding_top, padding_bottom].padding_y also accepts a one argument form [padding_vertical].:attr:padding_y is a :class:~kivy.properties.VariableListProperty anddefaults to [0, 0]. This might be changed by the current theme... deprecated:: 1.7.0Use :attr:padding instead.def on_padding_y(self, instance, value):self.padding[1] value[0]self.padding[3] value[1]padding VariableListProperty([6, 6, 6, 6])Padding of the text: [padding_left, padding_top, padding_right,padding_bottom].padding also accepts a two argument form [padding_horizontal,padding_vertical] and a one argument form [padding]... versionchanged:: 1.7.0Replaced AliasProperty with VariableListProperty.:attr:padding is a :class:~kivy.properties.VariableListProperty anddefaults to [6, 6, 6, 6].halign OptionProperty(auto, options[left, center, right,auto])Horizontal alignment of the text.:attr:halign is an :class:~kivy.properties.OptionProperty anddefaults to auto. Available options are : auto, left, center and right.Auto will attempt to autodetect horizontal alignment for RTL text (Pangoonly), otherwise it behaves like left... versionadded:: 1.10.1scroll_x NumericProperty(0)X scrolling value of the viewport. The scrolling is automaticallyupdated when the cursor is moved or text changed. If there is nouser input, the scroll_x and scroll_y properties may be changed.:attr:scroll_x is a :class:~kivy.properties.NumericProperty anddefaults to 0.scroll_y NumericProperty(0)Y scrolling value of the viewport. See :attr:scroll_x for moreinformation.:attr:scroll_y is a :class:~kivy.properties.NumericProperty anddefaults to 0.selection_color ColorProperty([0.1843, 0.6549, 0.8313, .5])Current color of the selection, in (r, g, b, a) format... warning::The color should always have an alpha component less than 1since the selection is drawn after the text.:attr:selection_color is a :class:~kivy.properties.ColorProperty anddefaults to [0.1843, 0.6549, 0.8313, .5]... versionchanged:: 2.0.0Changed from :class:~kivy.properties.ListProperty to:class:~kivy.properties.ColorProperty.border ListProperty([4, 4, 4, 4])Border used for :class:~kivy.graphics.vertex_instructions.BorderImagegraphics instruction. Used with :attr:background_normal and:attr:background_active. Can be used for a custom background... versionadded:: 1.4.1It must be a list of four values: (bottom, right, top, left). Read theBorderImage instruction for more information about how to use it.:attr:border is a :class:~kivy.properties.ListProperty and defaultsto (4, 4, 4, 4).background_normal StringProperty(atlas://data/images/defaulttheme/textinput)Background image of the TextInput when its not in focus... versionadded:: 1.4.1:attr:background_normal is a :class:~kivy.properties.StringProperty anddefaults to atlas://data/images/defaulttheme/textinput.background_disabled_normal StringProperty(atlas://data/images/defaulttheme/textinput_disabled)Background image of the TextInput when disabled... versionadded:: 1.8.0:attr:background_disabled_normal is a:class:~kivy.properties.StringProperty anddefaults to atlas://data/images/defaulttheme/textinput_disabled.background_active StringProperty(atlas://data/images/defaulttheme/textinput_active)Background image of the TextInput when its in focus... versionadded:: 1.4.1:attr:background_active is a:class:~kivy.properties.StringProperty anddefaults to atlas://data/images/defaulttheme/textinput_active.background_color ColorProperty([1, 1, 1, 1])Current color of the background, in (r, g, b, a) format... versionadded:: 1.2.0:attr:background_color is a :class:~kivy.properties.ColorPropertyand defaults to [1, 1, 1, 1] (white)... versionchanged:: 2.0.0Changed from :class:~kivy.properties.ListProperty to:class:~kivy.properties.ColorProperty.foreground_color ColorProperty([0, 0, 0, 1])Current color of the foreground, in (r, g, b, a) format... versionadded:: 1.2.0:attr:foreground_color is a :class:~kivy.properties.ColorPropertyand defaults to [0, 0, 0, 1] (black)... versionchanged:: 2.0.0Changed from :class:~kivy.properties.ListProperty to:class:~kivy.properties.ColorProperty.disabled_foreground_color ColorProperty([0, 0, 0, .5])Current color of the foreground when disabled, in (r, g, b, a) format... versionadded:: 1.8.0:attr:disabled_foreground_color is a:class:~kivy.properties.ColorProperty anddefaults to [0, 0, 0, 5] (50% transparent black)... versionchanged:: 2.0.0Changed from :class:~kivy.properties.ListProperty to:class:~kivy.properties.ColorProperty.use_bubble BooleanProperty(not _is_desktop)Indicates whether the cut/copy/paste bubble is used... versionadded:: 1.7.0:attr:use_bubble is a :class:~kivy.properties.BooleanPropertyand defaults to True on mobile OSs, False on desktop OSs.use_handles BooleanProperty(not _is_desktop)Indicates whether the selection handles are displayed... versionadded:: 1.8.0:attr:use_handles is a :class:~kivy.properties.BooleanPropertyand defaults to True on mobile OSs, False on desktop OSs.suggestion_text StringProperty()Shows a suggestion text at the end of the current line.The feature is useful for text autocompletion, and it does not implementvalidation (accepting the suggested text on enter etc.).This can also be used by the IME to setup the current word being edited... versionadded:: 1.9.0:attr:suggestion_text is a :class:~kivy.properties.StringProperty anddefaults to .def on_suggestion_text(self, instance, value):global MarkupLabelif not MarkupLabel:from kivy.core.text.markup import MarkupLabelcursor_row self.cursor_rowif cursor_row len(self._lines) or self.canvas is None:returncursor_pos self.cursor_postxt self._lines[cursor_row]kw self._get_line_options()rct self._lines_rects[cursor_row]lbl text Noneif value:lbl MarkupLabel(texttxt [b]{}[/b].format(value), **kw)else:lbl Label(**kw)text txtlbl.refresh()self._lines_labels[cursor_row] lbl.texturerct.size lbl.sizeself._update_graphics()def get_sel_from(self):return self._selection_fromselection_from AliasProperty(get_sel_from, None)If a selection is in progress or complete, this property will representthe cursor index where the selection started... versionchanged:: 1.4.0:attr:selection_from is an :class:~kivy.properties.AliasPropertyand defaults to None, readonly.def get_sel_to(self):return self._selection_toselection_to AliasProperty(get_sel_to, None)If a selection is in progress or complete, this property will representthe cursor index where the selection started... versionchanged:: 1.4.0:attr:selection_to is an :class:~kivy.properties.AliasProperty anddefaults to None, readonly.selection_text StringProperty(u)Current content selection.:attr:selection_text is a :class:~kivy.properties.StringPropertyand defaults to , readonly.def on_selection_text(self, instance, value):if value:if self.use_handles:self._trigger_show_handles()if CutBuffer and not self.password:self._trigger_update_cutbuffer()def _get_text(self):flags self._lines_flagslines self._lineslen_lines len(lines)less_flags len(flags) len_linesif less_flags:flags.append(1)text .join((\n if (flags[i] FL_IS_LINEBREAK) else ) lines[i]for i in range(len_lines))if less_flags:flags.pop()return textdef _set_text(self, text):if isinstance(text, bytes):text text.decode(utf8)if self.replace_crlf:text text.replace(u\r\n, u\n)if self.text ! text:self._refresh_text(text)self.cursor self.get_cursor_from_index(len(text))text AliasProperty(_get_text, _set_text, bind(_lines,), cacheTrue)Text of the widget.Creation of a simple hello world::widget TextInput(textHello world)If you want to create the widget with an unicode string, use::widget TextInput(textuMy unicode string):attr:text is an :class:~kivy.properties.AliasProperty.font_name StringProperty(DEFAULT_FONT)Filename of the font to use. The path can be absolute or relative.Relative paths are resolved by the :func:~kivy.resources.resource_findfunction... warning::Depending on your text provider, the font file may be ignored. However,you can mostly use this without problems.If the font used lacks the glyphs for the particular language/symbolsyou are using, you will see [] blank box characters instead of theactual glyphs. The solution is to use a font that has the glyphs youneed to display. For example, to display |unicodechar|, use a font likefreesans.ttf that has the glyph... |unicodechar| image:: images/unicode-char.png:attr:font_name is a :class:~kivy.properties.StringProperty anddefaults to Roboto. This value is takenfrom :class:~kivy.config.Config.font_size NumericProperty(15sp)Font size of the text in pixels.:attr:font_size is a :class:~kivy.properties.NumericProperty anddefaults to 15 :attr:~kivy.metrics.sp.font_context StringProperty(None, allownoneTrue)Font context. None means the font is used in isolation, so you areguaranteed to be drawing with the TTF file resolved by :attr:font_name.Specifying a value here will load the font file into a named context,enabling fallback between all fonts in the same context. If a fontcontext is set, you are not guaranteed that rendering will actually usethe specified TTF file for all glyphs (Pango will pick the one itthinks is best).If Kivy is linked against a system-wide installation of FontConfig,you can load the system fonts by specifying a font context startingwith the special string system://. This will load the systemfontconfig configuration, and add your application-specific fonts ontop of it (this imposes a signifficant risk of family name collision,Pango may not use your custom font file, but pick one from the system).. note::This feature requires the Pango text provider... versionadded:: 1.10.1:attr:font_context is a :class:~kivy.properties.StringProperty anddefaults to None.font_family StringProperty(None, allownoneTrue)Font family, this is only applicable when using :attr:font_contextoption. The specified font family will be requested, but note that it maynot be available, or there could be multiple fonts registered with thesame family. The value can be a family name (string) available in thefont context (for example a system font in a system:// context, or acustom font file added using :class:kivy.core.text.FontContextManager).If set to None, font selection is controlled by the :attr:font_namesetting... note::If using :attr:font_name to reference a custom font file, youshould leave this as None. The family name is managed automaticallyin this case... note::This feature requires the Pango text provider... versionadded:: 1.10.1:attr:font_family is a :class:~kivy.properties.StringProperty anddefaults to None.base_direction OptionProperty(None,options[ltr, rtl, weak_rtl, weak_ltr, None],allownoneTrue)Base direction of text, this impacts horizontal alignment when:attr:halign is auto (the default). Available options are: None,ltr (left to right), rtl (right to left) plus weak_ltr andweak_rtl... note::This feature requires the Pango text provider... note::Weak modes are currently not implemented in Kivy text layout, andhave the same effect as setting strong mode... versionadded:: 1.10.1:attr:base_direction is an :class:~kivy.properties.OptionProperty anddefaults to None (autodetect RTL if possible, otherwise LTR).text_language StringProperty(None, allownoneTrue)Language of the text, if None Pango will determine it from locale.This is an RFC-3066 format language tag (as a string), for exampleen_US, zh_CN, fr or ja. This can impact font selection, metricsand rendering. For example, the same bytes of text can look differentfor ur and ar languages, though both use Arabic script... note::This feature requires the Pango text provider... versionadded:: 1.10.1:attr:text_language is a :class:~kivy.properties.StringProperty anddefaults to None._hint_text StringProperty()def _set_hint_text(self, value):if isinstance(value, bytes):value value.decode(utf8)self._hint_text valuedef _get_hint_text(self):return self._hint_texthint_text AliasProperty(_get_hint_text, _set_hint_text, bind(_hint_text, ))Hint text of the widget, shown if text is ... versionadded:: 1.6.0.. versionchanged:: 1.10.0The property is now an AliasProperty and byte values are decoded tostrings. The hint text will stay visible when the widget is focused.:attr:hint_text a :class:~kivy.properties.AliasProperty and defaultsto .hint_text_color ColorProperty([0.5, 0.5, 0.5, 1.0])Current color of the hint_text text, in (r, g, b, a) format... versionadded:: 1.6.0:attr:hint_text_color is a :class:~kivy.properties.ColorProperty anddefaults to [0.5, 0.5, 0.5, 1.0] (grey)... versionchanged:: 2.0.0Changed from :class:~kivy.properties.ListProperty to:class:~kivy.properties.ColorProperty.auto_indent BooleanProperty(False)Automatically indent multiline text... versionadded:: 1.7.0:attr:auto_indent is a :class:~kivy.properties.BooleanProperty anddefaults to False.replace_crlf BooleanProperty(True)Automatically replace CRLF with LF... versionadded:: 1.9.1:attr:replace_crlf is a :class:~kivy.properties.BooleanProperty anddefaults to True.allow_copy BooleanProperty(True)Decides whether to allow copying the text... versionadded:: 1.8.0:attr:allow_copy is a :class:~kivy.properties.BooleanProperty anddefaults to True.def _get_min_height(self):return (len(self._lines) * (self.line_height self.line_spacing) self.padding[1] self.padding[3])minimum_height AliasProperty(_get_min_height,bind(_lines, line_spacing, padding,font_size, font_name, password,font_context, hint_text,line_height),cacheTrue)Minimum height of the content inside the TextInput... versionadded:: 1.8.0:attr:minimum_height is a readonly:class:~kivy.properties.AliasProperty... warning:::attr:minimum_width is calculated based on :attr:width thereforecode like this will lead to an infinite loop::FancyTextInput:height: self.minimum_heightwidth: self.heightline_spacing NumericProperty(0)Space taken up between the lines... versionadded:: 1.8.0:attr:line_spacing is a :class:~kivy.properties.NumericProperty anddefaults to 0.input_filter ObjectProperty(None, allownoneTrue) Filters the input according to the specified mode, if not None. IfNone, no filtering is applied... versionadded:: 1.9.0:attr:input_filter is an :class:~kivy.properties.ObjectProperty anddefaults to None. Can be one of None, int (string), or float(string), or a callable. If it is int, it will only accept numbers.If it is float it will also accept a single period. Finally, if it isa callable it will be called with two parameters; the string to be addedand a bool indicating whether the string is a result of undo (True). Thecallable should return a new substring that will be used instead.handle_image_middle StringProperty(atlas://data/images/defaulttheme/selector_middle)Image used to display the middle handle on the TextInput for cursorpositioning... versionadded:: 1.8.0:attr:handle_image_middle is a :class:~kivy.properties.StringPropertyand defaults to atlas://data/images/defaulttheme/selector_middle.def on_handle_image_middle(self, instance, value):if self._handle_middle:self._handle_middle.source valuehandle_image_left StringProperty(atlas://data/images/defaulttheme/selector_left)Image used to display the Left handle on the TextInput for selection... versionadded:: 1.8.0:attr:handle_image_left is a :class:~kivy.properties.StringProperty anddefaults to atlas://data/images/defaulttheme/selector_left.def on_handle_image_left(self, instance, value):if self._handle_left:self._handle_left.source valuehandle_image_right StringProperty(atlas://data/images/defaulttheme/selector_right)Image used to display the Right handle on the TextInput for selection... versionadded:: 1.8.0:attr:handle_image_right is a:class:~kivy.properties.StringProperty and defaults toatlas://data/images/defaulttheme/selector_right.def on_handle_image_right(self, instance, value):if self._handle_right:self._handle_right.source valuewrite_tab BooleanProperty(True)Whether the tab key should move focus to the next widget or if it shouldenter a tab in the :class:TextInput. If True a tab will be written,otherwise, focus will move to the next widget... versionadded:: 1.9.0:attr:write_tab is a :class:~kivy.properties.BooleanProperty anddefaults to True.if __name__ __main__:from kivy.app import Appfrom kivy.uix.boxlayout import BoxLayoutfrom kivy.lang import Builderclass TextInputApp(App):def build(self):Builder.load_string(
TextInputon_text:self.suggestion_text self.suggestion_text ion_text)root BoxLayout(orientationvertical)textinput TextInput(multilineTrue, use_bubbleTrue,use_handlesTrue)# textinput.text __doc__root.add_widget(textinput)textinput2 TextInput(multilineFalse, textmonoline textinput,size_hint(1, None), height30)root.add_widget(textinput2)return rootTextInputApp().run()_get_text_width
_get_text_width 是 TextInput 的一个方法用于获取文本在控件中的实际宽度。它的参数包括文本内容、制表符宽度和缓存标签。在上面的代码中这个方法被用于计算 TextInput 控件中文本的水平偏移量。 def _get_text_width(self, text, tab_width, _label_cached):# Return the width of a text, according to the current line optionskw self._get_line_options()try:cid u{}\0{}\0{}.format(text, self.password, kw)except UnicodeDecodeError:cid {}\0{}\0{}.format(text, self.password, kw)width Cache_get(textinput.width, cid)if width:return widthif not _label_cached:_label_cached self._label_cachedtext text.replace(\t, * tab_width)if not self.password:width _label_cached.get_extents(text)[0]else:width _label_cached.get_extents(self.password_mask * len(text))[0]Cache_append(textinput.width, cid, width)return width 实战: TextInput:id: name_inputtext: C0Csize_hint_x: .5size_hint_y: Noneheight: 32dppadding_x: [self.center[0]/6.0 - self._get_text_width(max(self._lines, keylen), self.tab_width, self._label_cached)/6.0, 0]padding_y: [self.height/2.0 - (self.line_height/2.0) * len(self._lines), 0]
这是一段Kivy的代码是一个名为name_input的TextInput组件其主要功能是用于用户输入文本。下面是各部分代码的作用介绍 id: name_input给TextInput组件设置了一个唯一的id方便后续在代码中进行引用。 text: ‘C0C’设置TextInput组件的默认文本为“C0C”。 size_hint_x: .5设置组件在父容器中占据的宽度比例为0.5即占据父容器宽度的50%。 size_hint_y: None设置组件在父容器中占据的高度比例为None即不进行自适应高度调整。 height: ‘32dp’设置组件的默认高度为32dp。 padding_x: [self.center/6.0 - self._get_text_width(max(self._lines, keylen), self.tab_width, self._label_cached)/6.0, 0]设置组件在水平方向上的内边距。这里使用了一些计算以使得输入框左右两侧的内边距与文字的长度相关。 padding_y: [self.height/2.0 - (self.line_height/2.0) * len(self._lines), 0]设置组件在垂直方向上的内边距。这里使用了一些计算以使得输入框上下两侧的内边距与文字的行数和行高相关。 padding_x和padding_y是用来设置文本框内部文字的位置的属性。具体来说
padding_x是一个长度为2的列表
第一个元素表示文字距离文本框左侧的距离
第二个元素表示文字距离文本框右侧的距离
padding_y同理
第一个元素表示文字距离文本框底部的距离
第二个元素表示文字距离文本框顶部的距离。
在这段代码中padding_x和padding_y的计算方式与文本框大小、文字行数、文字大小等因素有关。具体来说padding_x的第一个元素的计算方式是通过将文本框宽度除以6再减去文本的宽度除以6最后再除以2得到的padding_y的第一个元素的计算方式是将文本框高度除以2再减去每行文字高度的一半再乘以文字行数得到的。这样计算出来的padding_x和padding_y可以保证文字在文本框内部居中显示。 tab_width
将tap键替换为指定数量的空格默认为4 tab_width NumericProperty(4)By default, each tab will be replaced by four spaces on the textinput widget. You can set a lower or higher value.:attr:tab_width is a :class:~kivy.properties.NumericProperty anddefaults to 4.
padding_x
文本的水平填充格式为[padding_left, padding_right]或[padding_horizontal]默认为[0,0] padding_x VariableListProperty([0, 0], length2, deprecatedTrue)Horizontal padding of the text: [padding_left, padding_right].padding_x also accepts a one argument form [padding_horizontal].:attr:padding_x is a :class:~kivy.properties.VariableListProperty anddefaults to [0, 0]. This might be changed by the current theme... deprecated:: 1.7.0Use :attr:padding instead.
_label_cached
_label_cached
_label_cached是一个缓存的Label对象用于存储TextInput中已经渲染过的文本标签以便提高TextInput的渲染性能。
_label_cached是在TextInput的绘制方法中使用的它存储了当前已经绘制的文本标签避免每次绘制时都需要重新创建和设置标签的属性从而提高渲染性能。_lines
_lines ListProperty([])
_lines则是用于存储TextInput中文本的行数可能会根据文本内容的变化而动态调整。
_lines则是在TextInput的文本内容发生变化时更新的它用于计算文本的行数以便在padding_y中设置合适的内边距来使文本垂直居中。 个人总结:这简直就是从天方夜谭里抽出根绣花儿针削铁如泥真是6歪了。 center[0] Module: kivy.uix.widget
center[0]指的是TextInput组件的中心点在x轴方向上的位置。padding_x属性设置了TextInput组件中文本的左右边距使得文本内容相对于组件中心向左偏移了1/6的组件宽度这个偏移量是通过center来计算的。具体来说这个计算是通过将center除以6.0得到的值来实现的。 center
Center position of the widget.center is a ReferenceListProperty of (center_x, center_y) properties. 个人总结: 在TextInput里翻了半天没找到center这应该是继承它祖父的属性。