From 3d991e56daeeac2d6db0fb11e108ec4da94db587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 27 May 2019 14:11:09 +0200 Subject: [PATCH] [escher/poincare] Cleaner way to find the cursored layout in LayoutField Everything is now in the virtual method LayoutNode::layoutToPointWhenInserting This removes a dirty inclusion of apps/i18n.h in escher --- escher/include/escher/layout_field.h | 2 +- escher/src/layout_field.cpp | 74 +++++++++---------- .../include/poincare/condensed_sum_layout.h | 4 +- poincare/include/poincare/fraction_layout.h | 2 +- poincare/include/poincare/horizontal_layout.h | 1 + poincare/include/poincare/integral_layout.h | 2 +- poincare/include/poincare/layout.h | 7 +- poincare/include/poincare/layout_node.h | 5 +- poincare/include/poincare/sequence_layout.h | 2 +- poincare/src/fraction_layout.cpp | 2 +- poincare/src/horizontal_layout.cpp | 15 ++++ poincare/src/layout_node.cpp | 5 ++ 12 files changed, 69 insertions(+), 52 deletions(-) diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 874719df8..3994e89d4 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -53,7 +53,7 @@ private: static_assert(k_maxNumberOfLayouts == TextField::maxBufferSize(), "Maximal number of layouts in a layout field should be equal to max number of char in text field"); void scrollRightOfLayout(Poincare::Layout layoutR); void scrollToBaselinedRect(KDRect rect, KDCoordinate baseline); - void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Layout pointedLayout, bool forceCursorRightOfLayout = false); + void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout = false); class ContentView : public View { public: diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index a25088ef0..912e070c7 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -91,6 +90,10 @@ void LayoutField::reload(KDSize previousSize) { } bool LayoutField::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) { + /* The text here can be: + * - the result of a key pressed, such as "," or "cos(•)" + * - the text added after a toolbox selection + * - the result of a copy-paste. */ if (text[0] == 0) { // The text is empty return true; @@ -122,31 +125,18 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool } else { Expression resultExpression = Expression::Parse(text); if (resultExpression.isUninitialized()) { + // The text is not parsable (for instance, ",") and is added char by char. KDSize previousLayoutSize = minimalSizeForOptimalDisplay(); m_contentView.cursor()->insertText(text); reload(previousLayoutSize); - } else { - Layout resultLayout = resultExpression.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::PrintFloat::k_numberOfStoredSignificantDigits); - if (currentNumberOfLayouts + resultLayout.numberOfDescendants(true) >= k_maxNumberOfLayouts) { - return true; - } - // Find the pointed layout. - Layout pointedLayout; - if (!forceCursorRightOfText) { - if (strcmp(text, I18n::translate(I18n::Message::RandomCommandWithArg)) == 0) { - /* Special case: if the text is "random()", the cursor should not be set - * inside the parentheses. */ - pointedLayout = resultLayout; - } else if (resultLayout.type() == LayoutNode::Type::HorizontalLayout) { - pointedLayout = resultLayout.recursivelyMatches( - [](Poincare::Layout layout) { - return layout.type() == LayoutNode::Type::LeftParenthesisLayout || layout.isEmpty();}); - } - } - /* Insert the layout. If pointedLayout is uninitialized, the cursor will - * be on the right of the inserted layout. */ - insertLayoutAtCursor(resultLayout, pointedLayout, forceCursorRightOfText); + return true; } + // The text is parsable, we create its layout an insert it. + Layout resultLayout = resultExpression.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::PrintFloat::k_numberOfStoredSignificantDigits); + if (currentNumberOfLayouts + resultLayout.numberOfDescendants(true) >= k_maxNumberOfLayouts) { + return true; + } + insertLayoutAtCursor(resultLayout, resultExpression, forceCursorRightOfText); } return true; } @@ -285,42 +275,44 @@ void LayoutField::scrollToBaselinedRect(KDRect rect, KDCoordinate baseline) { scrollToContentRect(balancedRect, true); } -void LayoutField::insertLayoutAtCursor(Layout layoutR, Layout pointedLayoutR, bool forceCursorRightOfLayout) { +void LayoutField::insertLayoutAtCursor(Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout) { if (layoutR.isUninitialized()) { return; } KDSize previousSize = minimalSizeForOptimalDisplay(); + Poincare::LayoutCursor * cursor = m_contentView.cursor(); // Handle empty layouts - m_contentView.cursor()->showEmptyLayoutIfNeeded(); + cursor->showEmptyLayoutIfNeeded(); bool layoutWillBeMerged = layoutR.type() == LayoutNode::Type::HorizontalLayout; Layout lastMergedLayoutChild = layoutWillBeMerged ? layoutR.childAtIndex(layoutR.numberOfChildren()-1) : Layout(); - // Add the layout - m_contentView.cursor()->addLayoutAndMoveCursor(layoutR); + // Find the layout where the cursor will point + assert(!correspondingExpression.isUninitialized()); + Layout cursorLayout = forceCursorRightOfLayout ? layoutR : layoutR.layoutToPointWhenInserting(&correspondingExpression); + assert(!cursorLayout.isUninitialized()); - // Move the cursor if needed - if(!forceCursorRightOfLayout) { - if (!pointedLayoutR.isUninitialized() && (!layoutWillBeMerged || pointedLayoutR != layoutR)) { - // Make sure the layout was inserted (its parent is not uninitialized) - m_contentView.cursor()->setLayout(pointedLayoutR); - m_contentView.cursor()->setPosition(LayoutCursor::Position::Right); - } else if (!layoutWillBeMerged) { - m_contentView.cursor()->setLayout(layoutR.layoutToPointWhenInserting()); - m_contentView.cursor()->setPosition(LayoutCursor::Position::Right); - } - } else if (!layoutWillBeMerged) { - m_contentView.cursor()->setLayout(layoutR); - m_contentView.cursor()->setPosition(LayoutCursor::Position::Right); + // Add the layout. This puts the cursor at the right of the added layout + cursor->addLayoutAndMoveCursor(layoutR); + + /* Move the cursor if needed. + * If the layout to point to has been merged, it means that only its children + * have been inserted in the layout, so we must not move the cursor to the + * parent. In this case, addLayoutAndMoveCursor made the cursor point to the + * last merged child, which is what is wanted. + * For other cases, move the cursor to the computed layout. */ + if (!(layoutWillBeMerged && cursorLayout == layoutR)) { + cursor->setLayout(cursorLayout); + cursor->setPosition(LayoutCursor::Position::Right); } // Handle matrices - m_contentView.cursor()->layoutReference().addGreySquaresToAllMatrixAncestors(); + cursor->layoutReference().addGreySquaresToAllMatrixAncestors(); // Handle empty layouts - m_contentView.cursor()->hideEmptyLayoutIfNeeded(); + cursor->hideEmptyLayoutIfNeeded(); // Reload reload(previousSize); diff --git a/poincare/include/poincare/condensed_sum_layout.h b/poincare/include/poincare/condensed_sum_layout.h index 0495cbdb1..688b78bc4 100644 --- a/poincare/include/poincare/condensed_sum_layout.h +++ b/poincare/include/poincare/condensed_sum_layout.h @@ -26,9 +26,9 @@ public: return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Sum::s_functionHelper.name()); } - LayoutNode * layoutToPointWhenInserting() override { + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override { assert(false); - return nullptr; + return this; } // TreeNode diff --git a/poincare/include/poincare/fraction_layout.h b/poincare/include/poincare/fraction_layout.h index 423198210..94eeba128 100644 --- a/poincare/include/poincare/fraction_layout.h +++ b/poincare/include/poincare/fraction_layout.h @@ -27,7 +27,7 @@ public: int leftCollapsingAbsorbingChildIndex() const override { return 0; } int rightCollapsingAbsorbingChildIndex() const override { return 1; } void didCollapseSiblings(LayoutCursor * cursor) override; - LayoutNode * layoutToPointWhenInserting() override; + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override; bool canBeOmittedMultiplicationRightFactor() const override { return false; } /* WARNING: We need to override this function, else 1/2 3/4 would be * serialized as 1/2**3/4, as the two Fraction layouts think their sibling is diff --git a/poincare/include/poincare/horizontal_layout.h b/poincare/include/poincare/horizontal_layout.h index 1333108a8..fa3fe7ec6 100644 --- a/poincare/include/poincare/horizontal_layout.h +++ b/poincare/include/poincare/horizontal_layout.h @@ -26,6 +26,7 @@ public: void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; LayoutCursor equivalentCursor(LayoutCursor * cursor) override; void deleteBeforeCursor(LayoutCursor * cursor) override; + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; bool isEmpty() const override { return m_numberOfChildren == 1 && const_cast(this)->childAtIndex(0)->isEmpty(); } diff --git a/poincare/include/poincare/integral_layout.h b/poincare/include/poincare/integral_layout.h index 1a4836c61..cbd03b1a0 100644 --- a/poincare/include/poincare/integral_layout.h +++ b/poincare/include/poincare/integral_layout.h @@ -24,7 +24,7 @@ public: void moveCursorDown(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) override; void deleteBeforeCursor(LayoutCursor * cursor) override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; - LayoutNode * layoutToPointWhenInserting() override { return lowerBoundLayout(); } + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override { return lowerBoundLayout(); } CodePoint XNTCodePoint() const override { return 'x'; } // TreeNode diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index 8aa19ad4e..f9eaa0ad0 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -8,6 +8,7 @@ namespace Poincare { class LayoutCursor; +class Expression; class Layout : public TreeHandle { friend class GridLayoutNode; @@ -59,7 +60,11 @@ public: void deleteBeforeCursor(LayoutCursor * cursor) { return node()->deleteBeforeCursor(cursor); } bool removeGreySquaresFromAllMatrixAncestors() { return node()->removeGreySquaresFromAllMatrixAncestors(); } bool addGreySquaresToAllMatrixAncestors() { return node()->addGreySquaresToAllMatrixAncestors(); } - Layout layoutToPointWhenInserting() { return Layout(node()->layoutToPointWhenInserting()); } + Layout layoutToPointWhenInserting(Expression * correspondingExpression) { + // Pointer to correspondingExpr because expression.h includes layout.h + assert(correspondingExpression != nullptr); + return Layout(node()->layoutToPointWhenInserting(correspondingExpression)); + } // Cursor LayoutCursor cursor() const; diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index 2786ec135..3c82e2720 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -6,6 +6,7 @@ namespace Poincare { +class Expression; class LayoutCursor; class Layout; @@ -102,9 +103,7 @@ public: virtual void deleteBeforeCursor(LayoutCursor * cursor); // Other - virtual LayoutNode * layoutToPointWhenInserting() { - return numberOfChildren() > 0 ? childAtIndex(0) : this; - } + virtual LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression); bool removeGreySquaresFromAllMatrixAncestors() { return changeGreySquaresOfAllMatrixAncestors(false); } bool addGreySquaresToAllMatrixAncestors() { return changeGreySquaresOfAllMatrixAncestors(true); } /* A layout has text if it is not empty and it is not an horizontal layout diff --git a/poincare/include/poincare/sequence_layout.h b/poincare/include/poincare/sequence_layout.h index 9f5608adc..c301d120e 100644 --- a/poincare/include/poincare/sequence_layout.h +++ b/poincare/include/poincare/sequence_layout.h @@ -19,7 +19,7 @@ public: void moveCursorUp(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) override; void moveCursorDown(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) override; void deleteBeforeCursor(LayoutCursor * cursor) override; - LayoutNode * layoutToPointWhenInserting() override { return lowerBoundLayout(); } + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override { return lowerBoundLayout(); } CodePoint XNTCodePoint() const override { return 'n'; } // TreeNode diff --git a/poincare/src/fraction_layout.cpp b/poincare/src/fraction_layout.cpp index 8af85a37d..589ba4fcf 100644 --- a/poincare/src/fraction_layout.cpp +++ b/poincare/src/fraction_layout.cpp @@ -166,7 +166,7 @@ int FractionLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr return numberOfChar; } -LayoutNode * FractionLayoutNode::layoutToPointWhenInserting() { +LayoutNode * FractionLayoutNode::layoutToPointWhenInserting(Expression * correspondingExpression) { if (numeratorLayout()->isEmpty()){ return numeratorLayout(); } diff --git a/poincare/src/horizontal_layout.cpp b/poincare/src/horizontal_layout.cpp index 475ab8737..260ef72da 100644 --- a/poincare/src/horizontal_layout.cpp +++ b/poincare/src/horizontal_layout.cpp @@ -164,6 +164,21 @@ void HorizontalLayoutNode::deleteBeforeCursor(LayoutCursor * cursor) { LayoutNode::deleteBeforeCursor(cursor); } +LayoutNode * HorizontalLayoutNode::layoutToPointWhenInserting(Expression * correspondingExpression) { + assert(correspondingExpression != nullptr); + if (correspondingExpression->numberOfChildren() > 0) { + Layout layoutToPointTo = Layout(this).recursivelyMatches( + [](Poincare::Layout layout) { + return layout.type() == LayoutNode::Type::LeftParenthesisLayout || layout.isEmpty(); + } + ); + if (!layoutToPointTo.isUninitialized()) { + return layoutToPointTo.node(); + } + } + return this; +} + int HorizontalLayoutNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { if (numberOfChildren() == 0) { if (bufferSize == 0) { diff --git a/poincare/src/layout_node.cpp b/poincare/src/layout_node.cpp index fa6341dda..ba236cacd 100644 --- a/poincare/src/layout_node.cpp +++ b/poincare/src/layout_node.cpp @@ -110,6 +110,11 @@ void LayoutNode::deleteBeforeCursor(LayoutCursor * cursor) { // WARNING: Do no use "this" afterwards } +LayoutNode * LayoutNode::layoutToPointWhenInserting(Expression * correspondingExpression) { + assert(correspondingExpression != nullptr); + return numberOfChildren() > 0 ? childAtIndex(0) : this; +} + bool LayoutNode::willRemoveChild(LayoutNode * l, LayoutCursor * cursor, bool force) { if (!force) { Layout(this).replaceChildWithEmpty(Layout(l), cursor);