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() |
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 |