1 | # |
||
2 | # Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/] |
||
3 | # |
||
4 | # This file is part of Pygame.UI. |
||
5 | # |
||
6 | # Pygame.UI is free software; you can redistribute it and/or modify |
||
7 | # it under the terms of the Lesser GNU General Public License as published by |
||
8 | # the Free Software Foundation; either version 2.1 of the License, or |
||
9 | # (at your option) any later version. |
||
10 | # |
||
11 | # Pygame.UI is distributed in the hope that it will be useful, |
||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | # Lesser GNU General Public License for more details. |
||
15 | # |
||
16 | # You should have received a copy of the Lesser GNU General Public License |
||
17 | # along with Pygame.UI; if not, write to the Free Software |
||
18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||
19 | # |
||
20 | |||
21 | import unittest |
||
22 | import Const |
||
23 | from WordUtils import splitter |
||
24 | from Widget import Widget, registerWidget |
||
25 | import pygame.key |
||
26 | |||
27 | # keys mapping |
||
28 | MAPPING = { |
||
29 | pygame.K_KP0: '0', pygame.K_KP1: '1', pygame.K_KP2: '2', pygame.K_KP3: '3', pygame.K_KP4: '4', |
||
30 | pygame.K_KP5: '5', pygame.K_KP6: '6', pygame.K_KP7: '7', pygame.K_KP8: '8', pygame.K_KP9: '9', |
||
31 | } |
||
32 | |||
33 | |||
34 | class Selection(object): |
||
35 | """ Object to hold and pre-process information about selected area of the text. |
||
36 | """ |
||
37 | def __init__(self): |
||
38 | self._start = None |
||
39 | self._end = None |
||
40 | |||
41 | @property |
||
42 | def first(self): |
||
43 | """ Returns coordinates of the beginning of the selection """ |
||
44 | return min(self._start, self._end) |
||
45 | |||
46 | @property |
||
47 | def last(self): |
||
48 | """ Returns coordinates of the ending of the selection """ |
||
49 | return max(self._start, self._end) |
||
50 | |||
51 | def select(self, row, column): |
||
52 | if self._start is None: |
||
53 | self._start = (row, column) |
||
54 | self._end = (row, column) |
||
55 | |||
56 | def deselect(self): |
||
57 | self._start = self._end = None |
||
58 | |||
59 | def __nonzero__(self): |
||
60 | return self._start is not None and self._end is not None |
||
61 | |||
62 | |||
63 | class Text(Widget): |
||
64 | """Text edit widget.""" |
||
65 | |||
66 | def __init__(self, parent, **kwargs): |
||
67 | Widget.__init__(self, parent) |
||
68 | # data |
||
69 | self.__dict__['text'] = [""] |
||
70 | self.__dict__['offsetRow'] = 0 |
||
71 | self.__dict__['cursorRow'] = 0 |
||
72 | self.__dict__['cursorColumn'] = 0 |
||
73 | self.__dict__['action'] = None |
||
74 | self.__dict__['editable'] = 1 |
||
75 | self.__dict__['vertScrollbar'] = None |
||
76 | self.__dict__['selection'] = Selection() |
||
77 | # flags |
||
78 | self.processKWArguments(kwargs) |
||
79 | parent.registerWidget(self) |
||
80 | |||
81 | def draw(self, surface): |
||
82 | self.theme.drawText(surface, self) |
||
83 | return self.rect |
||
84 | |||
85 | def attachVScrollbar(self, scrollbar): |
||
86 | self.vertScrollbar = scrollbar |
||
87 | scrollbar.subscribeAction("*", self) |
||
88 | scrollbar.action = "onScroll" |
||
89 | scrollbar.slider.min = 0 |
||
90 | scrollbar.slider.max = len(self.text) + 100 |
||
91 | scrollbar.slider.position = self.offsetRow |
||
92 | scrollbar.slider.shown = 1 |
||
93 | |||
94 | def onScroll(self, widget, action, data): |
||
95 | self.offsetRow = self.vertScrollbar.slider.position |
||
96 | |||
97 | def onCursorChanged(self): |
||
98 | # force redraw |
||
99 | self.cursorColumn += 1 |
||
100 | self.cursorColumn -= 1 |
||
101 | # super |
||
102 | Widget.onCursorChanged(self) |
||
103 | |||
104 | def _deleteSelection(self): |
||
105 | assert self.selection |
||
106 | |||
107 | # one-line selection |
||
108 | if self.selection.first[0] == self.selection.last[0]: |
||
109 | textBefore = self.text[self.selection.last[0]][:self.selection.first[1]] |
||
110 | textAfter = self.text[self.selection.last[0]][self.selection.last[1]:] |
||
111 | self.text[self.selection.last[0]] = textBefore + textAfter |
||
112 | else: |
||
113 | # handle multi-line selection |
||
114 | # delete end of selection |
||
115 | self.text[self.selection.last[0]] = self.text[self.selection.last[0]][self.selection.last[1]:] |
||
116 | # delete fully selected rows |
||
117 | del self.text[self.selection.first[0] + 1:self.selection.last[0]] |
||
118 | # delete selection on first row |
||
119 | self.text[self.selection.first[0]] = self.text[self.selection.first[0]][:self.selection.first[1]] |
||
120 | # join the rows that are spanned |
||
121 | self.text[self.selection.first[0]] = self.text[self.selection.first[0]] + self.text[self.selection.first[0] + 1] |
||
122 | del self.text[self.selection.first[0] + 1] |
||
123 | # move cursor to selection begining |
||
124 | self.cursorColumn = self.selection.first[1] |
||
125 | self.cursorRow = self.selection.first[0] |
||
126 | # clear selection |
||
127 | self.selection.deselect() |
||
128 | |||
129 | def _processBackspace(self, evt): |
||
130 | if self.selection: |
||
131 | self._deleteSelection() |
||
132 | elif self.cursorColumn > 0: |
||
133 | self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn - 1] + self.text[self.cursorRow][self.cursorColumn:] |
||
134 | self.cursorColumn -= 1 |
||
135 | elif self.cursorRow > 0: |
||
136 | self.cursorColumn = len(self.text[self.cursorRow - 1]) |
||
137 | self.text[self.cursorRow - 1] = self.text[self.cursorRow - 1] + self.text[self.cursorRow] |
||
138 | del self.text[self.cursorRow] |
||
139 | self.cursorRow -= 1 |
||
140 | |||
141 | def _processDelete(self, evt): |
||
142 | if self.selection: |
||
143 | self._deleteSelection() |
||
144 | elif self.cursorColumn < len(self.text[self.cursorRow]): |
||
145 | self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn] + self.text[self.cursorRow][self.cursorColumn + 1:] |
||
146 | elif self.cursorRow < len(self.text) - 1: |
||
147 | self.text[self.cursorRow] = self.text[self.cursorRow] + self.text[self.cursorRow + 1] |
||
148 | del self.text[self.cursorRow + 1] |
||
149 | |||
150 | def _processLeft(self, evt): |
||
151 | if evt.mod & pygame.KMOD_CTRL: |
||
152 | # move one word left |
||
153 | # take words on line |
||
154 | words = splitter(self.text[self.cursorRow][:self.cursorColumn]) |
||
155 | if len(words) == 0: |
||
156 | if self.cursorRow == 0: |
||
157 | # we are on first line, so move cursor to begining of line |
||
158 | self.cursorColumn = 0 |
||
159 | else: |
||
160 | # move to previous line and try again |
||
161 | self.cursorRow -= 1 |
||
162 | self.cursorColumn = len(self.text[self.cursorRow]) |
||
163 | self._processLeft(evt) |
||
164 | return |
||
165 | # we must jump to begining of last word |
||
166 | self.cursorColumn = words[-1][1] |
||
167 | elif self.cursorColumn > 0: |
||
168 | self.cursorColumn -= 1 |
||
169 | elif self.cursorRow > 0: |
||
170 | self.cursorRow -= 1 |
||
171 | self.cursorColumn = len(self.text[self.cursorRow]) |
||
172 | |||
173 | def _processRight(self, evt): |
||
174 | if evt.mod & pygame.KMOD_CTRL: |
||
175 | # move one word right |
||
176 | # take words on line |
||
177 | words = splitter(self.text[self.cursorRow][self.cursorColumn:]) |
||
178 | if len(words) == 0: |
||
179 | if self.cursorRow == len(self.text) - 1: |
||
180 | # we are on last line, so move cursor to end of line |
||
181 | self.cursorColumn = len(self.text[self.cursorRow]) |
||
182 | else: |
||
183 | self.cursorRow += 1 |
||
184 | self.cursorColumn = 0 |
||
185 | self._processRight(evt) |
||
186 | return |
||
187 | # we jump to the end of the first word |
||
188 | self.cursorColumn += words[0][2] |
||
189 | elif self.cursorColumn < len(self.text[self.cursorRow]): |
||
190 | self.cursorColumn += 1 |
||
191 | elif self.cursorRow < len(self.text): |
||
192 | # move to the next row |
||
193 | if self.cursorRow < len(self.text) - 1: |
||
194 | self.cursorRow += 1 |
||
195 | self.cursorColumn = 0 |
||
196 | |||
197 | def _processUp(self, evt): |
||
198 | if self.cursorRow > 0: |
||
199 | self.cursorRow -= 1 |
||
200 | self.cursorColumn = min(self.cursorColumn, len(self.text[self.cursorRow])) |
||
201 | else: |
||
202 | self.cursorColumn = 0 |
||
203 | |||
204 | if self.vertScrollbar and self.cursorRow - self.offsetRow < 0: |
||
205 | self.vertScrollbar.onButton1(self, "", "") |
||
206 | |||
207 | def _processDown(self, evt): |
||
208 | if self.cursorRow < len(self.text) - 1: |
||
209 | self.cursorRow += 1 |
||
210 | self.cursorColumn = min(self.cursorColumn, len(self.text[self.cursorRow])) |
||
211 | else: |
||
212 | self.cursorColumn = len(self.text[self.cursorRow]) |
||
213 | |||
214 | if self.vertScrollbar and self.cursorRow - self.offsetRow >= self.theme.getTextDrawLines(self): |
||
215 | self.vertScrollbar.onButton2(self, "", "") |
||
216 | |||
217 | def _processHome(self, evt): |
||
218 | self.cursorColumn = 0 |
||
219 | |||
220 | def _processEnd(self, evt): |
||
221 | self.cursorColumn = len(self.text[self.cursorRow]) |
||
222 | |||
223 | def _processReturn(self, evt): |
||
224 | text1 = self.text[self.cursorRow][self.cursorColumn:] |
||
225 | text2 = self.text[self.cursorRow][:self.cursorColumn] |
||
226 | self.text[self.cursorRow] = text1 |
||
227 | self.text.insert(self.cursorRow, text2) |
||
228 | self.cursorRow += 1 |
||
229 | self.cursorColumn = 0 |
||
230 | |||
231 | def _processUnicode(self, evt): |
||
232 | char = evt.unicode |
||
233 | self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn] + char + self.text[self.cursorRow][self.cursorColumn:] |
||
234 | self.cursorColumn += 1 |
||
235 | |||
236 | def _processNumKeyboard(self, evt): |
||
237 | self.text[self.cursorRow] = self.text[self.cursorRow][:self.cursorColumn] + MAPPING[evt.key] + self.text[self.cursorRow][self.cursorColumn:] |
||
238 | self.cursorColumn += 1 |
||
239 | |||
240 | def wrapDeleteSelection(self, func, evt, deleteOnly=False): |
||
241 | if self.selection: |
||
242 | self._deleteSelection() |
||
243 | if deleteOnly: |
||
244 | return |
||
245 | func(evt) |
||
246 | |||
247 | def wrapSelect(self, func, evt): |
||
248 | if evt.mod & pygame.KMOD_SHIFT: |
||
249 | self.selection.select(self.cursorRow, self.cursorColumn) |
||
250 | func(evt) |
||
251 | self.selection.select(self.cursorRow, self.cursorColumn) |
||
252 | else: |
||
253 | func(evt) |
||
254 | |||
255 | def processKeyUp(self, evt): |
||
256 | # consume pygame.K_RETURN (acceptButton on Window will not work) |
||
257 | # can be choosable on construction? |
||
258 | if evt.key == pygame.K_RETURN: |
||
259 | return Const.NoEvent |
||
260 | else: |
||
261 | return Widget.processKeyUp(self, evt) |
||
262 | |||
263 | def processKeyDown(self, evt): |
||
264 | if not self.editable: |
||
265 | return Widget.processKeyDown(self, evt) |
||
266 | |||
267 | # process keys |
||
268 | if evt.key == pygame.K_BACKSPACE: |
||
269 | self.wrapDeleteSelection(self._processBackspace, evt, deleteOnly=True) |
||
270 | |||
271 | elif evt.key == pygame.K_DELETE: |
||
272 | self.wrapDeleteSelection(self._processDelete, evt, deleteOnly=True) |
||
273 | |||
274 | elif evt.key == pygame.K_ESCAPE: |
||
275 | self.app.setFocus(None) |
||
276 | |||
277 | elif evt.key == pygame.K_LEFT: |
||
278 | self.wrapSelect(self._processLeft, evt) |
||
279 | |||
280 | elif evt.key == pygame.K_RIGHT: |
||
281 | self.wrapSelect(self._processRight, evt) |
||
282 | |||
283 | elif evt.key == pygame.K_UP: |
||
284 | self.wrapSelect(self._processUp, evt) |
||
285 | |||
286 | elif evt.key == pygame.K_DOWN: |
||
287 | self.wrapSelect(self._processDown, evt) |
||
288 | |||
289 | elif evt.key == pygame.K_TAB: |
||
290 | pass |
||
291 | |||
292 | elif evt.key == pygame.K_HOME: |
||
293 | self.wrapSelect(self._processHome, evt) |
||
294 | |||
295 | elif evt.key == pygame.K_END: |
||
296 | self.wrapSelect(self._processEnd, evt) |
||
297 | |||
298 | elif evt.key == pygame.K_RETURN: |
||
299 | self.wrapDeleteSelection(self._processReturn, evt) |
||
300 | |||
301 | elif hasattr(evt, 'unicode') and evt.unicode: |
||
302 | self.wrapDeleteSelection(self._processUnicode, evt) |
||
303 | |||
304 | elif evt.key in MAPPING: |
||
305 | self.wrapDeleteSelection(self._processNumKeyboard, evt) |
||
306 | |||
307 | return Widget.processKeyDown(self, Const.NoEvent) |
||
308 | |||
309 | def onFocusGained(self): |
||
310 | Widget.onFocusGained(self) |
||
311 | self.cursorRow = len(self.text) - 1 |
||
312 | self.cursorColumn = len(self.text[self.cursorRow]) |
||
313 | |||
314 | def onFocusLost(self): |
||
315 | Widget.onFocusLost(self) |
||
316 | self.processAction(self.action) |
||
317 | |||
318 | # redirect mouse wheel events to the scollbar |
||
319 | def processMWUp(self, evt): |
||
320 | if self.vertScrollbar: |
||
321 | return self.vertScrollbar.processMWUp(evt) |
||
322 | |||
323 | def processMWDown(self, evt): |
||
324 | if self.vertScrollbar: |
||
325 | return self.vertScrollbar.processMWDown(evt) |
||
326 | |||
327 | |||
328 | registerWidget(Text, 'text') |
||
329 | |||
330 | |||
331 | class TextTestCase(unittest.TestCase): |
||
332 | def setUp(self): |
||
333 | class Evt: |
||
334 | def __init__(self, key, letter="", mod=0): |
||
335 | self.key = key |
||
336 | self.unicode = unicode(letter) |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||
337 | self.mod = mod |
||
338 | |||
339 | class App: |
||
340 | def __init__(self): |
||
341 | self.theme = "Test" |
||
342 | |||
343 | class Parent: |
||
344 | def __init__(self): |
||
345 | self.app = App() |
||
346 | |||
347 | def getApp(self): |
||
348 | return self.app |
||
349 | |||
350 | def redraw(self, *args, **kwargs): |
||
351 | return |
||
352 | |||
353 | def registerWidget(self, *args, **kwargs): |
||
354 | return |
||
355 | |||
356 | self.parent = Parent() |
||
357 | self.txt = Text(self.parent) |
||
358 | self.txt.text = ["Zero line", "One line", "Two line", "", "", "Five line"] |
||
359 | self.txt.offsetRow = 0 |
||
360 | self.txt.cursorRow = 0 |
||
361 | self.txt.cursorColumn = 0 |
||
362 | self.txt.action = None |
||
363 | self.txt.editable = 1 |
||
364 | self.txt.vertScrollbar = None |
||
365 | self.txt.selection = Selection() |
||
366 | self.Evt = Evt |
||
367 | |||
368 | def tearDown(self): |
||
369 | pass |
||
370 | |||
371 | def test_dummy(self): |
||
372 | self.txt.cursorColumn = 3 |
||
373 | self.txt.cursorRow = 2 |
||
374 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"]) |
||
375 | self.assertEqual(self.txt.cursorColumn, 3) |
||
376 | self.assertEqual(self.txt.cursorRow, 2) |
||
377 | |||
378 | def test_input(self): |
||
379 | self.txt.cursorColumn = 3 |
||
380 | self.txt.cursorRow = 2 |
||
381 | for letter in " quite expanded": |
||
382 | Text.processKeyDown(self.txt, self.Evt(None, letter=letter)) |
||
383 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two quite expanded line", "", "", "Five line"]) |
||
384 | self.assertEqual(self.txt.cursorColumn, 18) |
||
385 | |||
386 | def test_numpad_input(self): |
||
387 | self.txt.cursorColumn = 3 |
||
388 | self.txt.cursorRow = 2 |
||
389 | for key in [pygame.K_KP0, pygame.K_KP4, pygame.K_KP4, pygame.K_KP8]: |
||
390 | Text.processKeyDown(self.txt, self.Evt(key)) |
||
391 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two0448 line", "", "", "Five line"]) |
||
392 | self.assertEqual(self.txt.cursorColumn, 7) |
||
393 | |||
394 | def test_backspace(self): |
||
395 | evt = self.Evt(pygame.K_BACKSPACE) |
||
396 | self.txt.cursorColumn = 3 |
||
397 | self.txt.cursorRow = 2 |
||
398 | for x in range(4): |
||
399 | Text.processKeyDown(self.txt, evt) |
||
400 | self.assertEqual(self.txt.text, ["Zero line", "One line line", "", "", "Five line"]) |
||
401 | self.assertEqual(self.txt.cursorColumn, 8) |
||
402 | self.assertEqual(self.txt.cursorRow, 1) |
||
403 | # first character |
||
404 | self.txt.cursorColumn = 0 |
||
405 | self.txt.cursorRow = 0 |
||
406 | Text.processKeyDown(self.txt, evt) |
||
407 | self.assertEqual(self.txt.text, ["Zero line", "One line line", "", "", "Five line"]) |
||
408 | self.assertEqual(self.txt.cursorColumn, 0) |
||
409 | self.assertEqual(self.txt.cursorRow, 0) |
||
410 | |||
411 | def test_delete(self): |
||
412 | evt = self.Evt(pygame.K_DELETE) |
||
413 | self.txt.cursorColumn = 3 |
||
414 | self.txt.cursorRow = 2 |
||
415 | for x in range(6): |
||
416 | Text.processKeyDown(self.txt, evt) |
||
417 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two", "", "Five line"]) |
||
418 | self.assertEqual(self.txt.cursorColumn, 3) |
||
419 | self.assertEqual(self.txt.cursorRow, 2) |
||
420 | for x in range(3): |
||
421 | Text.processKeyDown(self.txt, evt) |
||
422 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Twoive line"]) |
||
423 | self.assertEqual(self.txt.cursorColumn, 3) |
||
424 | self.assertEqual(self.txt.cursorRow, 2) |
||
425 | # last character |
||
426 | self.txt.cursorColumn = 11 |
||
427 | Text.processKeyDown(self.txt, evt) |
||
428 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Twoive line"]) |
||
429 | self.assertEqual(self.txt.cursorColumn, 11) |
||
430 | self.assertEqual(self.txt.cursorRow, 2) |
||
431 | |||
432 | def test_return(self): |
||
433 | evt = self.Evt(pygame.K_RETURN) |
||
434 | self.txt.cursorColumn = 3 |
||
435 | self.txt.cursorRow = 2 |
||
436 | Text.processKeyDown(self.txt, evt) |
||
437 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two", " line", "", "", "Five line"]) |
||
438 | self.assertEqual(self.txt.cursorColumn, 0) |
||
439 | self.assertEqual(self.txt.cursorRow, 3) |
||
440 | Text.processKeyDown(self.txt, evt) |
||
441 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two", "", " line", "", "", "Five line"]) |
||
442 | self.assertEqual(self.txt.cursorColumn, 0) |
||
443 | self.assertEqual(self.txt.cursorRow, 4) |
||
444 | |||
445 | def test_up(self): |
||
446 | evt = self.Evt(pygame.K_UP) |
||
447 | self.txt.cursorColumn = 3 |
||
448 | self.txt.cursorRow = 5 |
||
449 | for x in range(3): |
||
450 | Text.processKeyDown(self.txt, evt) |
||
451 | self.assertEqual(self.txt.cursorColumn, 0) |
||
452 | self.assertEqual(self.txt.cursorRow, 2) |
||
453 | self.txt.cursorColumn = 3 |
||
454 | Text.processKeyDown(self.txt, evt) |
||
455 | self.assertEqual(self.txt.cursorColumn, 3) |
||
456 | |||
457 | def test_down(self): |
||
458 | evt = self.Evt(pygame.K_DOWN) |
||
459 | self.txt.cursorColumn = 3 |
||
460 | self.txt.cursorRow = 1 |
||
461 | Text.processKeyDown(self.txt, evt) |
||
462 | self.assertEqual(self.txt.cursorColumn, 3) |
||
463 | for x in range(3): |
||
464 | Text.processKeyDown(self.txt, evt) |
||
465 | self.assertEqual(self.txt.cursorColumn, 0) |
||
466 | self.assertEqual(self.txt.cursorRow, 5) |
||
467 | |||
468 | def test_left(self): |
||
469 | evt = self.Evt(pygame.K_LEFT) |
||
470 | self.txt.cursorColumn = 3 |
||
471 | self.txt.cursorRow = 2 |
||
472 | Text.processKeyDown(self.txt, evt) |
||
473 | self.assertEqual(self.txt.cursorColumn, 2) |
||
474 | for x in range(12): |
||
475 | Text.processKeyDown(self.txt, evt) |
||
476 | self.assertEqual(self.txt.cursorColumn, 9) |
||
477 | self.assertEqual(self.txt.cursorRow, 0) |
||
478 | |||
479 | def test_right(self): |
||
480 | evt = self.Evt(pygame.K_RIGHT) |
||
481 | self.txt.cursorColumn = 3 |
||
482 | self.txt.cursorRow = 2 |
||
483 | Text.processKeyDown(self.txt, evt) |
||
484 | self.assertEqual(self.txt.cursorColumn, 4) |
||
485 | for x in range(12): |
||
486 | Text.processKeyDown(self.txt, evt) |
||
487 | self.assertEqual(self.txt.cursorColumn, 5) |
||
488 | self.assertEqual(self.txt.cursorRow, 5) |
||
489 | |||
490 | def test_selection_movement(self): |
||
491 | evt = self.Evt(pygame.K_RIGHT, mod=pygame.KMOD_RSHIFT) |
||
492 | self.txt.cursorColumn = 3 |
||
493 | self.txt.cursorRow = 2 |
||
494 | for x in range(10): |
||
495 | Text.processKeyDown(self.txt, evt) |
||
496 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"]) |
||
497 | self.assertEqual(self.txt.selection.first, (2, 3)) |
||
498 | self.assertEqual(self.txt.selection.last, (5, 2)) |
||
499 | |||
500 | evt = self.Evt(pygame.K_LEFT, mod=pygame.KMOD_LSHIFT) |
||
501 | for x in range(3): |
||
502 | Text.processKeyDown(self.txt, evt) |
||
503 | self.assertEqual(self.txt.selection.first, (2, 3)) |
||
504 | self.assertEqual(self.txt.selection.last, (4, 0)) |
||
505 | |||
506 | evt = self.Evt(pygame.K_UP, mod=pygame.KMOD_LSHIFT) |
||
507 | for x in range(3): |
||
508 | Text.processKeyDown(self.txt, evt) |
||
509 | self.assertEqual(self.txt.selection.first, (1, 0)) |
||
510 | self.assertEqual(self.txt.selection.last, (2, 3)) |
||
511 | |||
512 | def test_selection_delete(self): |
||
513 | evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT) |
||
514 | self.txt.cursorColumn = 3 |
||
515 | self.txt.cursorRow = 0 |
||
516 | Text.processKeyDown(self.txt, evt) |
||
517 | Text.processKeyDown(self.txt, self.Evt(pygame.K_DELETE)) |
||
518 | self.assertEqual(self.txt.text, ["Zer line", "Two line", "", "", "Five line"]) |
||
519 | self.assertIs(self.txt.selection.first, None) |
||
520 | self.assertIs(self.txt.selection.last, None) |
||
521 | |||
522 | evt = self.Evt(pygame.K_RIGHT, mod=pygame.KMOD_LSHIFT) |
||
523 | for x in range(3): |
||
524 | Text.processKeyDown(self.txt, evt) |
||
525 | Text.processKeyDown(self.txt, self.Evt(pygame.K_DELETE)) |
||
526 | self.assertEqual(self.txt.text, ["Zerne", "Two line", "", "", "Five line"]) |
||
527 | |||
528 | evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT) |
||
529 | for x in range(4): |
||
530 | Text.processKeyDown(self.txt, evt) |
||
531 | Text.processKeyDown(self.txt, self.Evt(pygame.K_DELETE)) |
||
532 | self.assertEqual(self.txt.text, ["ZerFive line"]) |
||
533 | |||
534 | def test_selection_backspace(self): |
||
535 | evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT) |
||
536 | self.txt.cursorColumn = 3 |
||
537 | self.txt.cursorRow = 0 |
||
538 | Text.processKeyDown(self.txt, evt) |
||
539 | Text.processKeyDown(self.txt, self.Evt(pygame.K_BACKSPACE)) |
||
540 | self.assertEqual(self.txt.text, ["Zer line", "Two line", "", "", "Five line"]) |
||
541 | self.assertIs(self.txt.selection.first, None) |
||
542 | self.assertIs(self.txt.selection.last, None) |
||
543 | |||
544 | def test_selection_return(self): |
||
545 | evt = self.Evt(pygame.K_DOWN, mod=pygame.KMOD_LSHIFT) |
||
546 | self.txt.cursorColumn = 3 |
||
547 | self.txt.cursorRow = 0 |
||
548 | Text.processKeyDown(self.txt, evt) |
||
549 | Text.processKeyDown(self.txt, self.Evt(pygame.K_RETURN)) |
||
550 | self.assertEqual(self.txt.text, ["Zer", " line", "Two line", "", "", "Five line"]) |
||
551 | self.assertIs(self.txt.selection.first, None) |
||
552 | self.assertIs(self.txt.selection.last, None) |
||
553 | |||
554 | def test_move_word_left(self): |
||
555 | evt = self.Evt(pygame.K_LEFT, mod=pygame.KMOD_LCTRL) |
||
556 | self.txt.cursorColumn = 3 |
||
557 | self.txt.cursorRow = 5 |
||
558 | for x in range(2): |
||
559 | Text.processKeyDown(self.txt, evt) |
||
560 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"]) |
||
561 | self.assertEqual(self.txt.cursorColumn, 4) |
||
562 | self.assertEqual(self.txt.cursorRow, 2) |
||
563 | Text.processKeyDown(self.txt, evt) |
||
564 | self.assertEqual(self.txt.cursorColumn, 0) |
||
565 | self.assertEqual(self.txt.cursorRow, 2) |
||
566 | |||
567 | def test_move_word_right(self): |
||
568 | evt = self.Evt(pygame.K_RIGHT, mod=pygame.KMOD_RCTRL) |
||
569 | self.txt.cursorColumn = 1 |
||
570 | self.txt.cursorRow = 0 |
||
571 | Text.processKeyDown(self.txt, evt) |
||
572 | self.assertEqual(self.txt.text, ["Zero line", "One line", "Two line", "", "", "Five line"]) |
||
573 | self.assertEqual(self.txt.cursorColumn, 4) |
||
574 | self.assertEqual(self.txt.cursorRow, 0) |
||
575 | for x in range(4): |
||
576 | Text.processKeyDown(self.txt, evt) |
||
577 | self.assertEqual(self.txt.cursorColumn, 3) |
||
578 | self.assertEqual(self.txt.cursorRow, 2) |
||
579 | for x in range(2): |
||
580 | Text.processKeyDown(self.txt, evt) |
||
581 | self.assertEqual(self.txt.cursorColumn, 4) |
||
582 | self.assertEqual(self.txt.cursorRow, 5) |
||
583 |