| // Copyright 2019 Google LLC. | |
| // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. | |
| #ifndef editor_DEFINED | |
| #define editor_DEFINED | |
| #include "modules/skplaintexteditor/include/stringslice.h" | |
| #include "modules/skplaintexteditor/include/stringview.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkFont.h" | |
| #include "include/core/SkFontMgr.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTextBlob.h" | |
| #include <climits> | |
| #include <cstdint> | |
| #include <utility> | |
| #include <vector> | |
| class SkCanvas; | |
| class SkShaper; | |
| namespace SkPlainTextEditor { | |
| class Editor { | |
| struct TextLine; | |
| public: | |
| // total height in canvas display units. | |
| int getHeight() const { return fHeight; } | |
| // set display width in canvas display units | |
| void setWidth(int w); // may force re-shape | |
| // get/set current font (used for shaping and displaying text) | |
| const SkFont& font() const { return fFont; } | |
| void setFont(SkFont font); | |
| void setFontMgr(sk_sp<SkFontMgr> fontMgr); | |
| struct Text { | |
| const std::vector<TextLine>& fLines; | |
| struct Iterator { | |
| std::vector<TextLine>::const_iterator fPtr; | |
| StringView operator*() { return fPtr->fText.view(); } | |
| void operator++() { ++fPtr; } | |
| bool operator!=(const Iterator& other) const { return fPtr != other.fPtr; } | |
| }; | |
| Iterator begin() const { return Iterator{fLines.begin()}; } | |
| Iterator end() const { return Iterator{fLines.end()}; } | |
| }; | |
| // Loop over all the lines of text. The lines are not '\0'- or '\n'-terminated. | |
| // For example, to dump the entire file to standard output: | |
| // for (SkPlainTextEditor::StringView str : editor.text()) { | |
| // std::cout.write(str.data, str.size) << '\n'; | |
| // } | |
| Text text() const { return Text{fLines}; } | |
| // get size of line in canvas display units. | |
| int lineHeight(size_t index) const { return fLines[index].fHeight; } | |
| struct TextPosition { | |
| size_t fTextByteIndex = SIZE_MAX; // index into UTF-8 representation of line. | |
| size_t fParagraphIndex = SIZE_MAX; // logical line, based on hard newline characters. | |
| }; | |
| enum class Movement { | |
| kNowhere, | |
| kLeft, | |
| kUp, | |
| kRight, | |
| kDown, | |
| kHome, | |
| kEnd, | |
| kWordLeft, | |
| kWordRight, | |
| }; | |
| TextPosition move(Editor::Movement move, Editor::TextPosition pos) const; | |
| TextPosition getPosition(SkIPoint); | |
| SkRect getLocation(TextPosition); | |
| // insert into current text. | |
| TextPosition insert(TextPosition, const char* utf8Text, size_t byteLen); | |
| // remove text between two positions | |
| TextPosition remove(TextPosition, TextPosition); | |
| // If dst is nullptr, returns size of given selection. | |
| // Otherwise, fill dst with a copy of the selection, and return the amount copied. | |
| size_t copy(TextPosition pos1, TextPosition pos2, char* dst = nullptr) const; | |
| size_t lineCount() const { return fLines.size(); } | |
| StringView line(size_t i) const { | |
| return i < fLines.size() ? fLines[i].fText.view() : StringView{nullptr, 0}; | |
| } | |
| struct PaintOpts { | |
| SkColor4f fBackgroundColor = {1, 1, 1, 1}; | |
| SkColor4f fForegroundColor = {0, 0, 0, 1}; | |
| // TODO: maybe have multiple selections and cursors, each with separate colors. | |
| SkColor4f fSelectionColor = {0.729f, 0.827f, 0.988f, 1}; | |
| SkColor4f fCursorColor = {1, 0, 0, 1}; | |
| TextPosition fSelectionBegin; | |
| TextPosition fSelectionEnd; | |
| TextPosition fCursor; | |
| }; | |
| void paint(SkCanvas* canvas, PaintOpts); | |
| private: | |
| // TODO: rename this to TextParagraph. fLines to fParas. | |
| struct TextLine { | |
| StringSlice fText; | |
| sk_sp<const SkTextBlob> fBlob; | |
| std::vector<SkRect> fCursorPos; | |
| std::vector<size_t> fLineEndOffsets; | |
| std::vector<bool> fWordBoundaries; | |
| SkIPoint fOrigin = {0, 0}; | |
| int fHeight = 0; | |
| bool fShaped = false; | |
| explicit TextLine(StringSlice t) : fText(std::move(t)) {} | |
| TextLine() {} | |
| }; | |
| std::vector<TextLine> fLines; | |
| int fWidth = 0; | |
| int fHeight = 0; | |
| SkFont fFont; | |
| sk_sp<SkFontMgr> fFontMgr; | |
| bool fNeedsReshape = false; | |
| const char* fLocale = "en"; // TODO: make this setable | |
| void markDirty(TextLine*); | |
| void reshapeAll(); | |
| }; | |
| } // namespace SkPlainTextEditor | |
| static inline bool operator==(const SkPlainTextEditor::Editor::TextPosition& u, | |
| const SkPlainTextEditor::Editor::TextPosition& v) { | |
| return u.fParagraphIndex == v.fParagraphIndex && u.fTextByteIndex == v.fTextByteIndex; | |
| } | |
| static inline bool operator!=(const SkPlainTextEditor::Editor::TextPosition& u, | |
| const SkPlainTextEditor::Editor::TextPosition& v) { return !(u == v); } | |
| static inline bool operator<(const SkPlainTextEditor::Editor::TextPosition& u, | |
| const SkPlainTextEditor::Editor::TextPosition& v) { | |
| return u.fParagraphIndex < v.fParagraphIndex || | |
| (u.fParagraphIndex == v.fParagraphIndex && u.fTextByteIndex < v.fTextByteIndex); | |
| } | |
| #endif // editor_DEFINED |