diff --git a/poincare/Makefile b/poincare/Makefile index 765884a5f..66abfc415 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -3,6 +3,14 @@ SFLAGS += -Ipoincare/include #include poincare/src/simplify/Makefile #include poincare/src/simplification/Makefile +objs += $(addprefix poincare/src/,\ + char_layout_node.o\ + horizontal_layout_node.o\ + layout_cursor.o\ + layout_node.o\ + layout_reference.o\ +) + objs += $(addprefix poincare/src/,\ absolute_value.o\ addition.o\ @@ -47,10 +55,7 @@ objs += $(addprefix poincare/src/,\ imaginary_part.o\ integer.o\ integral.o\ - layout_cursor.o\ layout_engine.o\ - layout_node.o\ - layout_reference.o\ list_data.o\ least_common_multiple.o\ logarithm.o\ diff --git a/poincare/include/poincare/allocation_failed_layout_node.h b/poincare/include/poincare/allocation_failed_layout_node.h index 64f78adf0..37c4fe2f7 100644 --- a/poincare/include/poincare/allocation_failed_layout_node.h +++ b/poincare/include/poincare/allocation_failed_layout_node.h @@ -8,11 +8,28 @@ namespace Poincare { class AllocationFailedLayoutNode : public LayoutNode { public: + // LayoutNode + int writeTextInBuffer(char * buffer, int bufferSize, int numberOfSignificantDigits = PrintFloat::k_numberOfStoredSignificantDigits) const override { + assert(false); + return 0; + } // TreeNode size_t size() const override { return sizeof(AllocationFailedLayoutNode); } const char * description() const override { return "Allocation Failed"; } int numberOfChildren() const override { return 0; } bool isAllocationFailure() const override { return true; } + +protected: + // LayoutNode + void computeSize() override { m_sized = true; } + void computeBaseline() override { m_baselined = true; } + KDPoint positionOfChild(LayoutNode * child) override { + assert(false); + return KDPointZero; + } + +private: + void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override {} }; class AllocationFailedLayoutRef : public LayoutReference { diff --git a/poincare/include/poincare/char_layout_node.h b/poincare/include/poincare/char_layout_node.h index c2eee9789..c45796efb 100644 --- a/poincare/include/poincare/char_layout_node.h +++ b/poincare/include/poincare/char_layout_node.h @@ -1,59 +1,64 @@ #ifndef POINCARE_CHAR_LAYOUT_NODE_H #define POINCARE_CHAR_LAYOUT_NODE_H -#include -#include #include +#include +#include +#include namespace Poincare { class CharLayoutNode : public LayoutNode { public: - CharLayoutNode() : LayoutNode() {} - size_t size() const override { - return sizeof(CharLayoutNode); - } + CharLayoutNode() : + LayoutNode(), + m_char('a'), + m_fontSize(KDText::FontSize::Large) + {} + void setChar(char c) { m_char = c; } + void setFontSize(KDText::FontSize fontSize) { m_fontSize = fontSize; } + // LayoutNode + int writeTextInBuffer(char * buffer, int bufferSize, int numberOfSignificantDigits = PrintFloat::k_numberOfStoredSignificantDigits) const override { + return LayoutEngine::writeOneCharInBuffer(buffer, bufferSize, m_char); + } + void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; + void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; + + // TreeNode + size_t size() const override { return sizeof(CharLayoutNode); } int numberOfChildren() const override { return 0; } - - void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override { - if (cursor->position() == LayoutCursor::Position::Right) { - cursor->setPosition(LayoutCursor::Position::Left); - return; - } - LayoutNode * parentNode = parent(); - if (parentNode != nullptr) { - parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); - } - } - - void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override { - if (cursor->position() == LayoutCursor::Position::Left) { - cursor->setPosition(LayoutCursor::Position::Right); - return; - } - LayoutNode * parentNode = parent(); - if (parentNode != nullptr) { - parentNode->moveCursorRight(cursor, shouldRecomputeLayout); - } - } - +#if TREE_LOG const char * description() const override { static char Description[] = "Char a"; Description[5] = m_char; return Description; } +#endif + +protected: + // LayoutNode + void computeSize() override; + void computeBaseline() override; + KDPoint positionOfChild(LayoutNode * child) override { + assert(false); + return KDPointZero; + } - void setChar(char c) { m_char = c; } private: + void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; char m_char; + KDText::FontSize m_fontSize; }; class CharLayoutRef : public LayoutReference { public: - CharLayoutRef(char c) : LayoutReference() { + CharLayoutRef(char c, KDText::FontSize fontSize = KDText::FontSize::Large) : + LayoutReference() + { if (!(this->node()->isAllocationFailure())) { this->typedNode()->setChar(c); + this->typedNode()->setFontSize(fontSize); } } }; diff --git a/poincare/include/poincare/horizontal_layout_node.h b/poincare/include/poincare/horizontal_layout_node.h index 4f4647b7c..750db81c3 100644 --- a/poincare/include/poincare/horizontal_layout_node.h +++ b/poincare/include/poincare/horizontal_layout_node.h @@ -7,6 +7,9 @@ namespace Poincare { +/* WARNING: A HorizontalLayout should never have a HorizontalLayout child. For + * instance, use addOrMergeChildAtIndex to add a LayoutNode safely. */ + class HorizontalLayoutNode : public LayoutNode { public: HorizontalLayoutNode() : @@ -14,72 +17,34 @@ public: m_numberOfChildren(0) {} - size_t size() const override { - return sizeof(HorizontalLayoutNode); - } + // LayoutNode + 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; + // TreeNode + size_t size() const override { return sizeof(HorizontalLayoutNode); } int numberOfChildren() const override { return m_numberOfChildren; } void incrementNumberOfChildren(int increment = 1) override { m_numberOfChildren+= increment; } void decrementNumberOfChildren(int decrement = 1) override { - assert(m_numberOfChildren > 0); + assert(m_numberOfChildren >= decrement); m_numberOfChildren-= decrement; } - void eraseNumberOfChildren() override { - m_numberOfChildren = 0; - } - - void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override { - if (this == cursor->layoutReference().node()) { - if (cursor->position() == LayoutCursor::Position::Left) { - // Case: Left. Ask the parent. - LayoutNode * parentNode = parent(); - if (parentNode != nullptr) { - parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); - } - return; - } - assert(cursor->position() == LayoutCursor::Position::Right); - /* Case: Right. Go to the last child if there is one, and move Left. Else - * go Left and ask the parent. */ - int childrenCount = numberOfChildren(); - if (childrenCount >= 1) { - cursor->setLayoutNode(static_cast(childTreeAtIndex(childrenCount-1))); - } else { - cursor->setPosition(LayoutCursor::Position::Left); - } - return cursor->moveLeft(shouldRecomputeLayout); - } - - // Case: The cursor is Left of a child. - assert(cursor->position() == LayoutCursor::Position::Left); - int childIndex = indexOfChildByIdentifier(cursor->layoutIdentifier()); - assert(childIndex >= 0); - if (childIndex == 0) { - // Case: the child is the leftmost. Ask the parent. - if (parent()) { - cursor->setLayoutNode(this); - return cursor->moveLeft(shouldRecomputeLayout); - } - return; - } - // Case: the child is not the leftmost. Go to its left sibling and move Left. - cursor->setLayoutNode(static_cast(childTreeAtIndex(childIndex-1))); - cursor->setPosition(LayoutCursor::Position::Right); - cursor->moveLeft(shouldRecomputeLayout); - } - - void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override { - //TODO - LayoutNode * parentNode = parent(); - if (parentNode != nullptr) { - parentNode->moveCursorRight(cursor, shouldRecomputeLayout); - } - } - + void eraseNumberOfChildren() override { m_numberOfChildren = 0; } +#if TREE_LOG const char * description() const override { return "Horizontal Layout"; } +#endif + +protected: + // LayoutNode + void computeSize() override; + void computeBaseline() override; + KDPoint positionOfChild(LayoutNode * l) override; + private: + void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override {} int m_numberOfChildren; }; diff --git a/poincare/include/poincare/layout_cursor.h b/poincare/include/poincare/layout_cursor.h index ac4693af3..542f71849 100644 --- a/poincare/include/poincare/layout_cursor.h +++ b/poincare/include/poincare/layout_cursor.h @@ -49,7 +49,7 @@ public: bool isEquivalentTo(LayoutCursor cursor); /* Position */ - int middleLeftPoint(); + KDPoint middleLeftPoint(); /* Move */ void moveLeft(bool * shouldRecomputeLayout); diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index 49a8ab656..990a4c135 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -1,7 +1,9 @@ #ifndef POINCARE_LAYOUT_NODE_H #define POINCARE_LAYOUT_NODE_H -#include "tree_node.h" +#include +#include +#include namespace Poincare { @@ -9,6 +11,30 @@ class LayoutCursor; class LayoutNode : public TreeNode { public: + + // Constructor + LayoutNode() : + TreeNode(), + m_baseline(0), + m_frame(KDRectZero), + m_baselined(false), + m_positioned(false), + m_sized(false) + { + } + + // Rendering + void draw(KDContext * ctx, KDPoint p, KDColor expressionColor = KDColorBlack, KDColor backgroundColor = KDColorWhite); + KDPoint origin(); + KDPoint absoluteOrigin(); + KDSize layoutSize(); + KDCoordinate baseline(); + virtual void invalidAllSizesPositionsAndBaselines(); + + // Serialization + virtual int writeTextInBuffer(char * buffer, int bufferSize, int numberOfSignificantDigits = PrintFloat::k_numberOfStoredSignificantDigits) const = 0; + + // TreeNode static TreeNode * FailedAllocationStaticNode(); TreeNode * failedAllocationStaticNode() override { return FailedAllocationStaticNode(); } static int AllocationFailureNodeIdentifier() { @@ -18,15 +44,10 @@ public: return AllocationFailureNodeIdentifier(); } - /* Hierarchy */ + // Hierarchy LayoutNode * parent() const { return static_cast(parentTree()); } - /* Rendering */ - void draw(); - int origin(); - int absoluteOrigin(); - - /* Tree navigation */ + // Tree navigation virtual void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) {} virtual void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) {} virtual void moveCursorUp(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) {} @@ -36,6 +57,7 @@ public: LayoutNode * childAtIndex(int i) { return static_cast(childTreeAtIndex(i)); } protected: + // Iterators class Iterator { public: Iterator(LayoutNode * node) : m_node(node) {} @@ -62,8 +84,21 @@ protected: }; DirectChildren children() { return DirectChildren(this); } + // Sizing and positioning + virtual void computeSize() = 0; + virtual void computeBaseline() = 0; + virtual KDPoint positionOfChild(LayoutNode * child) = 0; + + /* m_baseline is the signed vertical distance from the top of the layout to + * the fraction bar of an hypothetical fraction sibling layout. If the top of + * the layout is under that bar, the baseline is negative. */ + KDCoordinate m_baseline; + KDRect m_frame; + bool m_baselined; + bool m_positioned; + bool m_sized; private: - virtual void render() {}; + virtual void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) = 0; }; } diff --git a/poincare/include/poincare/layout_reference.h b/poincare/include/poincare/layout_reference.h index e23a01723..a3da269a1 100644 --- a/poincare/include/poincare/layout_reference.h +++ b/poincare/include/poincare/layout_reference.h @@ -33,11 +33,11 @@ public: TreeReference::replaceChildAtIndex(oldChildIndex, newChild); } - int layoutOrigin() { + KDPoint layoutOrigin() { return this->typedNode()->layoutOrigin(); } - int absoluteOrigin() { + KDPoint absoluteOrigin() { return this->typedNode()->absoluteOrigin(); } }; diff --git a/poincare/src/char_layout_node.cpp b/poincare/src/char_layout_node.cpp new file mode 100644 index 000000000..5bcaec05d --- /dev/null +++ b/poincare/src/char_layout_node.cpp @@ -0,0 +1,45 @@ +#include + +namespace Poincare { + +void CharLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + if (cursor->position() == LayoutCursor::Position::Right) { + cursor->setPosition(LayoutCursor::Position::Left); + return; + } + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); + } +} + +void CharLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + if (cursor->position() == LayoutCursor::Position::Left) { + cursor->setPosition(LayoutCursor::Position::Right); + return; + } + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorRight(cursor, shouldRecomputeLayout); + } +} + +// Sizing and positioning +void CharLayoutNode::computeSize() { + assert(!m_sized); + m_frame.setSize(KDText::charSize(m_fontSize)); + m_sized = true; +} + +void CharLayoutNode::computeBaseline() { + assert(!m_baselined); + m_baseline = (KDText::charSize(m_fontSize).height()+1)/2; //TODO +1 ? + m_baselined = true; +} + +void CharLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { + char string[2] = {m_char, 0}; + ctx->drawString(string, p, m_fontSize, expressionColor, backgroundColor); +} + +} diff --git a/poincare/src/horizontal_layout_node.cpp b/poincare/src/horizontal_layout_node.cpp new file mode 100644 index 000000000..15d1503fe --- /dev/null +++ b/poincare/src/horizontal_layout_node.cpp @@ -0,0 +1,104 @@ +#include +#include + +namespace Poincare { + +static inline KDCoordinate max(KDCoordinate c1, KDCoordinate c2) { return c1 > c2 ? c1 : c2; } + +int HorizontalLayoutNode::writeTextInBuffer(char * buffer, int bufferSize, int numberOfSignificantDigits) const { + if (numberOfChildren() == 0) { + if (bufferSize == 0) { + return -1; + } + buffer[0] = 0; + return 0; + } + return LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer, bufferSize, numberOfSignificantDigits, ""); +} + +void HorizontalLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + if (this == cursor->layoutReference().node()) { + if (cursor->position() == LayoutCursor::Position::Left) { + // Case: Left. Ask the parent. + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); + } + return; + } + assert(cursor->position() == LayoutCursor::Position::Right); + /* Case: Right. Go to the last child if there is one, and move Left. Else + * go Left and ask the parent. */ + int childrenCount = numberOfChildren(); + if (childrenCount >= 1) { + cursor->setLayoutNode(static_cast(childTreeAtIndex(childrenCount-1))); + } else { + cursor->setPosition(LayoutCursor::Position::Left); + } + return cursor->moveLeft(shouldRecomputeLayout); + } + + // Case: The cursor is Left of a child. + assert(cursor->position() == LayoutCursor::Position::Left); + int childIndex = indexOfChildByIdentifier(cursor->layoutIdentifier()); + assert(childIndex >= 0); + if (childIndex == 0) { + // Case: the child is the leftmost. Ask the parent. + if (parent()) { + cursor->setLayoutNode(this); + return cursor->moveLeft(shouldRecomputeLayout); + } + return; + } + // Case: the child is not the leftmost. Go to its left sibling and move Left. + cursor->setLayoutNode(static_cast(childTreeAtIndex(childIndex-1))); + cursor->setPosition(LayoutCursor::Position::Right); + cursor->moveLeft(shouldRecomputeLayout); +} + +void HorizontalLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + //TODO + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorRight(cursor, shouldRecomputeLayout); + } +} + +KDSize HorizontalLayoutNode::computeSize() { + assert(!m_sized); + KDCoordinate totalWidth = 0; + KDCoordinate maxUnderBaseline = 0; + KDCoordinate maxAboveBaseline = 0; + for (LayoutNode * l : directChildren()) { + KDSize childSize = l->size(); + totalWidth += childSize.width(); + maxUnderBaseline = max(maxUnderBaseline, childSize.height() - l->baseline()); + maxAboveBaseline = max(maxAboveBaseline, l->baseline()); + } + m_frame.setSize(KDSize(totalWidth, maxUnderBaseline + maxAboveBaseline)); + m_sized = true; +} + +void HorizontalLayoutNode::computeBaseline() { + assert(!m_baselined); + m_baseline = 0; + for (LayoutNode * l : directChildren()) { + m_baseline = max(m_baseline, l->baseline()); + } + m_baselined = true; +} + +KDPoint HorizontalLayoutNode::positionOfChild(LayoutNode * l) { + assert(hasChild(l)); + KDCoordinate x = 0; + int index = indexOfChild(l); + assert(index > -1); + if (index > 0) { + LayoutNode * previousChild = child(index-1); + x = previousChild->origin().x() + previousChild->size().width(); + } + KDCoordinate y = baseline() - l->baseline(); + return KDPoint(x, y); +} + +} diff --git a/poincare/src/layout_cursor.cpp b/poincare/src/layout_cursor.cpp index 8fb4781b4..544ca6a09 100644 --- a/poincare/src/layout_cursor.cpp +++ b/poincare/src/layout_cursor.cpp @@ -14,8 +14,8 @@ bool LayoutCursor::isEquivalentTo(LayoutCursor cursor) { /* Position */ -int LayoutCursor::middleLeftPoint() { - int layoutOrigin = layoutReference().absoluteOrigin(); +KDPoint LayoutCursor::middleLeftPoint() { + KDPoint layoutOrigin = layoutReference().absoluteOrigin(); return layoutOrigin; } diff --git a/poincare/src/layout_node.cpp b/poincare/src/layout_node.cpp index 21241de3e..ff3aace35 100644 --- a/poincare/src/layout_node.cpp +++ b/poincare/src/layout_node.cpp @@ -4,39 +4,65 @@ namespace Poincare { -TreeNode * LayoutNode::FailedAllocationStaticNode() { - return LayoutRef::FailedAllocationStaticNode(); -} +// Rendering -void LayoutNode::draw() { - for (LayoutNode * child : children()) { - child->draw(); +void LayoutNode::draw(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { + for (LayoutNode * l : children()) { + l->draw(ctx, p, expressionColor, backgroundColor); } - render(); + render(ctx, absoluteOrigin().translatedBy(p), expressionColor, backgroundColor); } -int LayoutNode::origin() { - LayoutNode * parentLayout = parent(); - if (parentLayout == nullptr) { +KDPoint LayoutNode::origin() { + LayoutNode * p = parent(); + if (p != nullptr) { return absoluteOrigin(); } else { - return 1; //KDPoint(absoluteOrigin().x() - parentLayout->absoluteOrigin().x(), absoluteOrigin().y() - parentLayout->absoluteOrigin().y()); + return KDPoint(absoluteOrigin().x() - p->absoluteOrigin().x(), + absoluteOrigin().y() - p->absoluteOrigin().y()); } - return 0; } -int LayoutNode::absoluteOrigin() { - /*if (!m_positioned) { - LayoutNode * parentLayout = parent(); - if (parentLayout != nullptr) { - m_frame.setOrigin(parentLayout->absoluteOrigin().translatedBy(parentLayout->positionOfChild(this))); +KDPoint LayoutNode::absoluteOrigin() { + LayoutNode * p = parent(); + if (!m_positioned) { + if (p != nullptr) { + m_frame.setOrigin(p->absoluteOrigin().translatedBy(p->positionOfChild(this))); } else { m_frame.setOrigin(KDPointZero); } m_positioned = true; } - return m_frame.origin();*/ - return 1; + return m_frame.origin(); +} + +KDSize LayoutNode::layoutSize() { + if (!m_sized) { + computeSize(); + } + return m_frame.size(); +} + +KDCoordinate LayoutNode::baseline() { + if (!m_baselined) { + computeBaseline(); + } + return m_baseline; +} + +void LayoutNode::invalidAllSizesPositionsAndBaselines() { + m_sized = false; + m_positioned = false; + m_baselined = false; + for (LayoutNode * l : children()) { + l->invalidAllSizesPositionsAndBaselines(); + } +} + +// TreeNode + +TreeNode * LayoutNode::FailedAllocationStaticNode() { + return LayoutRef::FailedAllocationStaticNode(); } }