2019-04-28 18:40:45 +00:00
|
|
|
// 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
|
|
|
|
|
2019-05-14 19:01:39 +00:00
|
|
|
#include "experimental/editor/stringslice.h"
|
2019-05-03 21:14:21 +00:00
|
|
|
|
2019-04-28 18:40:45 +00:00
|
|
|
#include "include/core/SkColor.h"
|
|
|
|
#include "include/core/SkFont.h"
|
|
|
|
#include "include/core/SkString.h"
|
|
|
|
#include "include/core/SkTextBlob.h"
|
|
|
|
|
2019-05-03 21:14:21 +00:00
|
|
|
#include <climits>
|
|
|
|
#include <cstdint>
|
2019-04-28 18:40:45 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
class SkCanvas;
|
2019-04-30 16:38:28 +00:00
|
|
|
class SkShaper;
|
2019-04-28 18:40:45 +00:00
|
|
|
|
|
|
|
// TODO: modulize this; editor::Editor becomes SkEditor ?
|
|
|
|
|
|
|
|
namespace editor {
|
|
|
|
|
|
|
|
class Editor {
|
2019-06-20 15:29:10 +00:00
|
|
|
struct TextLine;
|
2019-04-28 18:40:45 +00:00
|
|
|
public:
|
2019-06-20 15:29:10 +00:00
|
|
|
// used to load a new file.
|
2019-04-28 18:40:45 +00:00
|
|
|
void setText(const char* text, size_t len);
|
2019-06-20 15:29:10 +00:00
|
|
|
// total height in canvas display units.
|
2019-04-28 18:40:45 +00:00
|
|
|
int getHeight() const { return fHeight; }
|
2019-06-20 15:29:10 +00:00
|
|
|
// spacing around the text, in canvas display units.
|
2019-04-28 18:40:45 +00:00
|
|
|
int getMargin() const { return fMargin; }
|
2019-05-03 21:14:21 +00:00
|
|
|
|
2019-06-20 15:29:10 +00:00
|
|
|
// set display width in canvas display units
|
2019-04-28 18:40:45 +00:00
|
|
|
void setWidth(int w); // may force re-shape
|
2019-06-20 15:29:10 +00:00
|
|
|
|
|
|
|
// get/set current font (used for shaping and displaying text)
|
2019-04-28 18:40:45 +00:00
|
|
|
const SkFont& font() const { return fFont; }
|
2019-05-03 21:14:21 +00:00
|
|
|
void setFont(SkFont font);
|
2019-04-28 18:40:45 +00:00
|
|
|
|
|
|
|
size_t lineCount() const { return fLines.size(); }
|
2019-06-20 15:29:10 +00:00
|
|
|
const StringSlice& line(size_t i) const { return SkASSERT(i < fLines.size()), fLines[i].fText; }
|
|
|
|
|
|
|
|
struct Text {
|
|
|
|
const std::vector<TextLine>& fLines;
|
|
|
|
struct Iterator {
|
|
|
|
std::vector<TextLine>::const_iterator fPtr;
|
|
|
|
const StringSlice& operator*() { return fPtr->fText; }
|
|
|
|
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()}; }
|
|
|
|
};
|
|
|
|
Text text() const { return Text{fLines}; }
|
|
|
|
|
|
|
|
// get size of line in canvas display units.
|
2019-04-28 18:40:45 +00:00
|
|
|
int lineHeight(size_t index) const { return fLines[index].fHeight; }
|
|
|
|
|
2019-05-03 21:14:21 +00:00
|
|
|
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,
|
2019-07-30 13:58:45 +00:00
|
|
|
kWordLeft,
|
|
|
|
kWordRight,
|
2019-05-03 21:14:21 +00:00
|
|
|
};
|
2019-06-20 15:29:10 +00:00
|
|
|
TextPosition move(Editor::Movement move, Editor::TextPosition pos) const;
|
2019-05-03 21:14:21 +00:00
|
|
|
TextPosition getPosition(SkIPoint);
|
2019-07-29 18:44:04 +00:00
|
|
|
SkRect getLocation(TextPosition);
|
2019-06-20 15:29:10 +00:00
|
|
|
// insert into current text.
|
2019-05-03 21:14:21 +00:00
|
|
|
TextPosition insert(TextPosition, const char* utf8Text, size_t byteLen);
|
2019-06-20 15:29:10 +00:00
|
|
|
// remove text between two positions
|
2019-05-03 21:14:21 +00:00
|
|
|
TextPosition remove(TextPosition, TextPosition);
|
2019-06-20 15:29:10 +00:00
|
|
|
|
|
|
|
StringSlice copy(TextPosition, TextPosition) const;
|
2019-05-03 21:14:21 +00:00
|
|
|
|
|
|
|
struct PaintOpts {
|
|
|
|
SkColor4f fBackgroundColor = {1, 1, 1, 1};
|
|
|
|
SkColor4f fForegroundColor = {0, 0, 0, 1};
|
2019-06-20 15:29:10 +00:00
|
|
|
// TODO: maybe have multiple selections and cursors, each with separate colors.
|
2019-05-03 21:14:21 +00:00
|
|
|
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);
|
2019-04-28 18:40:45 +00:00
|
|
|
|
|
|
|
private:
|
2019-07-25 17:58:42 +00:00
|
|
|
// TODO: rename this to TextParagraph. fLines to fParas.
|
2019-04-28 18:40:45 +00:00
|
|
|
struct TextLine {
|
2019-05-03 21:14:21 +00:00
|
|
|
StringSlice fText;
|
2019-06-20 15:29:10 +00:00
|
|
|
sk_sp<const SkTextBlob> fBlob;
|
2019-04-30 16:38:28 +00:00
|
|
|
std::vector<SkRect> fCursorPos;
|
2019-07-25 17:58:42 +00:00
|
|
|
std::vector<unsigned> fLineEndOffsets;
|
2019-07-30 13:58:45 +00:00
|
|
|
std::vector<bool> fWordBoundaries;
|
2019-05-03 21:14:21 +00:00
|
|
|
SkIPoint fOrigin = {0, 0};
|
2019-04-28 18:40:45 +00:00
|
|
|
int fHeight = 0;
|
2019-06-20 15:29:10 +00:00
|
|
|
bool fShaped = false;
|
2019-04-28 18:40:45 +00:00
|
|
|
|
2019-06-20 15:29:10 +00:00
|
|
|
TextLine(StringSlice t) : fText(std::move(t)) {}
|
|
|
|
TextLine() {}
|
2019-04-28 18:40:45 +00:00
|
|
|
};
|
|
|
|
std::vector<TextLine> fLines;
|
|
|
|
int fMargin = 10;
|
|
|
|
int fWidth = 0;
|
|
|
|
int fHeight = 0;
|
2019-05-03 21:14:21 +00:00
|
|
|
SkFont fFont;
|
|
|
|
SkRect fSpaceBounds = {0, 0, 0, 0};
|
|
|
|
bool fNeedsReshape = false;
|
2019-07-30 13:58:45 +00:00
|
|
|
const char* fLocale = "en"; // TODO: make this setable
|
2019-04-30 16:38:28 +00:00
|
|
|
|
2019-07-30 13:58:45 +00:00
|
|
|
static void Shape(TextLine*, SkShaper*, float width, const SkFont&, SkRect, const char*);
|
2019-07-30 12:55:35 +00:00
|
|
|
void markDirty(TextLine*);
|
|
|
|
void markAllDirty() { for (auto& l : fLines) { this->markDirty(&l); } }
|
2019-05-03 21:14:21 +00:00
|
|
|
void reshapeAll();
|
2019-04-28 18:40:45 +00:00
|
|
|
};
|
|
|
|
} // namespace editor
|
2019-05-03 21:14:21 +00:00
|
|
|
|
|
|
|
static inline bool operator==(const editor::Editor::TextPosition& u,
|
|
|
|
const editor::Editor::TextPosition& v) {
|
|
|
|
return u.fParagraphIndex == v.fParagraphIndex && u.fTextByteIndex == v.fTextByteIndex;
|
|
|
|
}
|
|
|
|
static inline bool operator!=(const editor::Editor::TextPosition& u,
|
|
|
|
const editor::Editor::TextPosition& v) { return !(u == v); }
|
|
|
|
|
|
|
|
static inline bool operator<(const editor::Editor::TextPosition& u,
|
|
|
|
const editor::Editor::TextPosition& v) {
|
|
|
|
return u.fParagraphIndex < v.fParagraphIndex ||
|
|
|
|
(u.fParagraphIndex == v.fParagraphIndex && u.fTextByteIndex < v.fTextByteIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-28 18:40:45 +00:00
|
|
|
#endif // editor_DEFINED
|