Text Editor based on SkText: delete and backspace

Change-Id: I1152f83af724634f06e1a767ef39bef2afe70b33
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/430656
Reviewed-by: Julia Lavrova <jlavrova@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/experimental/sktext/editor/Editor.cpp b/experimental/sktext/editor/Editor.cpp
index 61f3c13..4238287 100644
--- a/experimental/sktext/editor/Editor.cpp
+++ b/experimental/sktext/editor/Editor.cpp
@@ -98,7 +98,31 @@
     auto rect = std::get<3>(endOfText);
     fCursor->place(SkPoint::Make(rect.fLeft, rect.fTop),
                    SkSize::Make(std::max(DEFAULT_CURSOR_WIDTH, rect.width()), rect.height()));
-                   //SkSize::Make(DEFAULT_CURSOR_WIDTH, rect.height()));
+}
+
+void Editor::updateText(std::u16string text) {
+    // TODO: Update text styles
+    sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40);
+    Block block(fText.size(), fontChain);
+    auto fontBlocks = SkSpan<Block>(&block, 1);
+
+    // TODO: update all objects rather than recreate them
+    fText = std::move(text);
+    fUnicodeText = Text::parse(SkSpan<uint16_t>((uint16_t*)fText.data(), fText.size()));
+    fShapedText = fUnicodeText->shape(fontBlocks, TEXT_DIRECTION);
+    fWrappedText = fShapedText->wrap(fWidth, fHeight, fUnicodeText->getUnicode());
+    fFormattedText = fWrappedText->format(TEXT_ALIGN, TEXT_DIRECTION);
+
+    // Update the cursor
+    auto endOfText = fFormattedText->indexToAdjustedGraphemePosition(fText.size());
+    auto rect = std::get<3>(endOfText);
+    fCursor->place(SkPoint::Make(rect.fLeft, rect.fTop),
+                   SkSize::Make(std::max(DEFAULT_CURSOR_WIDTH, rect.width()), rect.height()));
+    // TODO: Update the mouse
+    fMouse->clearTouchInfo();
+
+    // TODO: Update the text selection
+    fSelection->clear();
 }
 
 bool Editor::moveCursor(skui::Key key) {
@@ -174,16 +198,10 @@
     modi &= ~skui::ModifierKey::kFirstPress;
     if (!Any(modi & (skui::ModifierKey::kControl | skui::ModifierKey::kOption |
                      skui::ModifierKey::kCommand))) {
-        /*
         if (((unsigned)c < 0x7F && (unsigned)c >= 0x20) || c == '\n') {
-            char ch = (char)c;
-            fEditor.insert(fTextPos, &ch, 1);
-            #ifdef SK_EDITOR_DEBUG_OUT
-            SkDebugf("insert: %X'%c'\n", (unsigned)c, ch);
-            #endif  // SK_EDITOR_DEBUG_OUT
-            return this->moveCursor(Editor::Movement::kRight);
+            insertCodepoint(c);
+            return true;
         }
-        */
     }
     static constexpr skui::ModifierKey kCommandOrControl =
             skui::ModifierKey::kCommand | skui::ModifierKey::kControl;
@@ -193,6 +211,51 @@
     return false;
 }
 
+bool Editor::deleteGrapheme(skui::Key key) {
+    auto cursorPosition = fCursor->getCenterPosition();
+    TextIndex startIndex = fFormattedText->positionToAdjustedGraphemeIndex(cursorPosition);
+    TextIndex endIndex = startIndex;
+    if (key == skui::Key::kBack) {
+        --startIndex;
+        fShapedText->adjustLeft(&startIndex);
+    } else {
+        ++endIndex;
+        fShapedText->adjustRight(&endIndex);
+    }
+
+    std::u16string text;
+    text.append(fText.substr(0, startIndex));
+    text.append(fText.substr(endIndex, std::u16string::npos));
+    updateText(text);
+
+    auto position = fFormattedText->indexToAdjustedGraphemePosition(startIndex);
+    SkRect cursorRect = std::get<3>(position);
+    fCursor->place(SkPoint::Make(cursorRect.fLeft, cursorRect.fTop), SkSize::Make(cursorRect.width(), cursorRect.height()));
+
+    this->invalidate();
+
+    return true;
+}
+
+bool Editor::insertCodepoint(SkUnichar unichar) {
+    auto cursorPosition = fCursor->getCenterPosition();
+    auto textIndex = fFormattedText->positionToAdjustedGraphemeIndex(cursorPosition);
+
+    std::u16string text;
+    text.append(fText.substr(0, textIndex));
+    text.append(1, (unsigned)unichar);
+    text.append(fText.substr(textIndex, std::u16string::npos));
+    updateText(text);
+
+    auto nextPosition = fFormattedText->indexToAdjustedGraphemePosition(textIndex);
+    SkRect cursorRect = std::get<3>(nextPosition);
+    fCursor->place(SkPoint::Make(cursorRect.fLeft, cursorRect.fTop), SkSize::Make(cursorRect.width(), cursorRect.height()));
+
+    this->invalidate();
+
+    return true;
+}
+
 bool Editor::onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers) {
 
     fSelection->clear();
@@ -216,28 +279,12 @@
             case skui::Key::kEnd:
                 this->moveCursor(key);
                 break;
-            /*
             case skui::Key::kDelete:
-                if (fMarkPos != Editor::TextPosition()) {
-                    (void)this->move(fEditor.remove(fMarkPos, fTextPos), false);
-                } else {
-                    auto pos = fEditor.move(Editor::Movement::kRight, fTextPos);
-                    (void)this->move(fEditor.remove(fTextPos, pos), false);
-                }
-                this->inval();
-                return true;
             case skui::Key::kBack:
-                if (fMarkPos != Editor::TextPosition()) {
-                    (void)this->move(fEditor.remove(fMarkPos, fTextPos), false);
-                } else {
-                    auto pos = fEditor.move(Editor::Movement::kLeft, fTextPos);
-                    (void)this->move(fEditor.remove(fTextPos, pos), false);
-                }
-                this->inval();
+                this->deleteGrapheme(key);
                 return true;
             case skui::Key::kOK:
                 return this->onChar('\n', modifiers);
-            */
             default:
                 break;
         }
diff --git a/experimental/sktext/editor/Editor.h b/experimental/sktext/editor/Editor.h
index 0278ce8..664cc64 100644
--- a/experimental/sktext/editor/Editor.h
+++ b/experimental/sktext/editor/Editor.h
@@ -114,6 +114,8 @@
 
     Editor(std::u16string text, SkSize size, SkSpan<Block> fontBlocks);
 
+    void updateText(std::u16string text);
+
     void onAttach(sk_app::Window* w) override { fParent = w; }
     void onPaint(SkSurface* surface) override;
 
@@ -133,6 +135,8 @@
     void onPlaceholder(TextRange, const SkRect& bounds) override;
 
     bool moveCursor(skui::Key key);
+    bool insertCodepoint(SkUnichar unichar);
+    bool deleteGrapheme(skui::Key key);
 
     std::unique_ptr<Cursor> fCursor;
     std::unique_ptr<Mouse> fMouse;
diff --git a/experimental/sktext/include/Text.h b/experimental/sktext/include/Text.h
index a55dca5..2df92a6f 100644
--- a/experimental/sktext/include/Text.h
+++ b/experimental/sktext/include/Text.h
@@ -82,6 +82,15 @@
             --index;
         }
     }
+    void adjustRight(size_t* index) const {
+        SkASSERT(index != nullptr);
+        while (*index < this->fGlyphUnitProperties.size()) {
+            if (isClusterEdge(*index)) {
+                return;
+            }
+            ++index;
+        }
+    }
     bool hasProperty(size_t index, GlyphUnitFlags flag) {
         return (fGlyphUnitProperties[index] & flag) == flag;
     }
diff --git a/experimental/sktext/src/Line.h b/experimental/sktext/src/Line.h
index a47d1e7..791ab51 100644
--- a/experimental/sktext/src/Line.h
+++ b/experimental/sktext/src/Line.h
@@ -170,6 +170,11 @@
     ~Line() = default;
 
     TextMetrics getMetrics() const { return fTextMetrics; }
+    GlyphPos glyphStart() const { return fTextStart; }
+    GlyphPos glyphEnd() const { return fTextEnd; }
+    GlyphPos glyphTrailingEnd() const { return fWhitespacesEnd; }
+    SkScalar width() const { return fTextWidth; }
+    SkScalar withWithTrailingSpaces() const { return fTextWidth + fSpacesWidth; }
 
 private:
     friend class WrappedText;