[poincare] Translate methods to new tree structure

This commit is contained in:
Léa Saviot
2018-07-04 11:53:26 +02:00
parent 6a77da86f7
commit 2eba0825d2
10 changed files with 246 additions and 34 deletions

View File

@@ -17,9 +17,11 @@ public:
m_numberOfChildren(0)
{}
bool isHorizontal() const override { return true; }
void addOrMergeChildAtIndex(LayoutNode * l, int index, bool removeEmptyChildren);
void mergeChildrenAtIndex(HorizontalLayoutNode * horizontalLayout, int index, bool removeEmptyChildren);
// LayoutNode
bool isHorizontal() const override { return true; }
int writeTextInBuffer(char * buffer, int bufferSize, int numberOfSignificantDigits = PrintFloat::k_numberOfStoredSignificantDigits) const override;
void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override;
void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override;
@@ -46,7 +48,10 @@ protected:
KDPoint positionOfChild(LayoutNode * l) override;
private:
void privateAddSibling(LayoutCursor * cursor, LayoutNode * sibling, bool moveCursor) override;
void privateRemoveChildAtIndex(int index, bool forceRemove);
void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override {}
int removeEmptyChildBeforeInsertionAtIndex(int index, bool shouldRemoveOnLeft);
int m_numberOfChildren;
};

View File

@@ -50,7 +50,6 @@ public:
bool isDefined() const { return m_layoutRef.isDefined(); }
/* Getters and setters */
LayoutRef layoutReference() { return m_layoutRef; }
int layoutIdentifier() { return m_layoutRef.identifier(); }
@@ -97,7 +96,6 @@ public:
return result;
}
/* Layout modification */
void clearLayout() {} //TODO
void addFractionLayoutAndCollapseSiblings() {} //TODO
void addEmptyExponentialLayout() {} //TODO
void addEmptyPowerLayout() {} //TODO
@@ -109,7 +107,8 @@ public:
void performBackspace() {} //TODO
bool showEmptyLayoutIfNeeded() { return false; } //TODO
bool hideEmptyLayoutIfNeeded() { return false; } //TODO
void addLayoutAndMoveCursor(LayoutRef l) {} //TODO
void addLayoutAndMoveCursor(LayoutRef l);
void clearLayout();
private:
constexpr static KDCoordinate k_cursorHeight = 18;

View File

@@ -25,7 +25,10 @@ public:
virtual bool hasText() const { return false; } //TODO
virtual char XNTChar() const { return 'x'; }
virtual bool isHorizontal() const { return false; }
virtual bool isEmpty() const { return false; }
virtual bool isLeftParenthesis() const { return false; }
virtual bool isVerticalOffset() const { return false; }
virtual bool mustHaveLeftSibling() const { return false; }
// Rendering
void draw(KDContext * ctx, KDPoint p, KDColor expressionColor = KDColorBlack, KDColor backgroundColor = KDColorWhite);
@@ -48,6 +51,7 @@ public:
// Hierarchy
LayoutNode * parent() const { return static_cast<LayoutNode *>(parentTree()); }
LayoutNode * childAtIndex(int i) { return static_cast<LayoutNode *>(childTreeAtIndex(i)); }
LayoutNode * root() { return static_cast<LayoutNode *>(rootTree()); }
// Tree navigation
virtual void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) {}
@@ -57,9 +61,13 @@ public:
virtual LayoutCursor equivalentCursor(LayoutCursor * cursor); //TODO
// Tree modification
void addSibling(LayoutCursor * cursor, LayoutNode * sibling);
void addSiblingAndMoveCursor(LayoutCursor * cursor, LayoutNode * sibling);
void collapseSiblingsAndMoveCursor(LayoutCursor * cursor) {} //TODO
bool removeGreySquaresFromAllMatrixAncestors() { return false; } //TODO
bool addGreySquaresToAllMatrixAncestors() { return false; } //TODO
virtual LayoutNode * layoutToPointWhenInserting() { return this; } //TODO
LayoutNode * replaceWithJuxtapositionOf(LayoutNode * leftChild, LayoutNode * rightChild) {return nullptr; } //TODO
protected:
// Iterators
class Iterator {
@@ -102,6 +110,7 @@ protected:
bool m_positioned;
bool m_sized;
private:
virtual void privateAddSibling(LayoutCursor * cursor, LayoutNode * sibling, bool moveCursor);
virtual void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) = 0;
};

View File

@@ -14,11 +14,10 @@ class LayoutReference : public TreeReference<T> {
public:
using TreeReference<T>::TreeReference;
/* Allow every LayoutReference<T> to be transformed into a
* LayoutReference<LayoutNode>, i.e. Layout */
operator LayoutReference<LayoutNode>() const {
return LayoutReference<LayoutNode>(this->node());
}
// Operators
// Allow every LayoutReference<T> to be transformed into a LayoutRef
operator LayoutReference<LayoutNode>() const { return LayoutReference<LayoutNode>(this->node()); }
LayoutReference& operator=(LayoutReference<LayoutNode>& lr) {
this->setTo(lr);
return *this;
@@ -30,36 +29,49 @@ public:
inline bool operator==(LayoutReference<LayoutNode> l) { return this->identifier() == l.identifier(); }
inline bool operator!=(LayoutReference<LayoutNode> l) { return this->identifier() != l.identifier(); }
static TreeNode * FailedAllocationStaticNode();
// Rendering
void draw(KDContext * ctx, KDPoint p, KDColor expressionColor = KDColorBlack, KDColor backgroundColor = KDColorWhite) {
return this->typedNode()->draw(ctx, p, expressionColor, backgroundColor);
}
KDSize layoutSize() { return this->typedNode()->layoutSize(); }
KDPoint layoutOrigin() { return this->typedNode()->layoutOrigin(); }
KDPoint absoluteOrigin() { return this->typedNode()->absoluteOrigin(); }
KDCoordinate baseline() { return this->typedNode()->baseline(); }
void invalidAllSizesPositionsAndBaselines() { return this->typedNode()->invalidAllSizesPositionsAndBaselines(); }
// Layout properties
bool isHorizontal() const { return this->typedNode()->isHorizontal(); }
bool isLeftParenthesis() const { return this->typedNode()->isLeftParenthesis(); }
bool hasText() { return this->typedNode()->hasText(); }
char XNTChar() const { return this->typedNode()->XNTChar(); }
// Layout modification
bool removeGreySquaresFromAllMatrixAncestors() { return this->typedNode()->removeGreySquaresFromAllMatrixAncestors(); }
bool addGreySquaresToAllMatrixAncestors() { return this->typedNode()->addGreySquaresToAllMatrixAncestors(); }
LayoutReference<LayoutNode> layoutToPointWhenInserting() { return LayoutReference<LayoutNode>(this->typedNode()->layoutToPointWhenInserting()); }
// Cursor
LayoutCursor cursor() const;
LayoutCursor equivalentCursor(LayoutCursor * cursor);
// Hierarchy
LayoutReference<LayoutNode> childAtIndex(int i) {
TreeReference<T> treeRefChild = TreeReference<T>::treeChildAtIndex(i);
return LayoutReference<LayoutNode>(treeRefChild.node());
}
LayoutReference<LayoutNode> root() { return LayoutReference<LayoutNode>(this->typedNode()->root()); }
// Hierarchy modification
void replaceChildAtIndex(int oldChildIndex, LayoutReference<LayoutNode> newChild) {
TreeReference<T>::replaceChildAtIndex(oldChildIndex, newChild);
}
void addSiblingAndMoveCursor(LayoutCursor * cursor, LayoutReference<LayoutNode> sibling) { return this->typedNode()->addSiblingAndMoveCursor(cursor, sibling.typedNode()); } //TODO
void collapseSiblingsAndMoveCursor(LayoutCursor * cursor) {} //TODO
LayoutReference<LayoutNode> replaceWithJuxtapositionOf(LayoutReference<LayoutNode> leftChild, LayoutReference<LayoutNode> rightChild); //TODO
void draw(KDContext * ctx, KDPoint p, KDColor expressionColor = KDColorBlack, KDColor backgroundColor = KDColorWhite) {
return this->typedNode()->draw(ctx, p, expressionColor, backgroundColor);
}
bool isHorizontal() const { return this->typedNode()->isHorizontal(); }
bool isLeftParenthesis() const { return this->typedNode()->isLeftParenthesis(); }
bool hasText() { return this->typedNode()->hasText(); }
char XNTChar() const { return this->typedNode()->XNTChar(); }
KDSize layoutSize() { return this->typedNode()->layoutSize(); }
KDPoint layoutOrigin() { return this->typedNode()->layoutOrigin(); }
KDPoint absoluteOrigin() { return this->typedNode()->absoluteOrigin(); }
KDCoordinate baseline() { return this->typedNode()->baseline(); }
LayoutCursor equivalentCursor(LayoutCursor * cursor);
void invalidAllSizesPositionsAndBaselines() { return this->typedNode()->invalidAllSizesPositionsAndBaselines(); }
bool removeGreySquaresFromAllMatrixAncestors() { return this->typedNode()->removeGreySquaresFromAllMatrixAncestors(); }
bool addGreySquaresToAllMatrixAncestors() { return this->typedNode()->addGreySquaresToAllMatrixAncestors(); }
LayoutReference<LayoutNode> layoutToPointWhenInserting() { return LayoutReference<LayoutNode>(this->typedNode()->layoutToPointWhenInserting()); }
// Allocation failure
static TreeNode * FailedAllocationStaticNode();
};
typedef LayoutReference<LayoutNode> LayoutRef;

View File

@@ -35,9 +35,7 @@ public:
}
}
virtual const char * description() const {
return "UNKNOWN";
}
virtual const char * description() const { return "UNKNOWN";}
// Serialization
virtual bool needsParenthesisWithParent(TreeNode * parentNode) { return false; } //TODO virtual pure and override on expresionNode/layoutNode
@@ -66,7 +64,7 @@ public:
// Hierarchy
TreeNode * parentTree() const;
TreeNode * editableRootTree();
TreeNode * rootTree();
virtual int numberOfChildren() const = 0;
virtual void incrementNumberOfChildren(int increment = 1) {} //TODO Put an assert false
virtual void decrementNumberOfChildren(int decrement = 1) {} //TODO Put an assert false //TODO what if somebody i stealing a unary tree's only child ?

View File

@@ -234,10 +234,11 @@ public:
TreePool::sharedPool()->move(secondChild.node(), firstChildNode);
}
void mergeChildren(TreeReference<T> t) {
void mergeChildrenAtIndex(TreeReference<T> t, int i) {
assert(i >= 0 && i <= numberOfChildren());
// Steal operands
int numberOfNewChildren = t.numberOfChildren();
TreePool::sharedPool()->moveChildren(t.node(), node()->lastDescendant()->next());
TreePool::sharedPool()->moveChildren(t.node(), node()->childTreeAtIndex(i)->nextSibling());
t.node()->eraseNumberOfChildren();
// If t is a child, remove it
if (node()->hasChild(t.node())) {

View File

@@ -5,6 +5,26 @@ namespace Poincare {
static inline KDCoordinate maxCoordinate(KDCoordinate c1, KDCoordinate c2) { return c1 > c2 ? c1 : c2; }
void HorizontalLayoutNode::addOrMergeChildAtIndex(LayoutNode * l, int index, bool removeEmptyChildren) {
if (l->isHorizontal()) {
mergeChildrenAtIndex(static_cast<HorizontalLayoutNode *>(l), index, removeEmptyChildren);
} else {
addChildAtIndex(l, index);
}
}
void HorizontalLayoutNode::mergeChildrenAtIndex(HorizontalLayoutNode * h, int index, bool removeEmptyChildren) {
/* Remove any empty child that would be next to the inserted layout.
* If the layout to insert starts with a vertical offset layout, any empty
* layout child directly on the left of the inserted layout (if there is one)
* should not be removed: it will be the base for the VerticalOffsetLayout. */
bool shouldRemoveOnLeft = h->numberOfChildren() == 0 ? true : !(h->childAtIndex(0)->mustHaveLeftSibling());
int newIndex = removeEmptyChildBeforeInsertionAtIndex(index, shouldRemoveOnLeft);
// Merge the horizontal layout
LayoutRef(this).mergeChildrenAtIndex(LayoutRef(h), newIndex);
}
int HorizontalLayoutNode::writeTextInBuffer(char * buffer, int bufferSize, int numberOfSignificantDigits) const {
if (numberOfChildren() == 0) {
if (bufferSize == 0) {
@@ -101,4 +121,87 @@ KDPoint HorizontalLayoutNode::positionOfChild(LayoutNode * l) {
return KDPoint(x, y);
}
// Private
void HorizontalLayoutNode::privateAddSibling(LayoutCursor * cursor, LayoutNode * sibling, bool moveCursor) {
int childrenCount = numberOfChildren();
// Add the "sibling" as a child.
if (cursor->position() == LayoutCursor::Position::Left) {
int indexForInsertion = 0;
/* If the first child is empty, remove it before adding the layout, unless
* the new sibling needs the empty layout as a base. */
if (childrenCount > 0 && childAtIndex(0)->isEmpty()) {
if (sibling->mustHaveLeftSibling()) {
indexForInsertion = 1;
} else {
/* We force the removal of the child even followed by a neighbourg
* requiring a left sibling as we are about to add a sibling in first
* position anyway. */
privateRemoveChildAtIndex(0, true);
}
}
if (moveCursor) {
if (childrenCount > indexForInsertion) {
cursor->setLayoutNode(childAtIndex(indexForInsertion));
} else {
cursor->setLayoutNode(this);
cursor->setPosition(LayoutCursor::Position::Right);
}
}
addOrMergeChildAtIndex(sibling, indexForInsertion, false);
return;
}
assert(cursor->position() == LayoutCursor::Position::Right);
// If the last child is empty, remove it before adding the layout.
if (childrenCount > 0 && childAtIndex(childrenCount - 1)->isEmpty() && !sibling->mustHaveLeftSibling()) {
/* Force remove the last child. */
privateRemoveChildAtIndex(childrenCount - 1, true);
childrenCount--;
}
addOrMergeChildAtIndex(sibling, childrenCount, false);
if (moveCursor) {
cursor->setLayoutNode(this);
}
}
void HorizontalLayoutNode::privateRemoveChildAtIndex(int index, bool forceRemove) {
/* Remove the child before potentially adding an EmptyLayout. Indeed, adding
* a new child would remove any EmptyLayout surrounding the new child and in
* the case the child to be removed was an Empty layout, it would result in
* removing it twice if we remove it afterwards. */
removeChild(childAtIndex(index));
/* If the child to remove is at index 0 and its right sibling must have a left
* sibling (e.g. it is a VerticalOffsetLayout), replace the child with an
* EmptyLayout instead of removing it. */
/*if (!forceRemove && index == 0 && numberOfChildren() > 0 && child(0)->mustHaveLeftSibling()) {
addChildAtIndex(new EmptyLayout(), 0);
}*/ //TODO
}
int HorizontalLayoutNode::removeEmptyChildBeforeInsertionAtIndex(int index, bool shouldRemoveOnLeft) {
int currentNumberOfChildren = numberOfChildren();
assert(index >= 0 && index <= currentNumberOfChildren);
int newIndex = index;
/* If empty, remove the child that would be on the right of the inserted
* layout. */
if (newIndex < currentNumberOfChildren) {
LayoutNode * c = childAtIndex(newIndex);
if (c->isEmpty()) {
removeChild(c);
currentNumberOfChildren--;
}
}
/* If empty, remove the child that would be on the left of the inserted
* layout. */
if (shouldRemoveOnLeft && newIndex - 1 >= 0 && newIndex - 1 <= currentNumberOfChildren -1) {
LayoutNode * c = childAtIndex(newIndex - 1);
if (c->isEmpty()) {
removeChild(c);
newIndex = index - 1;
}
}
return newIndex;
}
}

View File

@@ -58,6 +58,22 @@ void LayoutCursor::moveUnder(bool * shouldRecomputeLayout) {
layoutReference().typedNode()->moveCursorDown(this, shouldRecomputeLayout);
}
/* Layout modification */
void LayoutCursor::addLayoutAndMoveCursor(LayoutRef l) {
bool layoutWillBeMerged = l.isHorizontal();
m_layoutRef.addSiblingAndMoveCursor(this, l); //TODO
if (!layoutWillBeMerged) {
l.collapseSiblingsAndMoveCursor(this);
}
}
void LayoutCursor::clearLayout() {
LayoutRef rootLayoutR = m_layoutRef.root();
assert(rootLayoutR.isHorizontal());
rootLayoutR.removeChildren();
m_layoutRef = rootLayoutR;
}
/* Private */
KDCoordinate LayoutCursor::layoutHeight() {

View File

@@ -1,5 +1,6 @@
#include <poincare/layout_node.h>
#include <poincare/allocation_failed_layout_node.h>
#include <poincare/horizontal_layout_node.h>
#include <poincare/layout_cursor.h>
#include <poincare/layout_reference.h>
@@ -71,4 +72,72 @@ LayoutCursor LayoutNode::equivalentCursor(LayoutCursor * cursor) {
return LayoutCursor(cursor->layoutReference());
}
// Tree modification
void LayoutNode::addSibling(LayoutCursor * cursor, LayoutNode * sibling) {
privateAddSibling(cursor, sibling, false);
}
void LayoutNode::addSiblingAndMoveCursor(LayoutCursor * cursor, LayoutNode * sibling) {
privateAddSibling(cursor, sibling, true);
}
// Private
void LayoutNode::privateAddSibling(LayoutCursor * cursor, LayoutNode * sibling, bool moveCursor) {
/* The layout must have a parent, because HorizontalLayout overrides
* privateAddSibling and only an HorizontalLayout can be the root layout. */
LayoutNode * p = parent();
assert(p != nullptr);
if (p->isHorizontal()) {
int indexInParent = p->indexOfChild(this);
int siblingIndex = cursor->position() == LayoutCursor::Position::Left ? indexInParent : indexInParent + 1;
/* Special case: If the neighbour sibling is a VerticalOffsetLayout, let it
* handle the insertion of the new sibling. Do not enter the special case if
* "this" is a VerticalOffsetLayout, to avoid an infinite loop. */
if (!isVerticalOffset()) {
LayoutNode * neighbour = nullptr;
if (cursor->position() == LayoutCursor::Position::Left && indexInParent > 0) {
neighbour = p->childAtIndex(indexInParent - 1);
} else if (cursor->position() ==LayoutCursor::Position::Right && indexInParent < p->numberOfChildren() - 1) {
neighbour = p->childAtIndex(indexInParent + 1);
}
if (neighbour != nullptr && neighbour->isVerticalOffset()) {
cursor->setLayoutNode(neighbour);
cursor->setPosition(cursor->position() == LayoutCursor::Position::Left ? LayoutCursor::Position::Right : LayoutCursor::Position::Left);
if (moveCursor) {
neighbour->addSiblingAndMoveCursor(cursor, sibling);
} else {
neighbour->addSibling(cursor, sibling);
}
return;
}
}
// Else, let the parent add the sibling.
if (moveCursor) {
if (siblingIndex < p->numberOfChildren()) {
cursor->setLayoutNode(p->childAtIndex(siblingIndex));
cursor->setPosition(LayoutCursor::Position::Left);
} else {
cursor->setLayoutNode(p);
cursor->setPosition(LayoutCursor::Position::Right);
}
}
static_cast<HorizontalLayoutNode *>(p)->addOrMergeChildAtIndex(sibling, siblingIndex, true); //TODO
return;
}
LayoutNode * juxtapositionLayout = nullptr;
if (cursor->position() == LayoutCursor::Position::Left) {
juxtapositionLayout = replaceWithJuxtapositionOf(sibling, this); //TODO
} else {
assert(cursor->position() == LayoutCursor::Position::Right);
juxtapositionLayout = replaceWithJuxtapositionOf(this, sibling);
}
if (moveCursor) {
cursor->setLayoutNode(juxtapositionLayout);
cursor->setPosition(LayoutCursor::Position::Right);
}
}
}

View File

@@ -81,7 +81,7 @@ TreeNode * TreeNode::parentTree() const {
#endif
}
TreeNode * TreeNode::editableRootTree() {
TreeNode * TreeNode::rootTree() {
for (TreeNode * root : TreePool::sharedPool()->roots()) {
if (hasAncestor(root, true)) {
return root;