[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
This commit is contained in:
Léa Saviot
2019-05-27 14:11:09 +02:00
parent 77df361b3f
commit 852c43c092
12 changed files with 69 additions and 52 deletions

View File

@@ -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:

View File

@@ -1,5 +1,4 @@
#include <escher/layout_field.h>
#include <apps/i18n.h>
#include <escher/clipboard.h>
#include <escher/text_field.h>
#include <poincare/expression.h>
@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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<HorizontalLayoutNode *>(this)->childAtIndex(0)->isEmpty(); }

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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);