blob: b87d53646e922d0ea20ebeba6a62c744dddd9762 [file] [log] [blame]
// 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 <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);
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;
TextLine(StringSlice t) : fText(std::move(t)) {}
TextLine() {}
};
std::vector<TextLine> 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