[poincare] FractionLayoutNode

This commit is contained in:
Léa Saviot
2018-07-20 10:50:53 +02:00
parent dac017e3c9
commit cddbdaa71d
10 changed files with 342 additions and 16 deletions

View File

@@ -14,6 +14,7 @@ objs += $(addprefix poincare/src/,\
condensed_sum_layout_node.o\
conjugate_layout_node.o\
empty_layout_node.o\
fraction_layout_node.o\
horizontal_layout_node.o\
integral_layout_node.o\
layout_cursor.o\

View File

@@ -42,6 +42,7 @@
#include <poincare/floor.h>
#include <poincare/floor_layout_node.h>
#include <poincare/frac_part.h>
#include <poincare/fraction_layout_node.h>
#include <poincare/global_context.h>
#include <poincare/great_common_divisor.h>
#include <poincare/horizontal_layout_node.h>

View File

@@ -0,0 +1,69 @@
#ifndef POINCARE_FRACTION_LAYOUT_NODE_H
#define POINCARE_FRACTION_LAYOUT_NODE_H
#include <poincare/layout_cursor.h>
#include <poincare/layout_node.h>
#include <poincare/layout_reference.h>
namespace Poincare {
class FractionLayoutNode : public LayoutNode {
public:
using LayoutNode::LayoutNode;
// LayoutNode
void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override;
void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override;
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;
int writeTextInBuffer(char * buffer, int bufferSize, PrintFloat::Mode floatDisplayMode, int numberOfSignificantDigits) const override;
bool shouldCollapseSiblingsOnLeft() const override { return true; }
bool shouldCollapseSiblingsOnRight() const override { return true; }
int leftCollapsingAbsorbingChildIndex() const override { return 0; }
int rightCollapsingAbsorbingChildIndex() const override { return 1; }
void didCollapseSiblings(LayoutCursor * cursor) override;
LayoutNode * layoutToPointWhenInserting() 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
* an omitted multiplication layout factor. We have the same problem with
* 2^3 1/2 being serialized as 2^3**1/2, so must override the Right version
* and not canBeOmittedMultiplicationLeftFactor. */
// TreeNode
size_t size() const override { return sizeof(FractionLayoutNode); }
int numberOfChildren() const override { return 2; }
#if TREE_LOG
const char * description() const override {
return "FractionLayout";
}
#endif
protected:
// LayoutNode
void computeSize() override;
void computeBaseline() override;
KDPoint positionOfChild(LayoutNode * child) override;
private:
constexpr static KDCoordinate k_fractionLineMargin = 2;
constexpr static KDCoordinate k_fractionLineHeight = 1;
void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override;
LayoutNode * numeratorLayout() { return childAtIndex(0); }
LayoutNode * denominatorLayout() { return childAtIndex(1); }
};
class FractionLayoutRef : public LayoutReference<FractionLayoutNode> {
public:
FractionLayoutRef(LayoutRef numerator, LayoutRef denominator) :
LayoutReference<FractionLayoutNode>()
{
addChildTreeAtIndex(numerator, 0);
addChildTreeAtIndex(denominator, 1);
}
FractionLayoutRef(TreeNode * t) : LayoutReference<FractionLayoutNode>(t) {}
};
}
#endif

View File

@@ -113,7 +113,7 @@ public:
void addEmptySquareRootLayout();
void addEmptySquarePowerLayout();
void addEmptyTenPowerLayout();
void addFractionLayoutAndCollapseSiblings() {} //TODO
void addFractionLayoutAndCollapseSiblings();
void addXNTCharLayout();
void insertText(const char * text);
void addLayoutAndMoveCursor(LayoutRef l);

View File

@@ -91,7 +91,7 @@ public:
assert(p.isDefined());
p.replaceChild(*this, newChild, cursor);
}
void replaceWithJuxtapositionOf(LayoutReference<LayoutNode> leftChild, LayoutReference<LayoutNode> rightChild, LayoutCursor * cursor);
void replaceWithJuxtapositionOf(LayoutReference<LayoutNode> leftChild, LayoutReference<LayoutNode> rightChild, LayoutCursor * cursor, bool putCursorInTheMiddle = false);
// Remove
void removeChild(LayoutReference<LayoutNode> l, LayoutCursor * cursor, bool force = false);
void removeChildAtIndex(int index, LayoutCursor * cursor, bool force = false) {

View File

@@ -0,0 +1,219 @@
#include <poincare/fraction_layout_node.h>
#include <poincare/empty_layout_node.h>
#include <poincare/horizontal_layout_node.h>
#include <poincare/layout_engine.h>
#include <ion/charset.h>
//#include <string.h>
#include <escher/metric.h>
#include <ion/charset.h>
#include <assert.h>
namespace Poincare {
void FractionLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) {
if (cursor->position() == LayoutCursor::Position::Left
&& ((numeratorLayout() && cursor->layoutNode() == numeratorLayout())
|| (denominatorLayout() && cursor->layoutNode() == denominatorLayout())))
{
// Case: Left of the numerator or the denominator. Go Left of the fraction.
cursor->setLayoutNode(this);
return;
}
assert(cursor->layoutNode() == this);
// Case: Right. Go to the denominator.
if (cursor->position() == LayoutCursor::Position::Right) {
assert(denominatorLayout() != nullptr);
cursor->setLayoutNode(denominatorLayout());
cursor->setPosition(LayoutCursor::Position::Right);
return;
}
// Case: Left. Ask the parent.
assert(cursor->position() == LayoutCursor::Position::Left);
LayoutNode * parentNode = parent();
if (parentNode != nullptr) {
parentNode->moveCursorLeft(cursor, shouldRecomputeLayout);
}
}
void FractionLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) {
if (cursor->position() == LayoutCursor::Position::Right
&& ((numeratorLayout() && cursor->layoutNode() == numeratorLayout())
|| (denominatorLayout() && cursor->layoutNode() == denominatorLayout())))
{
// Case: Right of the numerator or the denominator. Go Right of the fraction.
cursor->setLayoutNode(this);
return;
}
assert(cursor->layoutNode() == this);
if (cursor->position() == LayoutCursor::Position::Left) {
// Case: Left. Go to the numerator.
assert(numeratorLayout() != nullptr);
cursor->setLayoutNode(numeratorLayout());
return;
}
// Case: Right. Ask the parent.
assert(cursor->position() == LayoutCursor::Position::Right);
LayoutNode * parentNode = parent();
if (parentNode) {
parentNode->moveCursorRight(cursor, shouldRecomputeLayout);
}
}
void FractionLayoutNode::moveCursorUp(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited) {
if (denominatorLayout() && cursor->layoutNode()->hasAncestor(denominatorLayout(), true)) {
// If the cursor is inside denominator, move it to the numerator.
assert(numeratorLayout() != nullptr);
numeratorLayout()->moveCursorUpInDescendants(cursor, shouldRecomputeLayout);
return;
}
if (cursor->layoutNode() == this){
// If the cursor is Left or Right, move it to the numerator.
assert(numeratorLayout() != nullptr);
cursor->setLayoutNode(numeratorLayout());
return;
}
LayoutNode::moveCursorUp(cursor, shouldRecomputeLayout, equivalentPositionVisited);
}
void FractionLayoutNode::moveCursorDown(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited) {
if (numeratorLayout() && cursor->layoutNode()->hasAncestor(numeratorLayout(), true)) {
// If the cursor is inside numerator, move it to the denominator.
assert(denominatorLayout() != nullptr);
denominatorLayout()->moveCursorDownInDescendants(cursor, shouldRecomputeLayout);
return;
}
if (cursor->layoutNode() == this){
// If the cursor is Left or Right, move it to the denominator.
assert(denominatorLayout() != nullptr);
cursor->setLayoutNode(denominatorLayout());
return;
}
LayoutNode::moveCursorDown(cursor, shouldRecomputeLayout, equivalentPositionVisited);
}
void FractionLayoutNode::deleteBeforeCursor(LayoutCursor * cursor) {
if (cursor->layoutNode() == denominatorLayout()) {
/* Case: Left of the denominator. Replace the fraction with a horizontal
* juxtaposition of the numerator and the denominator. */
assert(cursor->position() == LayoutCursor::Position::Left);
if (numeratorLayout()->isEmpty() && denominatorLayout()->isEmpty()) {
/* Case: Numerator and denominator are empty. Move the cursor and replace
* the fraction with an empty layout. */
FractionLayoutRef(this).replaceWith(EmptyLayoutRef(), cursor);
// WARNING: Do no use "this" afterwards
return;
}
/* Else, replace the fraction with a juxtaposition of the numerator and
* denominator. Place the cursor in the middle of the juxtaposition, which
* is right of the numerator. */
LayoutRef(this).replaceWithJuxtapositionOf(numeratorLayout(), denominatorLayout(), cursor, true);
// WARNING: Do no use "this" afterwards
return;
}
if (cursor->layoutNode() == this && cursor->position() == LayoutCursor::Position::Right) {
// Case: Right. Move Right of the denominator.
cursor->setLayoutNode(denominatorLayout());
return;
}
LayoutNode::deleteBeforeCursor(cursor);
}
int FractionLayoutNode::writeTextInBuffer(char * buffer, int bufferSize, PrintFloat::Mode floatDisplayMode, int numberOfSignificantDigits) const {
if (bufferSize == 0) {
return -1;
}
buffer[bufferSize-1] = 0;
int numberOfChar = 0;
if (numberOfChar >= bufferSize-1) { return bufferSize-1;}
int idxInParent = -1;
LayoutNode * p = parent();
if (p != nullptr) {
idxInParent = p->indexOfChild(this);
}
// Add a multiplication if omitted.
if (idxInParent > 0 && p->isHorizontal() && p->childAtIndex(idxInParent - 1)->canBeOmittedMultiplicationLeftFactor()) {
buffer[numberOfChar++] = Ion::Charset::MiddleDot;
if (numberOfChar >= bufferSize-1) { return bufferSize-1;}
}
bool addParenthesis = false;
if (idxInParent >= 0 && idxInParent < (p->numberOfChildren() - 1) && p->isHorizontal() && p->childAtIndex(idxInParent + 1)->isVerticalOffset()) {
addParenthesis = true;
// Add parenthesis
buffer[numberOfChar++] = '(';
if (numberOfChar >= bufferSize-1) { return bufferSize-1;}
}
// Write the content of the fraction
numberOfChar += LayoutEngine::writeInfixSerializableRefTextInBuffer(SerializableRef(const_cast<FractionLayoutNode *>(this)), buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits, "/");
if (numberOfChar >= bufferSize-1) { return bufferSize-1; }
if (addParenthesis) {
// Add parenthesis
buffer[numberOfChar++] = ')';
if (numberOfChar >= bufferSize-1) { return bufferSize-1;}
}
// Add a multiplication if omitted.
if (idxInParent >= 0 && idxInParent < (p->numberOfChildren() - 1) && p->isHorizontal() && p->childAtIndex(idxInParent + 1)->canBeOmittedMultiplicationRightFactor()) {
buffer[numberOfChar++] = Ion::Charset::MiddleDot;
if (numberOfChar >= bufferSize-1) { return bufferSize-1;}
}
buffer[numberOfChar] = 0;
return numberOfChar;
}
LayoutNode * FractionLayoutNode::layoutToPointWhenInserting() {
if (numeratorLayout()->isEmpty()){
return numeratorLayout();
}
if (denominatorLayout()->isEmpty()){
return denominatorLayout();
}
return this;
}
void FractionLayoutNode::didCollapseSiblings(LayoutCursor * cursor) {
cursor->setLayoutNode(denominatorLayout());
cursor->setPosition(LayoutCursor::Position::Left);
}
void FractionLayoutNode::computeSize() {
KDCoordinate width = max(numeratorLayout()->layoutSize().width(), denominatorLayout()->layoutSize().width())
+ 2*Metric::FractionAndConjugateHorizontalOverflow+2*Metric::FractionAndConjugateHorizontalMargin;
KDCoordinate height = numeratorLayout()->layoutSize().height()
+ k_fractionLineMargin + k_fractionLineHeight + k_fractionLineMargin
+ denominatorLayout()->layoutSize().height();
m_frame.setSize(KDSize(width, height));
m_sized = true;
}
void FractionLayoutNode::computeBaseline() {
m_baseline = numeratorLayout()->layoutSize().height() + k_fractionLineMargin + k_fractionLineHeight;
m_baselined = true;
}
KDPoint FractionLayoutNode::positionOfChild(LayoutNode * child) {
KDCoordinate x = 0;
KDCoordinate y = 0;
if (child == numeratorLayout()) {
x = (KDCoordinate)((layoutSize().width() - numeratorLayout()->layoutSize().width())/2);
} else if (child == denominatorLayout()) {
x = (KDCoordinate)((layoutSize().width() - denominatorLayout()->layoutSize().width())/2);
y = (KDCoordinate)(numeratorLayout()->layoutSize().height() + 2*k_fractionLineMargin + k_fractionLineHeight);
} else {
assert(false); // Should not happen
}
return KDPoint(x, y);
}
void FractionLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) {
KDCoordinate fractionLineY = p.y() + numeratorLayout()->layoutSize().height() + k_fractionLineMargin;
ctx->fillRect(KDRect(p.x()+Metric::FractionAndConjugateHorizontalMargin, fractionLineY, layoutSize().width()-2*Metric::FractionAndConjugateHorizontalMargin, k_fractionLineHeight), expressionColor);
}
}

View File

@@ -346,6 +346,9 @@ bool HorizontalLayoutNode::willReplaceChild(LayoutNode * oldChild, LayoutNode *
// HorizontalLayoutRef
void HorizontalLayoutRef::addOrMergeChildAtIndex(LayoutRef l, int index, bool removeEmptyChildren, LayoutCursor * cursor) {
if (l.isEmpty() && removeEmptyChildren) {
return;
}
if (l.isHorizontal()) {
mergeChildrenAtIndex(HorizontalLayoutRef(l.node()), index, removeEmptyChildren, cursor);
} else {

View File

@@ -3,6 +3,7 @@
#include <poincare/layout_reference.h>
#include <poincare/char_layout_node.h>
#include <poincare/empty_layout_node.h>
#include <poincare/fraction_layout_node.h>
#include <poincare/horizontal_layout_node.h>
#include <poincare/nth_root_layout_node.h>
#include <poincare/vertical_offset_layout_node.h>
@@ -115,6 +116,14 @@ void LayoutCursor::addEmptyTenPowerLayout() {
}
}
void LayoutCursor::addFractionLayoutAndCollapseSiblings() {
HorizontalLayoutRef child1 = HorizontalLayoutRef(EmptyLayoutRef());
HorizontalLayoutRef child2 = HorizontalLayoutRef(EmptyLayoutRef());
FractionLayoutRef newChild = FractionLayoutRef(child1, child2);
m_layoutRef.addSibling(this, newChild, true);
LayoutRef(newChild.node()).collapseSiblings(this);
}
void LayoutCursor::addXNTCharLayout() {
m_layoutRef.addSibling(this, CharLayoutRef(m_layoutRef.XNTChar()), true);
}

View File

@@ -42,16 +42,43 @@ void LayoutRef::replaceChildWithEmpty(LayoutRef oldChild, LayoutCursor * cursor)
}
template<>
void LayoutRef::replaceWithJuxtapositionOf(LayoutRef leftChild, LayoutRef rightChild, LayoutCursor * cursor) {
void LayoutRef::replaceWithJuxtapositionOf(LayoutRef leftChild, LayoutRef rightChild, LayoutCursor * cursor, bool putCursorInTheMiddle) {
LayoutReference<LayoutNode> p = parent();
assert(p.isDefined());
assert(!p.isHorizontal());
/* One of the children to juxtapose might be "this", so we cannot just call
* replaceWith. */
HorizontalLayoutRef horizontalLayoutR;
p.replaceChild(*this, horizontalLayoutR, cursor);
horizontalLayoutR.addOrMergeChildAtIndex(leftChild, 0, false);
horizontalLayoutR.addOrMergeChildAtIndex(rightChild, 1, false);
if (!p.isHorizontal()) {
/* One of the children to juxtapose might be "this", so we cannot just call
* replaceWith. */
HorizontalLayoutRef horizontalLayoutR;
p.replaceChild(*this, horizontalLayoutR, cursor);
horizontalLayoutR.addOrMergeChildAtIndex(leftChild, 0, false);
if (putCursorInTheMiddle) {
if (!horizontalLayoutR.isEmpty()) {
cursor->setLayoutReference(horizontalLayoutR.childAtIndex(horizontalLayoutR.numberOfChildren()-1));
cursor->setPosition(LayoutCursor::Position::Right);
} else {
cursor->setLayoutReference(horizontalLayoutR);
cursor->setPosition(LayoutCursor::Position::Left);
}
}
horizontalLayoutR.addOrMergeChildAtIndex(rightChild, 1, false);
return;
}
/* The parent is an Horizontal layout, so directly add the two juxtaposition
* children to the parent. */
int idxInParent = p.indexOfChild(*this);
HorizontalLayoutRef castedParent = HorizontalLayoutRef(p.node());
if (putCursorInTheMiddle) {
if (idxInParent > 0) {
cursor->setLayoutReference(castedParent.childAtIndex(idxInParent-1));
cursor->setPosition(LayoutCursor::Position::Right);
} else {
cursor->setLayoutReference(castedParent);
cursor->setPosition(LayoutCursor::Position::Left);
}
}
castedParent.addOrMergeChildAtIndex(rightChild, idxInParent, true);
castedParent.addOrMergeChildAtIndex(leftChild, idxInParent, true, putCursorInTheMiddle ? cursor : nullptr);
p.removeChild(*this, cursor->layoutReference() == *this ? cursor : nullptr);
}
template <typename T>

View File

@@ -7,8 +7,7 @@ extern "C" {
}
#include <poincare/arithmetic.h>
#include <poincare/opposite.h>
#include "layout/fraction_layout.h"
#include <poincare/char_layout_node.h>
#include <poincare/fraction_layout_node.h>
namespace Poincare {
@@ -157,10 +156,8 @@ LayoutRef Rational::createLayout(PrintFloat::Mode floatDisplayMode, int numberOf
if (m_denominator.isOne()) {
return numeratorLayout;
}
return CharLayoutRef('a'); //TODO
/*
ExpressionLayout * denominatorLayout = m_denominator.createLayout();
return new FractionLayout(numeratorLayout, denominatorLayout, false);*/
LayoutRef denominatorLayout = m_denominator.createLayout();
return FractionLayoutRef(numeratorLayout, denominatorLayout);
}
int Rational::writeTextInBuffer(char * buffer, int bufferSize, PrintFloat::Mode floatDisplayMode, int numberOfSignificantDigits) const {