1cb93a386Sopenharmony_ci// Copyright 2019 Google LLC. 2cb93a386Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. 3cb93a386Sopenharmony_ci 4cb93a386Sopenharmony_ci#include "modules/skplaintexteditor/include/editor.h" 5cb93a386Sopenharmony_ci 6cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 7cb93a386Sopenharmony_ci#include "include/core/SkExecutor.h" 8cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 9cb93a386Sopenharmony_ci#include "src/utils/SkUTF.h" 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ci#include "modules/skplaintexteditor/src/shape.h" 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ci#include <algorithm> 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ciusing namespace SkPlainTextEditor; 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_cistatic inline SkRect offset(SkRect r, SkIPoint p) { 18cb93a386Sopenharmony_ci return r.makeOffset((float)p.x(), (float)p.y()); 19cb93a386Sopenharmony_ci} 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_cistatic constexpr SkRect kUnsetRect{-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX}; 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_cistatic bool valid_utf8(const char* ptr, size_t size) { return SkUTF::CountUTF8(ptr, size) >= 0; } 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_ci// Kind of like Python's readlines(), but without any allocation. 26cb93a386Sopenharmony_ci// Calls f() on each line. 27cb93a386Sopenharmony_ci// F is [](const char*, size_t) -> void 28cb93a386Sopenharmony_citemplate <typename F> 29cb93a386Sopenharmony_cistatic void readlines(const void* data, size_t size, F f) { 30cb93a386Sopenharmony_ci const char* start = (const char*)data; 31cb93a386Sopenharmony_ci const char* end = start + size; 32cb93a386Sopenharmony_ci const char* ptr = start; 33cb93a386Sopenharmony_ci while (ptr < end) { 34cb93a386Sopenharmony_ci while (*ptr++ != '\n' && ptr < end) {} 35cb93a386Sopenharmony_ci size_t len = ptr - start; 36cb93a386Sopenharmony_ci SkASSERT(len > 0); 37cb93a386Sopenharmony_ci f(start, len); 38cb93a386Sopenharmony_ci start = ptr; 39cb93a386Sopenharmony_ci } 40cb93a386Sopenharmony_ci} 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_cistatic StringSlice remove_newline(const char* str, size_t len) { 43cb93a386Sopenharmony_ci return SkASSERT((str != nullptr) || (len == 0)), 44cb93a386Sopenharmony_ci StringSlice(str, (len > 0 && str[len - 1] == '\n') ? len - 1 : len); 45cb93a386Sopenharmony_ci} 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_civoid Editor::markDirty(TextLine* line) { 48cb93a386Sopenharmony_ci line->fBlob = nullptr; 49cb93a386Sopenharmony_ci line->fShaped = false; 50cb93a386Sopenharmony_ci line->fWordBoundaries = std::vector<bool>(); 51cb93a386Sopenharmony_ci} 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_civoid Editor::setFont(SkFont font) { 54cb93a386Sopenharmony_ci if (font != fFont) { 55cb93a386Sopenharmony_ci fFont = std::move(font); 56cb93a386Sopenharmony_ci fNeedsReshape = true; 57cb93a386Sopenharmony_ci for (auto& l : fLines) { this->markDirty(&l); } 58cb93a386Sopenharmony_ci } 59cb93a386Sopenharmony_ci} 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_civoid Editor::setWidth(int w) { 62cb93a386Sopenharmony_ci if (fWidth != w) { 63cb93a386Sopenharmony_ci fWidth = w; 64cb93a386Sopenharmony_ci fNeedsReshape = true; 65cb93a386Sopenharmony_ci for (auto& l : fLines) { this->markDirty(&l); } 66cb93a386Sopenharmony_ci } 67cb93a386Sopenharmony_ci} 68cb93a386Sopenharmony_cistatic SkPoint to_point(SkIPoint p) { return {(float)p.x(), (float)p.y()}; } 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_ciEditor::TextPosition Editor::getPosition(SkIPoint xy) { 71cb93a386Sopenharmony_ci Editor::TextPosition approximatePosition; 72cb93a386Sopenharmony_ci this->reshapeAll(); 73cb93a386Sopenharmony_ci for (size_t j = 0; j < fLines.size(); ++j) { 74cb93a386Sopenharmony_ci const TextLine& line = fLines[j]; 75cb93a386Sopenharmony_ci SkIRect lineRect = {0, 76cb93a386Sopenharmony_ci line.fOrigin.y(), 77cb93a386Sopenharmony_ci fWidth, 78cb93a386Sopenharmony_ci j + 1 < fLines.size() ? fLines[j + 1].fOrigin.y() : INT_MAX}; 79cb93a386Sopenharmony_ci if (const SkTextBlob* b = line.fBlob.get()) { 80cb93a386Sopenharmony_ci SkIRect r = b->bounds().roundOut(); 81cb93a386Sopenharmony_ci r.offset(line.fOrigin); 82cb93a386Sopenharmony_ci lineRect.join(r); 83cb93a386Sopenharmony_ci } 84cb93a386Sopenharmony_ci if (!lineRect.contains(xy.x(), xy.y())) { 85cb93a386Sopenharmony_ci continue; 86cb93a386Sopenharmony_ci } 87cb93a386Sopenharmony_ci SkPoint pt = to_point(xy - line.fOrigin); 88cb93a386Sopenharmony_ci const std::vector<SkRect>& pos = line.fCursorPos; 89cb93a386Sopenharmony_ci for (size_t i = 0; i < pos.size(); ++i) { 90cb93a386Sopenharmony_ci if (pos[i] != kUnsetRect && pos[i].contains(pt.x(), pt.y())) { 91cb93a386Sopenharmony_ci return Editor::TextPosition{i, j}; 92cb93a386Sopenharmony_ci } 93cb93a386Sopenharmony_ci } 94cb93a386Sopenharmony_ci approximatePosition = {xy.x() <= line.fOrigin.x() ? 0 : line.fText.size(), j}; 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci return approximatePosition; 97cb93a386Sopenharmony_ci} 98cb93a386Sopenharmony_ci 99cb93a386Sopenharmony_cistatic inline bool is_utf8_continuation(char v) { 100cb93a386Sopenharmony_ci return ((unsigned char)v & 0b11000000) == 101cb93a386Sopenharmony_ci 0b10000000; 102cb93a386Sopenharmony_ci} 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_cistatic const char* next_utf8(const char* p, const char* end) { 105cb93a386Sopenharmony_ci if (p < end) { 106cb93a386Sopenharmony_ci do { 107cb93a386Sopenharmony_ci ++p; 108cb93a386Sopenharmony_ci } while (p < end && is_utf8_continuation(*p)); 109cb93a386Sopenharmony_ci } 110cb93a386Sopenharmony_ci return p; 111cb93a386Sopenharmony_ci} 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_cistatic const char* align_utf8(const char* p, const char* begin) { 114cb93a386Sopenharmony_ci while (p > begin && is_utf8_continuation(*p)) { 115cb93a386Sopenharmony_ci --p; 116cb93a386Sopenharmony_ci } 117cb93a386Sopenharmony_ci return p; 118cb93a386Sopenharmony_ci} 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_cistatic const char* prev_utf8(const char* p, const char* begin) { 121cb93a386Sopenharmony_ci return p > begin ? align_utf8(p - 1, begin) : begin; 122cb93a386Sopenharmony_ci} 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ciSkRect Editor::getLocation(Editor::TextPosition cursor) { 125cb93a386Sopenharmony_ci this->reshapeAll(); 126cb93a386Sopenharmony_ci cursor = this->move(Editor::Movement::kNowhere, cursor); 127cb93a386Sopenharmony_ci if (fLines.size() > 0) { 128cb93a386Sopenharmony_ci const TextLine& cLine = fLines[cursor.fParagraphIndex]; 129cb93a386Sopenharmony_ci SkRect pos = {0, 0, 0, 0}; 130cb93a386Sopenharmony_ci if (cursor.fTextByteIndex < cLine.fCursorPos.size()) { 131cb93a386Sopenharmony_ci pos = cLine.fCursorPos[cursor.fTextByteIndex]; 132cb93a386Sopenharmony_ci } 133cb93a386Sopenharmony_ci pos.fRight = pos.fLeft + 1; 134cb93a386Sopenharmony_ci pos.fLeft -= 1; 135cb93a386Sopenharmony_ci return offset(pos, cLine.fOrigin); 136cb93a386Sopenharmony_ci } 137cb93a386Sopenharmony_ci return SkRect{0, 0, 0, 0}; 138cb93a386Sopenharmony_ci} 139cb93a386Sopenharmony_ci 140cb93a386Sopenharmony_cistatic size_t count_char(const StringSlice& string, char value) { 141cb93a386Sopenharmony_ci size_t count = 0; 142cb93a386Sopenharmony_ci for (char c : string) { if (c == value) { ++count; } } 143cb93a386Sopenharmony_ci return count; 144cb93a386Sopenharmony_ci} 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ciEditor::TextPosition Editor::insert(TextPosition pos, const char* utf8Text, size_t byteLen) { 147cb93a386Sopenharmony_ci if (!valid_utf8(utf8Text, byteLen) || 0 == byteLen) { 148cb93a386Sopenharmony_ci return pos; 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci pos = this->move(Editor::Movement::kNowhere, pos); 151cb93a386Sopenharmony_ci fNeedsReshape = true; 152cb93a386Sopenharmony_ci if (pos.fParagraphIndex < fLines.size()) { 153cb93a386Sopenharmony_ci fLines[pos.fParagraphIndex].fText.insert(pos.fTextByteIndex, utf8Text, byteLen); 154cb93a386Sopenharmony_ci this->markDirty(&fLines[pos.fParagraphIndex]); 155cb93a386Sopenharmony_ci } else { 156cb93a386Sopenharmony_ci SkASSERT(pos.fParagraphIndex == fLines.size()); 157cb93a386Sopenharmony_ci SkASSERT(pos.fTextByteIndex == 0); 158cb93a386Sopenharmony_ci fLines.push_back(Editor::TextLine(StringSlice(utf8Text, byteLen))); 159cb93a386Sopenharmony_ci } 160cb93a386Sopenharmony_ci pos = Editor::TextPosition{pos.fTextByteIndex + byteLen, pos.fParagraphIndex}; 161cb93a386Sopenharmony_ci size_t newlinecount = count_char(fLines[pos.fParagraphIndex].fText, '\n'); 162cb93a386Sopenharmony_ci if (newlinecount > 0) { 163cb93a386Sopenharmony_ci StringSlice src = std::move(fLines[pos.fParagraphIndex].fText); 164cb93a386Sopenharmony_ci std::vector<TextLine>::const_iterator next = fLines.begin() + pos.fParagraphIndex + 1; 165cb93a386Sopenharmony_ci fLines.insert(next, newlinecount, TextLine()); 166cb93a386Sopenharmony_ci TextLine* line = &fLines[pos.fParagraphIndex]; 167cb93a386Sopenharmony_ci readlines(src.begin(), src.size(), [&line](const char* str, size_t l) { 168cb93a386Sopenharmony_ci (line++)->fText = remove_newline(str, l); 169cb93a386Sopenharmony_ci }); 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci return pos; 172cb93a386Sopenharmony_ci} 173cb93a386Sopenharmony_ci 174cb93a386Sopenharmony_ciEditor::TextPosition Editor::remove(TextPosition pos1, TextPosition pos2) { 175cb93a386Sopenharmony_ci pos1 = this->move(Editor::Movement::kNowhere, pos1); 176cb93a386Sopenharmony_ci pos2 = this->move(Editor::Movement::kNowhere, pos2); 177cb93a386Sopenharmony_ci auto cmp = [](const Editor::TextPosition& u, const Editor::TextPosition& v) { return u < v; }; 178cb93a386Sopenharmony_ci Editor::TextPosition start = std::min(pos1, pos2, cmp); 179cb93a386Sopenharmony_ci Editor::TextPosition end = std::max(pos1, pos2, cmp); 180cb93a386Sopenharmony_ci if (start == end || start.fParagraphIndex == fLines.size()) { 181cb93a386Sopenharmony_ci return start; 182cb93a386Sopenharmony_ci } 183cb93a386Sopenharmony_ci fNeedsReshape = true; 184cb93a386Sopenharmony_ci if (start.fParagraphIndex == end.fParagraphIndex) { 185cb93a386Sopenharmony_ci SkASSERT(end.fTextByteIndex > start.fTextByteIndex); 186cb93a386Sopenharmony_ci fLines[start.fParagraphIndex].fText.remove( 187cb93a386Sopenharmony_ci start.fTextByteIndex, end.fTextByteIndex - start.fTextByteIndex); 188cb93a386Sopenharmony_ci this->markDirty(&fLines[start.fParagraphIndex]); 189cb93a386Sopenharmony_ci } else { 190cb93a386Sopenharmony_ci SkASSERT(end.fParagraphIndex < fLines.size()); 191cb93a386Sopenharmony_ci auto& line = fLines[start.fParagraphIndex]; 192cb93a386Sopenharmony_ci line.fText.remove(start.fTextByteIndex, 193cb93a386Sopenharmony_ci line.fText.size() - start.fTextByteIndex); 194cb93a386Sopenharmony_ci line.fText.insert(start.fTextByteIndex, 195cb93a386Sopenharmony_ci fLines[end.fParagraphIndex].fText.begin() + end.fTextByteIndex, 196cb93a386Sopenharmony_ci fLines[end.fParagraphIndex].fText.size() - end.fTextByteIndex); 197cb93a386Sopenharmony_ci this->markDirty(&line); 198cb93a386Sopenharmony_ci fLines.erase(fLines.begin() + start.fParagraphIndex + 1, 199cb93a386Sopenharmony_ci fLines.begin() + end.fParagraphIndex + 1); 200cb93a386Sopenharmony_ci } 201cb93a386Sopenharmony_ci return start; 202cb93a386Sopenharmony_ci} 203cb93a386Sopenharmony_ci 204cb93a386Sopenharmony_cistatic void append(char** dst, size_t* count, const char* src, size_t n) { 205cb93a386Sopenharmony_ci if (*dst) { 206cb93a386Sopenharmony_ci ::memcpy(*dst, src, n); 207cb93a386Sopenharmony_ci *dst += n; 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci *count += n; 210cb93a386Sopenharmony_ci} 211cb93a386Sopenharmony_ci 212cb93a386Sopenharmony_cisize_t Editor::copy(TextPosition pos1, TextPosition pos2, char* dst) const { 213cb93a386Sopenharmony_ci size_t size = 0; 214cb93a386Sopenharmony_ci pos1 = this->move(Editor::Movement::kNowhere, pos1); 215cb93a386Sopenharmony_ci pos2 = this->move(Editor::Movement::kNowhere, pos2); 216cb93a386Sopenharmony_ci auto cmp = [](const Editor::TextPosition& u, const Editor::TextPosition& v) { return u < v; }; 217cb93a386Sopenharmony_ci Editor::TextPosition start = std::min(pos1, pos2, cmp); 218cb93a386Sopenharmony_ci Editor::TextPosition end = std::max(pos1, pos2, cmp); 219cb93a386Sopenharmony_ci if (start == end || start.fParagraphIndex == fLines.size()) { 220cb93a386Sopenharmony_ci return size; 221cb93a386Sopenharmony_ci } 222cb93a386Sopenharmony_ci if (start.fParagraphIndex == end.fParagraphIndex) { 223cb93a386Sopenharmony_ci SkASSERT(end.fTextByteIndex > start.fTextByteIndex); 224cb93a386Sopenharmony_ci auto& str = fLines[start.fParagraphIndex].fText; 225cb93a386Sopenharmony_ci append(&dst, &size, str.begin() + start.fTextByteIndex, 226cb93a386Sopenharmony_ci end.fTextByteIndex - start.fTextByteIndex); 227cb93a386Sopenharmony_ci return size; 228cb93a386Sopenharmony_ci } 229cb93a386Sopenharmony_ci SkASSERT(end.fParagraphIndex < fLines.size()); 230cb93a386Sopenharmony_ci const std::vector<TextLine>::const_iterator firstP = fLines.begin() + start.fParagraphIndex; 231cb93a386Sopenharmony_ci const std::vector<TextLine>::const_iterator lastP = fLines.begin() + end.fParagraphIndex; 232cb93a386Sopenharmony_ci const auto& first = firstP->fText; 233cb93a386Sopenharmony_ci const auto& last = lastP->fText; 234cb93a386Sopenharmony_ci 235cb93a386Sopenharmony_ci append(&dst, &size, first.begin() + start.fTextByteIndex, first.size() - start.fTextByteIndex); 236cb93a386Sopenharmony_ci for (auto line = firstP + 1; line < lastP; ++line) { 237cb93a386Sopenharmony_ci append(&dst, &size, "\n", 1); 238cb93a386Sopenharmony_ci append(&dst, &size, line->fText.begin(), line->fText.size()); 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci append(&dst, &size, "\n", 1); 241cb93a386Sopenharmony_ci append(&dst, &size, last.begin(), end.fTextByteIndex); 242cb93a386Sopenharmony_ci return size; 243cb93a386Sopenharmony_ci} 244cb93a386Sopenharmony_ci 245cb93a386Sopenharmony_cistatic inline const char* begin(const StringSlice& s) { return s.begin(); } 246cb93a386Sopenharmony_ci 247cb93a386Sopenharmony_cistatic inline const char* end(const StringSlice& s) { return s.end(); } 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_cistatic size_t align_column(const StringSlice& str, size_t p) { 250cb93a386Sopenharmony_ci if (p >= str.size()) { 251cb93a386Sopenharmony_ci return str.size(); 252cb93a386Sopenharmony_ci } 253cb93a386Sopenharmony_ci return align_utf8(begin(str) + p, begin(str)) - begin(str); 254cb93a386Sopenharmony_ci} 255cb93a386Sopenharmony_ci 256cb93a386Sopenharmony_ci// returns smallest i such that list[i] > value. value > list[i-1] 257cb93a386Sopenharmony_ci// Use a binary search since list is monotonic 258cb93a386Sopenharmony_citemplate <typename T> 259cb93a386Sopenharmony_cistatic size_t find_first_larger(const std::vector<T>& list, T value) { 260cb93a386Sopenharmony_ci return (size_t)(std::upper_bound(list.begin(), list.end(), value) - list.begin()); 261cb93a386Sopenharmony_ci} 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_cistatic size_t find_closest_x(const std::vector<SkRect>& bounds, float x, size_t b, size_t e) { 264cb93a386Sopenharmony_ci if (b >= e) { 265cb93a386Sopenharmony_ci return b; 266cb93a386Sopenharmony_ci } 267cb93a386Sopenharmony_ci SkASSERT(e <= bounds.size()); 268cb93a386Sopenharmony_ci size_t best_index = b; 269cb93a386Sopenharmony_ci float best_diff = ::fabsf(bounds[best_index].x() - x); 270cb93a386Sopenharmony_ci for (size_t i = b + 1; i < e; ++i) { 271cb93a386Sopenharmony_ci float d = ::fabsf(bounds[i].x() - x); 272cb93a386Sopenharmony_ci if (d < best_diff) { 273cb93a386Sopenharmony_ci best_diff = d; 274cb93a386Sopenharmony_ci best_index = i; 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci } 277cb93a386Sopenharmony_ci return best_index; 278cb93a386Sopenharmony_ci} 279cb93a386Sopenharmony_ci 280cb93a386Sopenharmony_ciEditor::TextPosition Editor::move(Editor::Movement move, Editor::TextPosition pos) const { 281cb93a386Sopenharmony_ci if (fLines.empty()) { 282cb93a386Sopenharmony_ci return {0, 0}; 283cb93a386Sopenharmony_ci } 284cb93a386Sopenharmony_ci // First thing: fix possible bad input values. 285cb93a386Sopenharmony_ci if (pos.fParagraphIndex >= fLines.size()) { 286cb93a386Sopenharmony_ci pos.fParagraphIndex = fLines.size() - 1; 287cb93a386Sopenharmony_ci pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size(); 288cb93a386Sopenharmony_ci } else { 289cb93a386Sopenharmony_ci pos.fTextByteIndex = align_column(fLines[pos.fParagraphIndex].fText, pos.fTextByteIndex); 290cb93a386Sopenharmony_ci } 291cb93a386Sopenharmony_ci 292cb93a386Sopenharmony_ci SkASSERT(pos.fParagraphIndex < fLines.size()); 293cb93a386Sopenharmony_ci SkASSERT(pos.fTextByteIndex <= fLines[pos.fParagraphIndex].fText.size()); 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci SkASSERT(pos.fTextByteIndex == fLines[pos.fParagraphIndex].fText.size() || 296cb93a386Sopenharmony_ci !is_utf8_continuation(fLines[pos.fParagraphIndex].fText.begin()[pos.fTextByteIndex])); 297cb93a386Sopenharmony_ci 298cb93a386Sopenharmony_ci switch (move) { 299cb93a386Sopenharmony_ci case Editor::Movement::kNowhere: 300cb93a386Sopenharmony_ci break; 301cb93a386Sopenharmony_ci case Editor::Movement::kLeft: 302cb93a386Sopenharmony_ci if (0 == pos.fTextByteIndex) { 303cb93a386Sopenharmony_ci if (pos.fParagraphIndex > 0) { 304cb93a386Sopenharmony_ci --pos.fParagraphIndex; 305cb93a386Sopenharmony_ci pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size(); 306cb93a386Sopenharmony_ci } 307cb93a386Sopenharmony_ci } else { 308cb93a386Sopenharmony_ci const auto& str = fLines[pos.fParagraphIndex].fText; 309cb93a386Sopenharmony_ci pos.fTextByteIndex = 310cb93a386Sopenharmony_ci prev_utf8(begin(str) + pos.fTextByteIndex, begin(str)) - begin(str); 311cb93a386Sopenharmony_ci } 312cb93a386Sopenharmony_ci break; 313cb93a386Sopenharmony_ci case Editor::Movement::kRight: 314cb93a386Sopenharmony_ci if (fLines[pos.fParagraphIndex].fText.size() == pos.fTextByteIndex) { 315cb93a386Sopenharmony_ci if (pos.fParagraphIndex + 1 < fLines.size()) { 316cb93a386Sopenharmony_ci ++pos.fParagraphIndex; 317cb93a386Sopenharmony_ci pos.fTextByteIndex = 0; 318cb93a386Sopenharmony_ci } 319cb93a386Sopenharmony_ci } else { 320cb93a386Sopenharmony_ci const auto& str = fLines[pos.fParagraphIndex].fText; 321cb93a386Sopenharmony_ci pos.fTextByteIndex = 322cb93a386Sopenharmony_ci next_utf8(begin(str) + pos.fTextByteIndex, end(str)) - begin(str); 323cb93a386Sopenharmony_ci } 324cb93a386Sopenharmony_ci break; 325cb93a386Sopenharmony_ci case Editor::Movement::kHome: 326cb93a386Sopenharmony_ci { 327cb93a386Sopenharmony_ci const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets; 328cb93a386Sopenharmony_ci size_t f = find_first_larger(list, pos.fTextByteIndex); 329cb93a386Sopenharmony_ci pos.fTextByteIndex = f > 0 ? list[f - 1] : 0; 330cb93a386Sopenharmony_ci } 331cb93a386Sopenharmony_ci break; 332cb93a386Sopenharmony_ci case Editor::Movement::kEnd: 333cb93a386Sopenharmony_ci { 334cb93a386Sopenharmony_ci const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets; 335cb93a386Sopenharmony_ci size_t f = find_first_larger(list, pos.fTextByteIndex); 336cb93a386Sopenharmony_ci if (f < list.size()) { 337cb93a386Sopenharmony_ci pos.fTextByteIndex = list[f] > 0 ? list[f] - 1 : 0; 338cb93a386Sopenharmony_ci } else { 339cb93a386Sopenharmony_ci pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size(); 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci } 342cb93a386Sopenharmony_ci break; 343cb93a386Sopenharmony_ci case Editor::Movement::kUp: 344cb93a386Sopenharmony_ci { 345cb93a386Sopenharmony_ci SkASSERT(pos.fTextByteIndex < fLines[pos.fParagraphIndex].fCursorPos.size()); 346cb93a386Sopenharmony_ci float x = fLines[pos.fParagraphIndex].fCursorPos[pos.fTextByteIndex].left(); 347cb93a386Sopenharmony_ci const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets; 348cb93a386Sopenharmony_ci size_t f = find_first_larger(list, pos.fTextByteIndex); 349cb93a386Sopenharmony_ci // list[f] > value. value > list[f-1] 350cb93a386Sopenharmony_ci if (f > 0) { 351cb93a386Sopenharmony_ci // not the first line in paragraph. 352cb93a386Sopenharmony_ci pos.fTextByteIndex = find_closest_x(fLines[pos.fParagraphIndex].fCursorPos, x, 353cb93a386Sopenharmony_ci (f == 1) ? 0 : list[f - 2], 354cb93a386Sopenharmony_ci list[f - 1]); 355cb93a386Sopenharmony_ci } else if (pos.fParagraphIndex > 0) { 356cb93a386Sopenharmony_ci --pos.fParagraphIndex; 357cb93a386Sopenharmony_ci const auto& newLine = fLines[pos.fParagraphIndex]; 358cb93a386Sopenharmony_ci size_t r = newLine.fLineEndOffsets.size(); 359cb93a386Sopenharmony_ci if (r > 0) { 360cb93a386Sopenharmony_ci pos.fTextByteIndex = find_closest_x(newLine.fCursorPos, x, 361cb93a386Sopenharmony_ci newLine.fLineEndOffsets[r - 1], 362cb93a386Sopenharmony_ci newLine.fCursorPos.size()); 363cb93a386Sopenharmony_ci } else { 364cb93a386Sopenharmony_ci pos.fTextByteIndex = find_closest_x(newLine.fCursorPos, x, 0, 365cb93a386Sopenharmony_ci newLine.fCursorPos.size()); 366cb93a386Sopenharmony_ci } 367cb93a386Sopenharmony_ci } 368cb93a386Sopenharmony_ci pos.fTextByteIndex = 369cb93a386Sopenharmony_ci align_column(fLines[pos.fParagraphIndex].fText, pos.fTextByteIndex); 370cb93a386Sopenharmony_ci } 371cb93a386Sopenharmony_ci break; 372cb93a386Sopenharmony_ci case Editor::Movement::kDown: 373cb93a386Sopenharmony_ci { 374cb93a386Sopenharmony_ci const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets; 375cb93a386Sopenharmony_ci float x = fLines[pos.fParagraphIndex].fCursorPos[pos.fTextByteIndex].left(); 376cb93a386Sopenharmony_ci 377cb93a386Sopenharmony_ci size_t f = find_first_larger(list, pos.fTextByteIndex); 378cb93a386Sopenharmony_ci if (f < list.size()) { 379cb93a386Sopenharmony_ci const auto& bounds = fLines[pos.fParagraphIndex].fCursorPos; 380cb93a386Sopenharmony_ci pos.fTextByteIndex = find_closest_x(bounds, x, list[f], 381cb93a386Sopenharmony_ci f + 1 < list.size() ? list[f + 1] 382cb93a386Sopenharmony_ci : bounds.size()); 383cb93a386Sopenharmony_ci } else if (pos.fParagraphIndex + 1 < fLines.size()) { 384cb93a386Sopenharmony_ci ++pos.fParagraphIndex; 385cb93a386Sopenharmony_ci const auto& bounds = fLines[pos.fParagraphIndex].fCursorPos; 386cb93a386Sopenharmony_ci const std::vector<size_t>& l2 = fLines[pos.fParagraphIndex].fLineEndOffsets; 387cb93a386Sopenharmony_ci pos.fTextByteIndex = find_closest_x(bounds, x, 0, 388cb93a386Sopenharmony_ci l2.size() > 0 ? l2[0] : bounds.size()); 389cb93a386Sopenharmony_ci } else { 390cb93a386Sopenharmony_ci pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size(); 391cb93a386Sopenharmony_ci } 392cb93a386Sopenharmony_ci pos.fTextByteIndex = 393cb93a386Sopenharmony_ci align_column(fLines[pos.fParagraphIndex].fText, pos.fTextByteIndex); 394cb93a386Sopenharmony_ci } 395cb93a386Sopenharmony_ci break; 396cb93a386Sopenharmony_ci case Editor::Movement::kWordLeft: 397cb93a386Sopenharmony_ci { 398cb93a386Sopenharmony_ci if (pos.fTextByteIndex == 0) { 399cb93a386Sopenharmony_ci pos = this->move(Editor::Movement::kLeft, pos); 400cb93a386Sopenharmony_ci break; 401cb93a386Sopenharmony_ci } 402cb93a386Sopenharmony_ci const std::vector<bool>& words = fLines[pos.fParagraphIndex].fWordBoundaries; 403cb93a386Sopenharmony_ci SkASSERT(words.size() == fLines[pos.fParagraphIndex].fText.size()); 404cb93a386Sopenharmony_ci do { 405cb93a386Sopenharmony_ci --pos.fTextByteIndex; 406cb93a386Sopenharmony_ci } while (pos.fTextByteIndex > 0 && !words[pos.fTextByteIndex]); 407cb93a386Sopenharmony_ci } 408cb93a386Sopenharmony_ci break; 409cb93a386Sopenharmony_ci case Editor::Movement::kWordRight: 410cb93a386Sopenharmony_ci { 411cb93a386Sopenharmony_ci const StringSlice& text = fLines[pos.fParagraphIndex].fText; 412cb93a386Sopenharmony_ci if (pos.fTextByteIndex == text.size()) { 413cb93a386Sopenharmony_ci pos = this->move(Editor::Movement::kRight, pos); 414cb93a386Sopenharmony_ci break; 415cb93a386Sopenharmony_ci } 416cb93a386Sopenharmony_ci const std::vector<bool>& words = fLines[pos.fParagraphIndex].fWordBoundaries; 417cb93a386Sopenharmony_ci SkASSERT(words.size() == text.size()); 418cb93a386Sopenharmony_ci do { 419cb93a386Sopenharmony_ci ++pos.fTextByteIndex; 420cb93a386Sopenharmony_ci } while (pos.fTextByteIndex < text.size() && !words[pos.fTextByteIndex]); 421cb93a386Sopenharmony_ci } 422cb93a386Sopenharmony_ci break; 423cb93a386Sopenharmony_ci 424cb93a386Sopenharmony_ci } 425cb93a386Sopenharmony_ci return pos; 426cb93a386Sopenharmony_ci} 427cb93a386Sopenharmony_ci 428cb93a386Sopenharmony_civoid Editor::paint(SkCanvas* c, PaintOpts options) { 429cb93a386Sopenharmony_ci this->reshapeAll(); 430cb93a386Sopenharmony_ci if (!c) { 431cb93a386Sopenharmony_ci return; 432cb93a386Sopenharmony_ci } 433cb93a386Sopenharmony_ci 434cb93a386Sopenharmony_ci c->drawPaint(SkPaint(options.fBackgroundColor)); 435cb93a386Sopenharmony_ci 436cb93a386Sopenharmony_ci SkPaint selection = SkPaint(options.fSelectionColor); 437cb93a386Sopenharmony_ci auto cmp = [](const Editor::TextPosition& u, const Editor::TextPosition& v) { return u < v; }; 438cb93a386Sopenharmony_ci for (TextPosition pos = std::min(options.fSelectionBegin, options.fSelectionEnd, cmp), 439cb93a386Sopenharmony_ci end = std::max(options.fSelectionBegin, options.fSelectionEnd, cmp); 440cb93a386Sopenharmony_ci pos < end; 441cb93a386Sopenharmony_ci pos = this->move(Editor::Movement::kRight, pos)) 442cb93a386Sopenharmony_ci { 443cb93a386Sopenharmony_ci SkASSERT(pos.fParagraphIndex < fLines.size()); 444cb93a386Sopenharmony_ci const TextLine& l = fLines[pos.fParagraphIndex]; 445cb93a386Sopenharmony_ci c->drawRect(offset(l.fCursorPos[pos.fTextByteIndex], l.fOrigin), selection); 446cb93a386Sopenharmony_ci } 447cb93a386Sopenharmony_ci 448cb93a386Sopenharmony_ci if (fLines.size() > 0) { 449cb93a386Sopenharmony_ci c->drawRect(Editor::getLocation(options.fCursor), SkPaint(options.fCursorColor)); 450cb93a386Sopenharmony_ci } 451cb93a386Sopenharmony_ci 452cb93a386Sopenharmony_ci SkPaint foreground = SkPaint(options.fForegroundColor); 453cb93a386Sopenharmony_ci for (const TextLine& line : fLines) { 454cb93a386Sopenharmony_ci if (line.fBlob) { 455cb93a386Sopenharmony_ci c->drawTextBlob(line.fBlob.get(), line.fOrigin.x(), line.fOrigin.y(), foreground); 456cb93a386Sopenharmony_ci } 457cb93a386Sopenharmony_ci } 458cb93a386Sopenharmony_ci} 459cb93a386Sopenharmony_ci 460cb93a386Sopenharmony_civoid Editor::reshapeAll() { 461cb93a386Sopenharmony_ci if (fNeedsReshape) { 462cb93a386Sopenharmony_ci if (fLines.empty()) { 463cb93a386Sopenharmony_ci fLines.push_back(TextLine()); 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci float shape_width = (float)(fWidth); 466cb93a386Sopenharmony_ci #ifdef SK_EDITOR_GO_FAST 467cb93a386Sopenharmony_ci SkSemaphore semaphore; 468cb93a386Sopenharmony_ci std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool(100); 469cb93a386Sopenharmony_ci int jobCount = 0; 470cb93a386Sopenharmony_ci for (TextLine& line : fLines) { 471cb93a386Sopenharmony_ci if (!line.fShaped) { 472cb93a386Sopenharmony_ci executor->add([&]() { 473cb93a386Sopenharmony_ci ShapeResult result = Shape(line.fText.begin(), line.fText.size(), 474cb93a386Sopenharmony_ci fFont, fLocale, shape_width); 475cb93a386Sopenharmony_ci line.fBlob = std::move(result.blob); 476cb93a386Sopenharmony_ci line.fLineEndOffsets = std::move(result.lineBreakOffsets); 477cb93a386Sopenharmony_ci line.fCursorPos = std::move(result.glyphBounds); 478cb93a386Sopenharmony_ci line.fWordBoundaries = std::move(result.wordBreaks); 479cb93a386Sopenharmony_ci line.fHeight = result.verticalAdvance; 480cb93a386Sopenharmony_ci line.fShaped = true; 481cb93a386Sopenharmony_ci semaphore.signal(); 482cb93a386Sopenharmony_ci } 483cb93a386Sopenharmony_ci ++jobCount; 484cb93a386Sopenharmony_ci }); 485cb93a386Sopenharmony_ci } 486cb93a386Sopenharmony_ci while (jobCount-- > 0) { semaphore.wait(); } 487cb93a386Sopenharmony_ci #else 488cb93a386Sopenharmony_ci int i = 0; 489cb93a386Sopenharmony_ci for (TextLine& line : fLines) { 490cb93a386Sopenharmony_ci if (!line.fShaped) { 491cb93a386Sopenharmony_ci ShapeResult result = Shape(line.fText.begin(), line.fText.size(), 492cb93a386Sopenharmony_ci fFont, fLocale, shape_width); 493cb93a386Sopenharmony_ci line.fBlob = std::move(result.blob); 494cb93a386Sopenharmony_ci line.fLineEndOffsets = std::move(result.lineBreakOffsets); 495cb93a386Sopenharmony_ci line.fCursorPos = std::move(result.glyphBounds); 496cb93a386Sopenharmony_ci line.fWordBoundaries = std::move(result.wordBreaks); 497cb93a386Sopenharmony_ci line.fHeight = result.verticalAdvance; 498cb93a386Sopenharmony_ci line.fShaped = true; 499cb93a386Sopenharmony_ci } 500cb93a386Sopenharmony_ci ++i; 501cb93a386Sopenharmony_ci } 502cb93a386Sopenharmony_ci #endif 503cb93a386Sopenharmony_ci int y = 0; 504cb93a386Sopenharmony_ci for (TextLine& line : fLines) { 505cb93a386Sopenharmony_ci line.fOrigin = {0, y}; 506cb93a386Sopenharmony_ci y += line.fHeight; 507cb93a386Sopenharmony_ci } 508cb93a386Sopenharmony_ci fHeight = y; 509cb93a386Sopenharmony_ci fNeedsReshape = false; 510cb93a386Sopenharmony_ci } 511cb93a386Sopenharmony_ci} 512cb93a386Sopenharmony_ci 513