Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cfde_texteditengine_unittest.cpp
Go to the documentation of this file.
1// Copyright 2017 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "xfa/fde/cfde_texteditengine.h"
6
7#include "core/fxcrt/fx_codepage.h"
8#include "core/fxcrt/fx_extension.h"
9#include "core/fxge/text_char_pos.h"
10#include "testing/gtest/include/gtest/gtest.h"
11#include "testing/xfa_test_environment.h"
12#include "xfa/fgas/font/cfgas_gefont.h"
13
15 public:
16 class Delegate final : public CFDE_TextEditEngine::Delegate {
17 public:
18 void Reset() {
19 text_is_full = false;
20 fail_validation = false;
21 }
22
23 void NotifyTextFull() override { text_is_full = true; }
24
25 void OnCaretChanged() override {}
26 void OnTextWillChange(CFDE_TextEditEngine::TextChange* change) override {}
27 void OnTextChanged() override {}
28 void OnSelChanged() override {}
29 bool OnValidate(const WideString& wsText) override {
30 return !fail_validation;
31 }
32 void SetScrollOffset(float fScrollOffset) override {}
33
34 bool fail_validation = false;
35 bool text_is_full = false;
36 };
37
40
41 void SetUp() override {
42 const wchar_t kFontFamily[] = L"Arimo Bold";
43 font_ = CFGAS_GEFont::LoadFont(kFontFamily, 0, FX_CodePage::kDefANSI);
44 ASSERT_TRUE(font_);
45
46 engine_ = std::make_unique<CFDE_TextEditEngine>();
47 engine_->SetFont(font_);
48 engine_->SetFontSize(12.0f);
49 }
50
52 engine_.reset();
53 font_.Reset();
54 }
55
56 CFDE_TextEditEngine* engine() const { return engine_.get(); }
57
58 private:
59 RetainPtr<CFGAS_GEFont> font_;
60 std::unique_ptr<CFDE_TextEditEngine> engine_;
61};
62
64 EXPECT_STREQ(L"", engine()->GetText().c_str());
65
66 engine()->Insert(0, L"");
67 EXPECT_STREQ(L"", engine()->GetText().c_str());
68 EXPECT_EQ(0U, engine()->GetLength());
69
70 engine()->Insert(0, L"Hello");
71 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
72 EXPECT_EQ(5U, engine()->GetLength());
73
74 engine()->Insert(5, L" World");
75 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
76 EXPECT_EQ(11U, engine()->GetLength());
77
78 engine()->Insert(5, L" New");
79 EXPECT_STREQ(L"Hello New World", engine()->GetText().c_str());
80
81 engine()->Insert(100, L" Cat");
82 EXPECT_STREQ(L"Hello New World Cat", engine()->GetText().c_str());
83
84 engine()->Clear();
85
86 engine()->SetHasCharacterLimit(true);
87 engine()->SetCharacterLimit(5);
88 engine()->Insert(0, L"Hello");
89
90 // No delegate
91 engine()->Insert(5, L" World");
92 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
93
94 engine()->SetCharacterLimit(8);
95 engine()->Insert(5, L" World");
96 EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
97
98 engine()->Clear();
99
100 // With Delegate
101 auto delegate = std::make_unique<CFDE_TextEditEngineTest::Delegate>();
102 engine()->SetDelegate(delegate.get());
103
104 engine()->SetCharacterLimit(5);
105 engine()->Insert(0, L"Hello");
106
107 // Insert when full.
108 engine()->Insert(5, L" World");
109 EXPECT_TRUE(delegate->text_is_full);
110 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
111 delegate->Reset();
112
113 engine()->SetCharacterLimit(8);
114 engine()->Insert(5, L" World");
115 EXPECT_TRUE(delegate->text_is_full);
116 EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
117 delegate->Reset();
118 engine()->SetHasCharacterLimit(false);
119
120 engine()->Clear();
121 engine()->Insert(0, L"Hello");
122
123 // Insert Invalid text
124 delegate->fail_validation = true;
125 engine()->EnableValidation(true);
126 engine()->Insert(5, L" World");
127 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
128
129 delegate->fail_validation = false;
130 engine()->Insert(5, L" World");
131 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
132 engine()->EnableValidation(false);
133
134 engine()->Clear();
135
136 engine()->Insert(0, L"Hello\nWorld");
137 EXPECT_FALSE(delegate->text_is_full);
138 EXPECT_STREQ(L"Hello\nWorld", engine()->GetText().c_str());
139 delegate->Reset();
140 engine()->Clear();
141
142 // Insert with limited area and over-fill
143 engine()->LimitHorizontalScroll(true);
144 engine()->SetAvailableWidth(52.0f); // Fits 'Hello Wo'.
145 engine()->Insert(0, L"Hello");
146 EXPECT_FALSE(delegate->text_is_full);
147 engine()->Insert(5, L" World");
148 EXPECT_TRUE(delegate->text_is_full);
149 EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
150 engine()->LimitHorizontalScroll(false);
151
152 delegate->Reset();
153 engine()->Clear();
154
155 engine()->SetLineSpace(12.0f);
156 engine()->LimitVerticalScroll(true);
157 // Default is one line of text.
158 engine()->Insert(0, L"Hello");
159 EXPECT_FALSE(delegate->text_is_full);
160 engine()->Insert(5, L" Wo\nrld");
161 EXPECT_TRUE(delegate->text_is_full);
162 EXPECT_STREQ(L"Hello Wo\n", engine()->GetText().c_str());
163 engine()->LimitVerticalScroll(false);
164
165 engine()->SetDelegate(nullptr);
166}
167
169 engine()->SetHasCharacterLimit(true);
170 engine()->Insert(0, L"Hello World");
171 engine()->SetCharacterLimit(5);
172 engine()->Insert(0, L"Not Inserted before ");
173 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
174
175 engine()->SetHasCharacterLimit(false);
176 engine()->Insert(0, L"Inserted before ");
177 engine()->SetHasCharacterLimit(true);
178 engine()->Insert(0, L"Not Inserted before ");
179 EXPECT_STREQ(L"Inserted before Hello World", engine()->GetText().c_str());
180}
181
183 engine()->SetHasCharacterLimit(true);
184 engine()->SetCharacterLimit(8);
185 engine()->Insert(0, L"Hello");
186 engine()->Insert(5, L" World",
187 CFDE_TextEditEngine::RecordOperation::kSkipNotify);
188 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
189
190 engine()->Insert(0, L"Not inserted");
191 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
192
193 engine()->Delete(5, 1);
194 EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str());
195
196 engine()->Insert(0, L"****");
197 EXPECT_STREQ(L"*HelloWorld", engine()->GetText().c_str());
198}
199
201 engine()->Insert(0, L"||");
202 for (size_t i = 1; i < 1023; ++i) {
203 engine()->Insert(i, L"a");
204 }
205 WideString result = engine()->GetText();
206 ASSERT_EQ(result.GetLength(), 1024u);
207 EXPECT_EQ(result[0], L'|');
208 EXPECT_EQ(result[1], L'a');
209 EXPECT_EQ(result[2], L'a');
210 // ...
211 EXPECT_EQ(result[1022], L'a');
212 EXPECT_EQ(result[1023], L'|');
213}
214
216 EXPECT_STREQ(L"", engine()->Delete(0, 50).c_str());
217 EXPECT_STREQ(L"", engine()->GetText().c_str());
218
219 engine()->Insert(0, L"Hello World");
220 EXPECT_STREQ(L" World", engine()->Delete(5, 6).c_str());
221 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
222
223 engine()->Clear();
224 engine()->Insert(0, L"Hello World");
225 EXPECT_STREQ(L" ", engine()->Delete(5, 1).c_str());
226 EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str());
227
228 EXPECT_STREQ(L"elloWorld", engine()->Delete(1, 50).c_str());
229 EXPECT_STREQ(L"H", engine()->GetText().c_str());
230}
231
233 EXPECT_STREQ(L"", engine()->GetText().c_str());
234
235 engine()->Clear();
236 EXPECT_STREQ(L"", engine()->GetText().c_str());
237
238 engine()->Insert(0, L"Hello World");
239 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
240
241 engine()->Clear();
242 EXPECT_STREQ(L"", engine()->GetText().c_str());
243 EXPECT_EQ(0U, engine()->GetLength());
244}
245
247 // Out of bounds.
248 EXPECT_EQ(L'\0', engine()->GetChar(0));
249
250 engine()->Insert(0, L"Hello World");
251 EXPECT_EQ(L'H', engine()->GetChar(0));
252 EXPECT_EQ(L'd', engine()->GetChar(engine()->GetLength() - 1));
253 EXPECT_EQ(L' ', engine()->GetChar(5));
254
255 engine()->Insert(5, L" A");
256 EXPECT_STREQ(L"Hello A World", engine()->GetText().c_str());
257 EXPECT_EQ(L'W', engine()->GetChar(8));
258
259 engine()->EnablePasswordMode(true);
260 EXPECT_EQ(L'*', engine()->GetChar(8));
261
262 engine()->SetAliasChar(L'+');
263 EXPECT_EQ(L'+', engine()->GetChar(8));
264}
265
267 // Out of Bounds.
268 EXPECT_EQ(0, engine()->GetWidthOfChar(0));
269
270 engine()->Insert(0, L"Hello World");
271 EXPECT_EQ(173280, engine()->GetWidthOfChar(0));
272 EXPECT_EQ(133440, engine()->GetWidthOfChar(1));
273
274 engine()->Insert(0, L"\t");
275 EXPECT_EQ(0, engine()->GetWidthOfChar(0));
276}
277
279 EXPECT_EQ(0U, engine()->GetDisplayPos(FDE_TEXTEDITPIECE()).size());
280}
281
283 EXPECT_FALSE(engine()->HasSelection());
284 engine()->SelectAll();
285 EXPECT_FALSE(engine()->HasSelection());
286
287 engine()->Insert(0, L"Hello World");
288 EXPECT_STREQ(L"", engine()->DeleteSelectedText().c_str());
289
290 EXPECT_FALSE(engine()->HasSelection());
291 engine()->SelectAll();
292 EXPECT_TRUE(engine()->HasSelection());
293 EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str());
294
295 engine()->ClearSelection();
296 EXPECT_FALSE(engine()->HasSelection());
297 EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
298
299 engine()->SelectAll();
300 size_t start_idx;
301 size_t count;
302 std::tie(start_idx, count) = engine()->GetSelection();
303 EXPECT_EQ(0U, start_idx);
304 EXPECT_EQ(11U, count);
305
306 // Selection before gap.
307 EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str());
308 EXPECT_TRUE(engine()->HasSelection());
309 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
310
311 engine()->Insert(5, L" A");
312 EXPECT_FALSE(engine()->HasSelection());
313 EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
314
315 // Selection over the gap.
316 engine()->SelectAll();
317 EXPECT_TRUE(engine()->HasSelection());
318 EXPECT_STREQ(L"Hello A World", engine()->GetSelectedText().c_str());
319 engine()->Clear();
320
321 engine()->Insert(0, L"Hello World");
322 engine()->SelectAll();
323
324 EXPECT_STREQ(L"Hello World", engine()->DeleteSelectedText().c_str());
325 EXPECT_FALSE(engine()->HasSelection());
326 EXPECT_STREQ(L"", engine()->GetText().c_str());
327
328 engine()->Insert(0, L"Hello World");
329 engine()->SetSelection(5, 5);
330 EXPECT_STREQ(L" Worl", engine()->DeleteSelectedText().c_str());
331 EXPECT_FALSE(engine()->HasSelection());
332 EXPECT_STREQ(L"Hellod", engine()->GetText().c_str());
333
334 engine()->Clear();
335 engine()->Insert(0, L"Hello World");
336 engine()->SelectAll();
337 engine()->ReplaceSelectedText(L"Goodbye Everybody");
338 EXPECT_FALSE(engine()->HasSelection());
339 EXPECT_STREQ(L"Goodbye Everybody", engine()->GetText().c_str());
340
341 engine()->Clear();
342 engine()->Insert(0, L"Hello World");
343 engine()->SetSelection(1, 4);
344 engine()->ReplaceSelectedText(L"i,");
345 EXPECT_FALSE(engine()->HasSelection());
346 EXPECT_STREQ(L"Hi, World", engine()->GetText().c_str());
347
348 // Selection fully after gap.
349 engine()->Clear();
350 engine()->Insert(0, L"Hello");
351 engine()->Insert(0, L"A ");
352 engine()->SetSelection(3, 6);
353 EXPECT_STREQ(L"ello", engine()->GetSelectedText().c_str());
354
355 engine()->Clear();
356 engine()->Insert(0, L"Hello World");
357 engine()->ClearSelection();
358 engine()->DeleteSelectedText();
359 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
360}
361
363 EXPECT_FALSE(engine()->CanUndo());
364 EXPECT_FALSE(engine()->CanRedo());
365 EXPECT_FALSE(engine()->Undo());
366 EXPECT_FALSE(engine()->Redo());
367
368 engine()->Insert(0, L"Hello");
369 EXPECT_TRUE(engine()->CanUndo());
370 EXPECT_FALSE(engine()->CanRedo());
371 EXPECT_TRUE(engine()->Undo());
372 EXPECT_STREQ(L"", engine()->GetText().c_str());
373 EXPECT_FALSE(engine()->CanUndo());
374 EXPECT_TRUE(engine()->CanRedo());
375 EXPECT_TRUE(engine()->Redo());
376 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
377 EXPECT_TRUE(engine()->CanUndo());
378 EXPECT_FALSE(engine()->CanRedo());
379
380 engine()->Clear();
381 EXPECT_FALSE(engine()->CanUndo());
382 EXPECT_FALSE(engine()->CanRedo());
383
384 engine()->Insert(0, L"Hello World");
385 engine()->SelectAll();
386 engine()->DeleteSelectedText();
387 EXPECT_STREQ(L"", engine()->GetText().c_str());
388 EXPECT_TRUE(engine()->CanUndo());
389 EXPECT_TRUE(engine()->Undo());
390 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
391 EXPECT_TRUE(engine()->CanRedo());
392 EXPECT_TRUE(engine()->Redo());
393 EXPECT_STREQ(L"", engine()->GetText().c_str());
394 EXPECT_TRUE(engine()->CanUndo());
395 EXPECT_FALSE(engine()->CanRedo());
396
397 engine()->Insert(0, L"Hello World");
398 engine()->SelectAll();
399 engine()->ReplaceSelectedText(L"Goodbye Friend");
400 EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str());
401 EXPECT_TRUE(engine()->CanUndo());
402 EXPECT_TRUE(engine()->Undo());
403 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
404 EXPECT_TRUE(engine()->CanRedo());
405 EXPECT_TRUE(engine()->Redo());
406 EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str());
407
408 engine()->Clear();
409 engine()->SetMaxEditOperationsForTesting(3);
410 engine()->Insert(0, L"First ");
411 engine()->Insert(engine()->GetLength(), L"Second ");
412 engine()->Insert(engine()->GetLength(), L"Third");
413
414 EXPECT_TRUE(engine()->CanUndo());
415 EXPECT_TRUE(engine()->Undo());
416 EXPECT_STREQ(L"First Second ", engine()->GetText().c_str());
417 EXPECT_TRUE(engine()->CanUndo());
418 EXPECT_TRUE(engine()->Undo());
419 EXPECT_FALSE(
420 engine()->CanUndo()); // Can't undo First; undo buffer too small.
421 EXPECT_STREQ(L"First ", engine()->GetText().c_str());
422
423 EXPECT_TRUE(engine()->CanRedo());
424 EXPECT_TRUE(engine()->Redo());
425 EXPECT_TRUE(engine()->CanRedo());
426 EXPECT_TRUE(engine()->Redo());
427 EXPECT_FALSE(engine()->CanRedo());
428 EXPECT_STREQ(L"First Second Third", engine()->GetText().c_str());
429
430 engine()->Clear();
431
432 engine()->SetMaxEditOperationsForTesting(4);
433
434 // Go beyond the max operations limit.
435 engine()->Insert(0, L"H");
436 engine()->Insert(1, L"e");
437 engine()->Insert(2, L"l");
438 engine()->Insert(3, L"l");
439 engine()->Insert(4, L"o");
440 engine()->Insert(5, L" World");
441 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
442
443 // Do A, undo. Do B, undo. Redo should cause B.
444 engine()->Delete(4, 3);
445 EXPECT_STREQ(L"Hellorld", engine()->GetText().c_str());
446 EXPECT_TRUE(engine()->Undo());
447 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
448 engine()->Delete(5, 6);
449 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
450 EXPECT_TRUE(engine()->Undo());
451 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
452 EXPECT_TRUE(engine()->Redo());
453 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
454
455 // Undo down to the limit.
456 EXPECT_TRUE(engine()->Undo());
457 EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
458 EXPECT_TRUE(engine()->Undo());
459 EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
460 EXPECT_TRUE(engine()->Undo());
461 EXPECT_STREQ(L"Hell", engine()->GetText().c_str());
462 EXPECT_FALSE(engine()->Undo());
463 EXPECT_STREQ(L"Hell", engine()->GetText().c_str());
464}
465
467 engine()->SetFontSize(10.0f);
468 engine()->Insert(0, L"Hello World");
469 EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
470 EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
471 EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
472 EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
473 EXPECT_EQ(2U, engine()->GetIndexForPoint({10.0f, 5.0f}));
474}
475
477 engine()->SetFontSize(10.0f);
478 engine()->Insert(0,
479 L"A text long enough to span multiple lines and test "
480 L"getting indexes on multi-line edits.");
481 EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
482 EXPECT_EQ(87U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
483 EXPECT_EQ(18U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
484 EXPECT_EQ(19U, engine()->GetIndexForPoint({1.0f, 10.0f}));
485 EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
486 EXPECT_EQ(2U, engine()->GetIndexForPoint({10.0f, 5.0f}));
487}
488
490 engine()->SetFontSize(10.0f);
491 engine()->Insert(0, L"Hello World ");
492 EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
493 EXPECT_EQ(12U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
494 EXPECT_EQ(12U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
495}
496
498 engine()->SetFontSize(10.0f);
499 engine()->Insert(0, L"Hello\nWorld");
500 EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
501 EXPECT_EQ(5U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
502 EXPECT_EQ(6U, engine()->GetIndexForPoint({0.0f, 10.0f}));
503 EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
504}
505
507 RetainPtr<CFGAS_GEFont> font = engine()->GetFont();
508 ASSERT_TRUE(font);
509
510 // Has font but no text.
511 EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
512
513 // Has font and text.
514 engine()->Insert(0, L"Hi!");
515 EXPECT_TRUE(engine()->CanGenerateCharacterInfo());
516
517 // Has text but no font.
518 engine()->SetFont(nullptr);
519 EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
520
521 // Has no text and no font.
522 engine()->Clear();
523 EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
524}
525
527 std::pair<int32_t, CFX_RectF> char_info;
528
529 engine()->Insert(0, L"Hi!");
530 ASSERT_EQ(3U, engine()->GetLength());
531
532 char_info = engine()->GetCharacterInfo(0);
533 EXPECT_EQ(0, char_info.first);
534 EXPECT_FLOAT_EQ(0.0f, char_info.second.Left());
535 EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
536 EXPECT_FLOAT_EQ(8.664f, char_info.second.Width());
537 EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
538
539 char_info = engine()->GetCharacterInfo(1);
540 EXPECT_EQ(0, char_info.first);
541 EXPECT_FLOAT_EQ(8.664f, char_info.second.Left());
542 EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
543 EXPECT_FLOAT_EQ(3.324f, char_info.second.Width());
544 EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
545
546 char_info = engine()->GetCharacterInfo(2);
547 EXPECT_EQ(0, char_info.first);
548 EXPECT_FLOAT_EQ(11.988f, char_info.second.Left());
549 EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
550 EXPECT_FLOAT_EQ(3.996f, char_info.second.Width());
551 EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
552
553 // Allow retrieving the character info for the end of the text, as that
554 // information can be used to determine where to draw a cursor positioned at
555 // the end.
556 char_info = engine()->GetCharacterInfo(3);
557 EXPECT_EQ(0, char_info.first);
558 EXPECT_FLOAT_EQ(15.984, char_info.second.Left());
559 EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
560 EXPECT_FLOAT_EQ(0.0f, char_info.second.Width());
561 EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
562}
563
565 size_t start_idx;
566 size_t count;
567
568 std::tie(start_idx, count) = engine()->BoundsForWordAt(100);
569 EXPECT_EQ(0U, start_idx);
570 EXPECT_EQ(0U, count);
571 engine()->SetSelection(start_idx, count);
572 EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
573
574 engine()->Clear();
575 engine()->Insert(0, L"Hello");
576 std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
577 EXPECT_EQ(0U, start_idx);
578 EXPECT_EQ(5U, count);
579 engine()->SetSelection(start_idx, count);
580 EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
581
582 engine()->Clear();
583 engine()->Insert(0, L"Hello World");
584 std::tie(start_idx, count) = engine()->BoundsForWordAt(100);
585 EXPECT_EQ(0U, start_idx);
586 EXPECT_EQ(0U, count);
587 engine()->SetSelection(start_idx, count);
588 EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
589
590 std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
591 EXPECT_EQ(0U, start_idx);
592 EXPECT_EQ(5U, count);
593 engine()->SetSelection(start_idx, count);
594 EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
595
596 std::tie(start_idx, count) = engine()->BoundsForWordAt(1);
597 EXPECT_EQ(0U, start_idx);
598 EXPECT_EQ(5U, count);
599 engine()->SetSelection(start_idx, count);
600 EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
601
602 std::tie(start_idx, count) = engine()->BoundsForWordAt(4);
603 EXPECT_EQ(0U, start_idx);
604 EXPECT_EQ(5U, count);
605 engine()->SetSelection(start_idx, count);
606 EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
607
608 // Select the space
609 std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
610 EXPECT_EQ(5U, start_idx);
611 EXPECT_EQ(1U, count);
612 engine()->SetSelection(start_idx, count);
613 EXPECT_STREQ(L" ", engine()->GetSelectedText().c_str());
614
615 std::tie(start_idx, count) = engine()->BoundsForWordAt(6);
616 EXPECT_EQ(6U, start_idx);
617 EXPECT_EQ(5U, count);
618 engine()->SetSelection(start_idx, count);
619 EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
620
621 engine()->Clear();
622 engine()->Insert(0, L"123 456 789");
623 std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
624 engine()->SetSelection(start_idx, count);
625 EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str());
626
627 engine()->Clear();
628 engine()->Insert(0, L"123def789");
629 std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
630 engine()->SetSelection(start_idx, count);
631 EXPECT_STREQ(L"123def789", engine()->GetSelectedText().c_str());
632
633 engine()->Clear();
634 engine()->Insert(0, L"abc456ghi");
635 std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
636 engine()->SetSelection(start_idx, count);
637 EXPECT_STREQ(L"abc456ghi", engine()->GetSelectedText().c_str());
638
639 engine()->Clear();
640 engine()->Insert(0, L"hello, world");
641 std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
642 engine()->SetSelection(start_idx, count);
643 EXPECT_STREQ(L"hello", engine()->GetSelectedText().c_str());
644
645 engine()->Clear();
646 engine()->Insert(0, L"hello, world");
647 std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
648 engine()->SetSelection(start_idx, count);
649 EXPECT_STREQ(L",", engine()->GetSelectedText().c_str());
650
651 engine()->Clear();
652 engine()->Insert(0, L"np-complete");
653 std::tie(start_idx, count) = engine()->BoundsForWordAt(6);
654 engine()->SetSelection(start_idx, count);
655 EXPECT_STREQ(L"complete", engine()->GetSelectedText().c_str());
656
657 engine()->Clear();
658 engine()->Insert(0, L"(123) 456-7890");
659 std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
660 engine()->SetSelection(start_idx, count);
661 EXPECT_STREQ(L"(", engine()->GetSelectedText().c_str());
662
663 std::tie(start_idx, count) = engine()->BoundsForWordAt(1);
664 engine()->SetSelection(start_idx, count);
665 EXPECT_STREQ(L"123", engine()->GetSelectedText().c_str());
666
667 std::tie(start_idx, count) = engine()->BoundsForWordAt(7);
668 engine()->SetSelection(start_idx, count);
669 EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str());
670
671 std::tie(start_idx, count) = engine()->BoundsForWordAt(11);
672 engine()->SetSelection(start_idx, count);
673 EXPECT_STREQ(L"7890", engine()->GetSelectedText().c_str());
674
675 // Tests from:
676 // http://unicode.org/Public/UNIDATA/auxiliary/WordBreakTest.html#samples
677 struct bounds {
678 size_t start;
679 size_t end;
680 };
681 struct {
682 const wchar_t* str;
683 std::vector<const wchar_t*> results;
684 } tests[] = {
685 // {L"\r\na\n\u0308", {L"\r\n", L"a", L"\n", L"\u0308"}},
686 // {L"a\u0308", {L"a\u0308"}},
687 // {L" \u200d\u0646", {L" \u200d", L"\u0646"}},
688 // {L"\u0646\u200d ", {L"\u0646\u200d", L" "}},
689 {L"AAA", {L"AAA"}},
690 {L"A:A", {L"A:A"}},
691 {L"A::A", {L"A", L":", L":", L"A"}},
692 // {L"\u05d0'", {L"\u05d0'"}},
693 // {L"\u05d0\"\u05d0", {L"\u05d0\"\u05d0"}},
694 {L"A00A", {L"A00A"}},
695 {L"0,0", {L"0,0"}},
696 {L"0,,0", {L"0", L",", L",", L"0"}},
697 {L"\u3031\u3031", {L"\u3031\u3031"}},
698 {L"A_0_\u3031_", {L"A_0_\u3031_"}},
699 {L"A__A", {L"A__A"}},
700 // {L"\u200d\u2640", {L"\u200d\u2640"}},
701 // {L"a\u0308\u200b\u0308b", {L"a\u0308\u200b\u0308b"}},
702 };
703
704 for (auto t : tests) {
705 engine()->Clear();
706 engine()->Insert(0, t.str);
707
708 size_t idx = 0;
709 for (const auto* res : t.results) {
710 std::tie(start_idx, count) = engine()->BoundsForWordAt(idx);
711 engine()->SetSelection(start_idx, count);
712 EXPECT_STREQ(res, engine()->GetSelectedText().c_str())
713 << "Input: '" << t.str << "'";
714 idx += count;
715 }
716 }
717}
718
720 engine()->Clear();
721 engine()->Insert(0, L"Hello");
722
723 EXPECT_EQ(0U, engine()->GetIndexLeft(0));
724 EXPECT_EQ(5U, engine()->GetIndexRight(5));
725 EXPECT_EQ(2U, engine()->GetIndexUp(2));
726 EXPECT_EQ(2U, engine()->GetIndexDown(2));
727 EXPECT_EQ(1U, engine()->GetIndexLeft(2));
728 EXPECT_EQ(3U, engine()->GetIndexRight(2));
729 EXPECT_EQ(0U, engine()->GetIndexAtStartOfLine(2));
730 EXPECT_EQ(5U, engine()->GetIndexAtEndOfLine(2));
731
732 engine()->Clear();
733 engine()->Insert(0, L"The book is \"مدخل إلى C++\"");
734 EXPECT_FALSE(FX_IsOdd(engine()->GetCharacterInfo(3).first));
735 EXPECT_EQ(2U, engine()->GetIndexLeft(3));
736 EXPECT_EQ(4U, engine()->GetIndexRight(3));
737 EXPECT_TRUE(FX_IsOdd(engine()->GetCharacterInfo(15).first));
738 EXPECT_EQ(14U, engine()->GetIndexLeft(15));
739 EXPECT_EQ(16U, engine()->GetIndexRight(15));
740 EXPECT_FALSE(FX_IsOdd(engine()->GetCharacterInfo(23).first));
741 EXPECT_EQ(22U, engine()->GetIndexLeft(23));
742 EXPECT_EQ(24U, engine()->GetIndexRight(23));
743
744 engine()->Clear();
745 engine()->Insert(0, L"Hello\r\nWorld\r\nTest");
746 // Move to end of Hello from start of World.
747 engine()->SetSelection(engine()->GetIndexLeft(7U), 7);
748 EXPECT_STREQ(L"\r\nWorld", engine()->GetSelectedText().c_str());
749
750 // Second letter in Hello from second letter in World.
751 engine()->SetSelection(engine()->GetIndexUp(8U), 2);
752 EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
753
754 // Second letter in World from second letter in Test.
755 engine()->SetSelection(engine()->GetIndexUp(15U), 2);
756 EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
757
758 // Second letter in World from second letter in Hello.
759 engine()->SetSelection(engine()->GetIndexDown(1U), 2);
760 EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
761
762 // Second letter in Test from second letter in World.
763 engine()->SetSelection(engine()->GetIndexDown(8U), 2);
764 EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
765
766 size_t start_idx = engine()->GetIndexAtStartOfLine(8U);
767 size_t end_idx = engine()->GetIndexAtEndOfLine(8U);
768 engine()->SetSelection(start_idx, end_idx - start_idx);
769 EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
770
771 // Move past \r\n to before W.
772 engine()->SetSelection(engine()->GetIndexRight(5U), 5);
773 EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
774
775 engine()->Clear();
776 engine()->Insert(0, L"Short\nAnd a very long line");
777 engine()->SetSelection(engine()->GetIndexUp(14U), 11);
778 EXPECT_STREQ(L"\nAnd a very", engine()->GetSelectedText().c_str());
779
780 engine()->Clear();
781 engine()->Insert(0, L"A Very long line\nShort");
782 EXPECT_EQ(engine()->GetLength(), engine()->GetIndexDown(8U));
783
784 engine()->Clear();
785 engine()->Insert(0, L"Hello\rWorld\rTest");
786 // Move to end of Hello from start of World.
787 engine()->SetSelection(engine()->GetIndexLeft(6U), 6);
788 EXPECT_STREQ(L"\rWorld", engine()->GetSelectedText().c_str());
789
790 // Second letter in Hello from second letter in World.
791 engine()->SetSelection(engine()->GetIndexUp(7U), 2);
792 EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
793
794 // Second letter in World from second letter in Test.
795 engine()->SetSelection(engine()->GetIndexUp(13U), 2);
796 EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
797
798 // Second letter in World from second letter in Hello.
799 engine()->SetSelection(engine()->GetIndexDown(1U), 2);
800 EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
801
802 // Second letter in Test from second letter in World.
803 engine()->SetSelection(engine()->GetIndexDown(7U), 2);
804 EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
805
806 start_idx = engine()->GetIndexAtStartOfLine(7U);
807 end_idx = engine()->GetIndexAtEndOfLine(7U);
808 engine()->SetSelection(start_idx, end_idx - start_idx);
809 EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
810
811 // Move past \r to before W.
812 engine()->SetSelection(engine()->GetIndexRight(5U), 5);
813 EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
814
815 engine()->Clear();
816 engine()->Insert(0, L"Hello\nWorld\nTest");
817 // Move to end of Hello from start of World.
818 engine()->SetSelection(engine()->GetIndexLeft(6U), 6);
819 EXPECT_STREQ(L"\nWorld", engine()->GetSelectedText().c_str());
820
821 // Second letter in Hello from second letter in World.
822 engine()->SetSelection(engine()->GetIndexUp(7U), 2);
823 EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
824
825 // Second letter in World from second letter in Test.
826 engine()->SetSelection(engine()->GetIndexUp(13U), 2);
827 EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
828
829 // Second letter in World from second letter in Hello.
830 engine()->SetSelection(engine()->GetIndexDown(1U), 2);
831 EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
832
833 // Second letter in Test from second letter in World.
834 engine()->SetSelection(engine()->GetIndexDown(7U), 2);
835 EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
836
837 start_idx = engine()->GetIndexAtStartOfLine(7U);
838 end_idx = engine()->GetIndexAtEndOfLine(7U);
839 engine()->SetSelection(start_idx, end_idx - start_idx);
840 EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
841
842 // Move past \r to before W.
843 engine()->SetSelection(engine()->GetIndexRight(5U), 5);
844 EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
845}
TEST_F(CFDE_TextEditEngineTest, Insert)
void OnTextWillChange(CFDE_TextEditEngine::TextChange *change) override
void SetScrollOffset(float fScrollOffset) override
bool OnValidate(const WideString &wsText) override
~CFDE_TextEditEngineTest() override=default
CFDE_TextEditEngine * engine() const
CFDE_TextEditEngineTest()=default
CharType operator[](const size_t index) const
Definition widestring.h:146
#define FX_IsOdd(a)