tuikit/widgets/textbox.py
changeset 112 ce2e67e7bbb8
parent 111 b055add74b18
child 113 6796adfdc7eb
equal deleted inserted replaced
111:b055add74b18 112:ce2e67e7bbb8
     2 from tuikit.core.signal import Signal
     2 from tuikit.core.signal import Signal
     3 
     3 
     4 
     4 
     5 class TextBox(Widget):
     5 class TextBox(Widget):
     6 
     6 
     7     """Multiline text view/edit widget.
     7     """Multiline text view/edit widget."""
     8 
       
     9     Spot is used for text cursor position.
       
    10 
       
    11     """
       
    12 
     8 
    13     def __init__(self, text=''):
     9     def __init__(self, text=''):
    14         Widget.__init__(self)
    10         Widget.__init__(self)
    15         self.allow_focus = True
    11         self.allow_focus = True
    16 
    12 
    17         # Text content, splitted as lines
    13         # Text content, splitted as lines
    18         self._lines = []
    14         self._lines = []
    19         self.text = text
    15         self.text = text
    20 
    16 
    21         # Cursor position is same as spot.
    17         self._cursor_visible = True
    22         # This variable rememberes horizontal position
    18         # This variable rememberes horizontal cursor position
    23         # for the case when cursor moves to shorter line.
    19         # for the case when cursor moves to shorter line.
    24         self.cursor_column = 0
    20         self.cursor_column = 0
    25         # selection - line and column of selection start
    21         # selection - line and column of selection start
    26         self.sel_line = 0
    22         self.sel_line = 0
    27         self.sel_column = 0
    23         self.sel_column = 0
    38         maxlen = max([len(line) for line in self._lines])
    34         maxlen = max([len(line) for line in self._lines])
    39         self.sizereq.update(w=maxlen, h=len(self._lines))
    35         self.sizereq.update(w=maxlen, h=len(self._lines))
    40 
    36 
    41     @property
    37     @property
    42     def cur_line(self):
    38     def cur_line(self):
    43         return self._lines[self._spot.y]
    39         return self._lines[self._cursor.y]
    44 
    40 
    45     @cur_line.setter
    41     @cur_line.setter
    46     def cur_line(self, value):
    42     def cur_line(self, value):
    47         self._lines[self._spot.y] = value
    43         self._lines[self._cursor.y] = value
    48 
       
    49     def set_theme(self, theme):
       
    50         Widget.set_theme(self, theme)
       
    51         self.color = theme.normal
       
    52 
    44 
    53     def draw(self, buffer):
    45     def draw(self, buffer):
    54         exposed = self.exposed(buffer)
    46         exposed = self.exposed(buffer)
    55         with buffer.attr(self.color):
    47         with buffer.attr(self.theme.normal):
    56             buffer.fill()
    48             buffer.fill()
    57             end_y = min(len(self._lines), exposed.y + exposed.h)
    49             end_y = min(len(self._lines), exposed.y + exposed.h)
    58             for j in range(exposed.y, end_y):
    50             for j in range(exposed.y, end_y):
    59                 line = self._lines[j]
    51                 line = self._lines[j]
    60                 buffer.puts(line, 0, j)
    52                 buffer.puts(line, 0, j)
    61             #self.cursor = (self._spot.x, self._spot.y)
       
    62 
    53 
    63     def keypress_event(self, ev):
    54     def keypress_event(self, ev):
    64         if ev.keyname and not ev.mods:
    55         if ev.keyname and not ev.mods:
    65             consumed = True
    56             consumed = True
    66             if   ev.keyname == 'left':        self.move_left()
    57             if   ev.keyname == 'left':        self.move_left()
    94             return True
    85             return True
    95 
    86 
    96     def on_mousedown(self, ev):
    87     def on_mousedown(self, ev):
    97         y = ev.wy
    88         y = ev.wy
    98         x = min(ev.wx, len(self._lines[y]))
    89         x = min(ev.wx, len(self._lines[y]))
    99         self._spot.update(x=x, y=y)
    90         self._cursor.update(x=x, y=y)
   100         self.redraw()
    91         self.redraw()
   101 
    92 
   102     def on_mousewheel(self, ev):
    93     def on_mousewheel(self, ev):
   103         if ev.button == 4:
    94         if ev.button == 4:
   104             # wheel up
    95             # wheel up
   107             # wheel down
    98             # wheel down
   108             self.emit('scrollreq', +5)
    99             self.emit('scrollreq', +5)
   109         self.redraw()
   100         self.redraw()
   110 
   101 
   111     def move_left(self):
   102     def move_left(self):
   112         if self._spot.x > 0:
   103         if self._cursor.x > 0:
   113             self._spot.x -= 1
   104             self._cursor.x -= 1
   114         else:
   105         else:
   115             if self._spot.y > 0:
   106             if self._cursor.y > 0:
   116                 self._spot.y -= 1
   107                 self._cursor.y -= 1
   117                 self._spot.x = len(self.cur_line)
   108                 self._cursor.x = len(self.cur_line)
   118         self.cursor_column = self._spot.x
   109         self.cursor_column = self._cursor.x
   119 
   110 
   120     def move_right(self):
   111     def move_right(self):
   121         if self._spot.x < len(self.cur_line):
   112         if self._cursor.x < len(self.cur_line):
   122             self._spot.x += 1
   113             self._cursor.x += 1
   123         else:
   114         else:
   124             if self._spot.y < len(self._lines) - 1:
   115             if self._cursor.y < len(self._lines) - 1:
   125                 self._spot.y += 1
   116                 self._cursor.y += 1
   126                 self._spot.x = 0
   117                 self._cursor.x = 0
   127         self.cursor_column = self._spot.x
   118         self.cursor_column = self._cursor.x
   128 
   119 
   129     def move_home(self):
   120     def move_home(self):
   130         self._spot.x = 0
   121         self._cursor.x = 0
   131         self.cursor_column = self._spot.x
   122         self.cursor_column = self._cursor.x
   132 
   123 
   133     def move_end(self):
   124     def move_end(self):
   134         self._spot.x = len(self.cur_line)
   125         self._cursor.x = len(self.cur_line)
   135         self.cursor_column = self._spot.x
   126         self.cursor_column = self._cursor.x
   136 
   127 
   137     def move_up(self):
   128     def move_up(self):
   138         if self._spot.y > 0:
   129         if self._cursor.y > 0:
   139             self._spot.y -= 1
   130             self._cursor.y -= 1
   140         self._update_spot_x()
   131         self._update_cursor_x()
   141 
   132 
   142     def move_down(self):
   133     def move_down(self):
   143         if self._spot.y < len(self._lines) - 1:
   134         if self._cursor.y < len(self._lines) - 1:
   144             self._spot.y += 1
   135             self._cursor.y += 1
   145         self._update_spot_x()
   136         self._update_cursor_x()
   146 
   137 
   147     def move_pageup(self):
   138     def move_pageup(self):
   148         if self._spot.y >= self.view_height - 1:
   139         if self._cursor.y >= self.view_height - 1:
   149             self.emit('scrollreq', - (self.view_height - 1))
   140             self.emit('scrollreq', - (self.view_height - 1))
   150             self._spot.y -= self.view_height - 1
   141             self._cursor.y -= self.view_height - 1
   151         else:
   142         else:
   152             self._spot.y = 0
   143             self._cursor.y = 0
   153         self._update_spot_x()
   144         self._update_cursor_x()
   154 
   145 
   155     def move_pagedown(self):
   146     def move_pagedown(self):
   156         if len(self._lines) - self._spot.y > (self.view_height - 1):
   147         if len(self._lines) - self._cursor.y > (self.view_height - 1):
   157             self.emit('scrollreq', (self.view_height - 1))
   148             self.emit('scrollreq', (self.view_height - 1))
   158             self._spot.y += self.view_height - 1
   149             self._cursor.y += self.view_height - 1
   159         else:
   150         else:
   160             self._spot.y = len(self._lines) - 1
   151             self._cursor.y = len(self._lines) - 1
   161         self._update_spot_x()
   152         self._update_cursor_x()
   162 
   153 
   163     def move_top(self):
   154     def move_top(self):
   164         self._spot.y = 0
   155         self._cursor.y = 0
   165         self._update_spot_x()
   156         self._update_cursor_x()
   166 
   157 
   167     def move_bottom(self):
   158     def move_bottom(self):
   168         self._spot.y = len(self._lines) - 1
   159         self._cursor.y = len(self._lines) - 1
   169         self._update_spot_x()
   160         self._update_cursor_x()
   170 
   161 
   171     def add_char(self, c):
   162     def add_char(self, c):
   172         ln = self.cur_line
   163         ln = self.cur_line
   173         sx = self._spot.x
   164         sx = self._cursor.x
   174         self.cur_line = ln[:sx] + c + ln[sx:]
   165         self.cur_line = ln[:sx] + c + ln[sx:]
   175         self.cursor_column = sx
   166         self.cursor_column = sx
   176 
   167 
   177     def add_newline(self, move=False):
   168     def add_newline(self, move=False):
   178         ln = self.cur_line
   169         ln = self.cur_line
   179         sx = self._spot.x
   170         sx = self._cursor.x
   180         self.cur_line = ln[sx:]
   171         self.cur_line = ln[sx:]
   181         self._lines.insert(self._spot.y, ln[:sx])
   172         self._lines.insert(self._cursor.y, ln[:sx])
   182         self._default_size.update(h=len(self._lines))
   173         self.sizereq.update(h=len(self._lines))
   183         if move:
   174         if move:
   184             self.move_right()
   175             self.move_right()
   185 
   176 
   186     def add_line(self, text):
   177     def add_line(self, text):
   187         ln = self.cur_line
   178         ln = self.cur_line
   188         sx = self._spot.x
   179         sx = self._cursor.x
   189         self.cur_line = ln[sx:]
   180         self.cur_line = ln[sx:]
   190         self._lines.insert(self._spot.y, ln[:sx] + text)
   181         self._lines.insert(self._cursor.y, ln[:sx] + text)
   191         self.cursor_column = 0
   182         self.cursor_column = 0
   192         self._spot.x = 0
   183         self._cursor.x = 0
   193         self._spot.y += 1
   184         self._cursor.y += 1
   194         w = max(self._default_size.w, len(ln[:sx] + text))
   185         w = max(self.sizereq.w, len(ln[:sx] + text))
   195         self._default_size.update(w=w, h=len(self._lines))
   186         self.sizereq.update(w=w, h=len(self._lines))
   196 
   187 
   197     def backspace(self):
   188     def backspace(self):
   198         if self._spot.y > 0 or self._spot.x > 0:
   189         if self._cursor.y > 0 or self._cursor.x > 0:
   199             self.move_left()
   190             self.move_left()
   200             self.del_char()
   191             self.del_char()
   201 
   192 
   202     def del_char(self):
   193     def del_char(self):
   203         ln = self.cur_line
   194         ln = self.cur_line
   204         sx = self._spot.x
   195         sx = self._cursor.x
   205         if sx == len(self.cur_line):
   196         if sx == len(self.cur_line):
   206             if self._spot.y + 1 < len(self._lines):
   197             if self._cursor.y + 1 < len(self._lines):
   207                 self.cur_line += self._lines[self._spot.y+1]
   198                 self.cur_line += self._lines[self._cursor.y+1]
   208                 del self._lines[self._spot.y+1]
   199                 del self._lines[self._cursor.y+1]
   209                 self._default_size.update(h=len(self._lines))
   200                 self.sizereq.update(h=len(self._lines))
   210         else:
   201         else:
   211             self.cur_line = ln[:sx] + ln[sx+1:]
   202             self.cur_line = ln[:sx] + ln[sx+1:]
   212 
   203 
   213     def _update_spot_x(self):
   204     def _update_cursor_x(self):
   214         if self.cursor_column > len(self.cur_line):
   205         if self.cursor_column > len(self.cur_line):
   215             self._spot.x = len(self.cur_line)
   206             self._cursor.x = len(self.cur_line)
   216         else:
   207         else:
   217             self._spot.x = self.cursor_column
   208             self._cursor.x = self.cursor_column
   218 
   209