1// Copyright 2021 Google LLC.
2#include "experimental/sktext/editor/Texts.h"
3
4using namespace skia::text;
5
6namespace skia {
7namespace editor {
8
9void DynamicText::paint(SkCanvas* canvas) {
10    if (!fDrawableText) {
11        auto chunks = this->getDecorationChunks(fDecorations);
12        fDrawableText = fWrappedText->prepareToDraw<DrawableText>(fUnicodeText.get(),
13                                                                  PositionType::kGraphemeCluster,
14                                                                  SkSpan<TextIndex>(chunks.data(), chunks.size()));
15    }
16
17    auto foregroundPaint = fDecorations[0].foregroundPaint;
18    auto textBlobs = fDrawableText->getTextBlobs();
19    for (auto& textBLob : textBlobs) {
20        canvas->drawTextBlob(textBLob, 0, 0, foregroundPaint);
21    }
22}
23
24std::vector<TextIndex> DynamicText::getDecorationChunks(SkSpan<DecoratedBlock> decorations) const {
25    std::vector<TextIndex> result;
26    TextIndex textIndex = 0;
27    for (auto& decoration : decorations) {
28        textIndex += decoration.charCount;
29        result.emplace_back(textIndex);
30    }
31    return result;
32}
33
34void EditableText::paint(SkCanvas* canvas) {
35
36    if (fSelection->isEmpty()) {
37        DynamicText::paint(canvas);
38    } else {
39        auto decorations = mergeSelectionIntoDecorations();
40        auto chunks = this->getDecorationChunks(SkSpan<DecoratedBlock>(decorations.data(), decorations.size()));
41        fDrawableText = fWrappedText->prepareToDraw<DrawableText>(fUnicodeText.get(),
42                                                                  PositionType::kGraphemeCluster,
43                                                                  SkSpan<TextIndex>(chunks.data(), chunks.size()));
44    }
45    auto foregroundPaint = fDecorations[0].foregroundPaint;
46    auto textBlobs = fDrawableText->getTextBlobs();
47    for (auto& textBLob : textBlobs) {
48        canvas->drawTextBlob(textBLob, 0, 0, foregroundPaint);
49    }
50}
51
52SkTArray<DecoratedBlock> EditableText::mergeSelectionIntoDecorations() {
53    SkTArray<DecoratedBlock> merged;
54    merged.reserve_back(fDecorations.size() + fSelection->count());
55
56    size_t indexDecor = 0ul;                        // index in fDecorations
57    size_t decorPos = 0ul;
58    for (auto& selected : fSelection->fTextRanges) {
59        // Add all the decoration blocks that are placed before the selected block
60        DecoratedBlock& decor = fDecorations[indexDecor];
61        while (indexDecor < fDecorations.size()) {
62            decor = fDecorations[indexDecor++];
63            if (decorPos + decor.charCount >= selected.fStart) {
64                break;
65            }
66            // The entire decoration block is before
67            merged.emplace_back(decor);
68            decorPos += decor.charCount;
69        }
70
71        auto lastDecorPos = decorPos;
72        if (selected.fStart > decorPos) {
73            // The decoration block is has a part that is before the selection so we add it
74            merged.emplace_back(selected.fStart - decorPos, decor.foregroundPaint, decor.backgroundPaint);
75            decorPos = selected.fStart;
76        }
77        SkASSERT(decorPos == selected.fStart);
78
79        // So the next decoration intersects the selection (and the selection wins)
80        merged.emplace_back(selected.width(), fSelection->fForeground, fSelection->fBackground);
81        decorPos += selected.width();
82        SkASSERT(decorPos == selected.fEnd);
83
84        if (lastDecorPos + decor.charCount > selected.fEnd) {
85            // We still need to add the rest of the decoration block
86            merged.emplace_back(lastDecorPos + decor.charCount - selected.fEnd, decor.foregroundPaint, decor.backgroundPaint);
87            decorPos += lastDecorPos + decor.charCount - selected.fEnd;
88        }
89    }
90    return merged;
91}
92
93} // namespace editor
94} // namespace skia
95