// 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/SkString.h" #include "include/core/SkTextBlob.h" #include #include #include #include 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); struct Text { const std::vector& fLines; struct Iterator { std::vector::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 fBlob; std::vector fCursorPos; std::vector fLineEndOffsets; std::vector fWordBoundaries; SkIPoint fOrigin = {0, 0}; int fHeight = 0; bool fShaped = false; TextLine(StringSlice t) : fText(std::move(t)) {} TextLine() {} }; std::vector fLines; int fWidth = 0; int fHeight = 0; SkFont fFont; 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