1 // Copyright 2019 Google LLC. 2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. 3 #ifndef editor_DEFINED 4 #define editor_DEFINED 5 6 #include "experimental/editor/stringslice.h" 7 #include "experimental/editor/stringview.h" 8 9 #include "include/core/SkColor.h" 10 #include "include/core/SkFont.h" 11 #include "include/core/SkString.h" 12 #include "include/core/SkTextBlob.h" 13 14 #include <climits> 15 #include <cstdint> 16 #include <utility> 17 #include <vector> 18 19 class SkCanvas; 20 class SkShaper; 21 22 // TODO: modulize this; editor::Editor becomes SkEditor ? 23 24 namespace editor { 25 26 class Editor { 27 struct TextLine; 28 public: 29 // total height in canvas display units. getHeight()30 int getHeight() const { return fHeight; } 31 32 // set display width in canvas display units 33 void setWidth(int w); // may force re-shape 34 35 // get/set current font (used for shaping and displaying text) font()36 const SkFont& font() const { return fFont; } 37 void setFont(SkFont font); 38 39 struct Text { 40 const std::vector<TextLine>& fLines; 41 struct Iterator { 42 std::vector<TextLine>::const_iterator fPtr; 43 StringView operator*() { return fPtr->fText.view(); } 44 void operator++() { ++fPtr; } 45 bool operator!=(const Iterator& other) const { return fPtr != other.fPtr; } 46 }; beginText47 Iterator begin() const { return Iterator{fLines.begin()}; } endText48 Iterator end() const { return Iterator{fLines.end()}; } 49 }; 50 // Loop over all the lines of text. The lines are not '\0'- or '\n'-terminated. 51 // For example, to dump the entire file to standard output: 52 // for (editor::StringView str : editor.text()) { 53 // std::cout.write(str.data, str.size) << '\n'; 54 // } text()55 Text text() const { return Text{fLines}; } 56 57 // get size of line in canvas display units. lineHeight(size_t index)58 int lineHeight(size_t index) const { return fLines[index].fHeight; } 59 60 struct TextPosition { 61 size_t fTextByteIndex = SIZE_MAX; // index into UTF-8 representation of line. 62 size_t fParagraphIndex = SIZE_MAX; // logical line, based on hard newline characters. 63 }; 64 enum class Movement { 65 kNowhere, 66 kLeft, 67 kUp, 68 kRight, 69 kDown, 70 kHome, 71 kEnd, 72 kWordLeft, 73 kWordRight, 74 }; 75 TextPosition move(Editor::Movement move, Editor::TextPosition pos) const; 76 TextPosition getPosition(SkIPoint); 77 SkRect getLocation(TextPosition); 78 // insert into current text. 79 TextPosition insert(TextPosition, const char* utf8Text, size_t byteLen); 80 // remove text between two positions 81 TextPosition remove(TextPosition, TextPosition); 82 83 // If dst is nullptr, returns size of given selection. 84 // Otherwise, fill dst with a copy of the selection, and return the amount copied. 85 size_t copy(TextPosition pos1, TextPosition pos2, char* dst = nullptr) const; lineCount()86 size_t lineCount() const { return fLines.size(); } line(size_t i)87 StringView line(size_t i) const { 88 return i < fLines.size() ? fLines[i].fText.view() : StringView{nullptr, 0}; 89 } 90 91 struct PaintOpts { 92 SkColor4f fBackgroundColor = {1, 1, 1, 1}; 93 SkColor4f fForegroundColor = {0, 0, 0, 1}; 94 // TODO: maybe have multiple selections and cursors, each with separate colors. 95 SkColor4f fSelectionColor = {0.729f, 0.827f, 0.988f, 1}; 96 SkColor4f fCursorColor = {1, 0, 0, 1}; 97 TextPosition fSelectionBegin; 98 TextPosition fSelectionEnd; 99 TextPosition fCursor; 100 }; 101 void paint(SkCanvas* canvas, PaintOpts); 102 103 private: 104 // TODO: rename this to TextParagraph. fLines to fParas. 105 struct TextLine { 106 StringSlice fText; 107 sk_sp<const SkTextBlob> fBlob; 108 std::vector<SkRect> fCursorPos; 109 std::vector<size_t> fLineEndOffsets; 110 std::vector<bool> fWordBoundaries; 111 SkIPoint fOrigin = {0, 0}; 112 int fHeight = 0; 113 bool fShaped = false; 114 TextLineTextLine115 TextLine(StringSlice t) : fText(std::move(t)) {} TextLineTextLine116 TextLine() {} 117 }; 118 std::vector<TextLine> fLines; 119 int fWidth = 0; 120 int fHeight = 0; 121 SkFont fFont; 122 bool fNeedsReshape = false; 123 const char* fLocale = "en"; // TODO: make this setable 124 125 void markDirty(TextLine*); 126 void reshapeAll(); 127 }; 128 } // namespace editor 129 130 static inline bool operator==(const editor::Editor::TextPosition& u, 131 const editor::Editor::TextPosition& v) { 132 return u.fParagraphIndex == v.fParagraphIndex && u.fTextByteIndex == v.fTextByteIndex; 133 } 134 static inline bool operator!=(const editor::Editor::TextPosition& u, 135 const editor::Editor::TextPosition& v) { return !(u == v); } 136 137 static inline bool operator<(const editor::Editor::TextPosition& u, 138 const editor::Editor::TextPosition& v) { 139 return u.fParagraphIndex < v.fParagraphIndex || 140 (u.fParagraphIndex == v.fParagraphIndex && u.fTextByteIndex < v.fTextByteIndex); 141 } 142 143 144 #endif // editor_DEFINED 145