diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 663458c20..2db62c47b 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -60,6 +60,7 @@ private: void scrollRightOfLayout(Poincare::Layout layoutR); void scrollToBaselinedRect(KDRect rect, KDCoordinate baseline); void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout = false); + bool eventShouldUpdateInsertionCursor(Ion::Events::Event event) { return event == Ion::Events::Up; } class ContentView : public View { public: @@ -83,14 +84,22 @@ private: void copySelection(Poincare::Context * context); bool selectionIsEmpty() const; void deleteSelection(); + void invalidateInsertionCursor() { m_insertionCursor = Poincare::LayoutCursor(); } + void updateInsertionCursor() { + if (!m_insertionCursor.isDefined()) { + m_insertionCursor = m_cursor; + } + } private: int numberOfSubviews() const override { return 2; } View * subviewAtIndex(int index) override; void layoutSubviews(bool force = false) override; void layoutCursorSubview(bool force); + void useInsertionCursor(); KDRect selectionRect() const; Poincare::LayoutCursor m_cursor; + Poincare::LayoutCursor m_insertionCursor; ExpressionView m_expressionView; TextCursorView m_cursorView; /* The selection starts on the left of m_selectionStart, and ends on the diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index 4002038c3..2e9094fe4 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -11,6 +11,7 @@ using namespace Poincare; LayoutField::ContentView::ContentView() : m_cursor(), + m_insertionCursor(), m_expressionView(0.0f, 0.5f, KDColorBlack, KDColorWhite, &m_selectionStart, &m_selectionEnd), m_cursorView(), m_selectionStart(), @@ -30,12 +31,21 @@ bool LayoutField::ContentView::setEditing(bool isEditing) { m_expressionView.layout().invalidAllSizesPositionsAndBaselines(); return true; } + } else { + // We're leaving the edition of the current layout + useInsertionCursor(); } layoutSubviews(); markRectAsDirty(bounds()); return false; } +void LayoutField::ContentView::useInsertionCursor() { + if (m_insertionCursor.isDefined()) { + m_cursor = m_insertionCursor; + } +} + void LayoutField::ContentView::clearLayout() { HorizontalLayout h = HorizontalLayout::Builder(); if (m_expressionView.setLayout(h)) { @@ -318,6 +328,12 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool * - the text added after a toolbox selection * - the result of a copy-paste. */ + /* This routing can be called even if no actual underlying event has been + * dispatched on the LayoutField. For instance, when someone wants to insert + * text in the field from the outside. In this scenario, let's make sure the + * insertionCursor is invalidated. */ + m_contentView.invalidateInsertionCursor(); + // Delete the selected layouts if needed deleteSelection(); @@ -382,6 +398,9 @@ bool LayoutField::handleEvent(Ion::Events::Event event) { KDSize previousSize = minimalSizeForOptimalDisplay(); bool shouldRecomputeLayout = m_contentView.cursor()->showEmptyLayoutIfNeeded(); bool moveEventChangedLayout = false; + if (!eventShouldUpdateInsertionCursor(event)) { + m_contentView.invalidateInsertionCursor(); + } if (privateHandleMoveEvent(event, &moveEventChangedLayout)) { if (!isEditing()) { setEditing(true); @@ -554,6 +573,9 @@ bool LayoutField::privateHandleMoveEvent(Ion::Events::Event event, bool * should LayoutCursor result; result = m_contentView.cursor()->cursorAtDirection(DirectionForMoveEvent(event), shouldRecomputeLayout); if (result.isDefined()) { + if (eventShouldUpdateInsertionCursor(event)) { + m_contentView.updateInsertionCursor(); + } m_contentView.setCursor(result); return true; }