From ccd25a360d6137a43f9fa9cdbf0c53d444b8ffb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 2 Jan 2018 16:11:06 +0100 Subject: [PATCH] [poincare] Serialize expression layout. We can now convert layouts into text: we will be able to parse them into an Expression. Change-Id: I82f4834c08f5b2c5fb294bdcb9a49257b574e20b --- apps/math_toolbox.cpp | 14 ++- poincare/include/poincare/expression_layout.h | 3 + poincare/include/poincare/layout_engine.h | 46 ++++++- poincare/src/layout/absolute_value_layout.h | 4 + .../src/layout/baseline_relative_layout.cpp | 22 ++++ .../src/layout/baseline_relative_layout.h | 1 + poincare/src/layout/bracket_layout.cpp | 22 ++++ poincare/src/layout/bracket_layout.h | 1 + poincare/src/layout/bracket_left_layout.h | 4 + poincare/src/layout/bracket_right_layout.h | 4 + poincare/src/layout/ceiling_layout.h | 4 + poincare/src/layout/char_layout.h | 4 + poincare/src/layout/condensed_sum_layout.h | 4 + poincare/src/layout/conjugate_layout.h | 4 + poincare/src/layout/empty_layout.cpp | 8 ++ poincare/src/layout/empty_layout.h | 2 + poincare/src/layout/floor_layout.h | 4 + poincare/src/layout/fraction_layout.cpp | 22 ++++ poincare/src/layout/fraction_layout.h | 2 + poincare/src/layout/grid_layout.cpp | 38 ++++++ poincare/src/layout/grid_layout.h | 3 + poincare/src/layout/horizontal_layout.h | 6 + poincare/src/layout/integral_layout.cpp | 53 ++++++++- poincare/src/layout/integral_layout.h | 9 ++ poincare/src/layout/nth_root_layout.h | 11 ++ poincare/src/layout/parenthesis_layout.h | 4 + poincare/src/layout/parenthesis_left_layout.h | 4 + .../src/layout/parenthesis_right_layout.h | 4 + poincare/src/layout/product_layout.cpp | 6 +- poincare/src/layout/product_layout.h | 2 + poincare/src/layout/sequence_layout.cpp | 61 ++++++++-- poincare/src/layout/sequence_layout.h | 4 +- poincare/src/layout/string_layout.cpp | 14 +++ poincare/src/layout/string_layout.h | 2 + poincare/src/layout/sum_layout.cpp | 6 +- poincare/src/layout/sum_layout.h | 2 + .../uneditable_horizontal_trio_layout.h | 5 + poincare/src/layout_engine.cpp | 112 +++++++++++++----- 38 files changed, 471 insertions(+), 50 deletions(-) diff --git a/apps/math_toolbox.cpp b/apps/math_toolbox.cpp index b9fbc6a54..a1b29bf5c 100644 --- a/apps/math_toolbox.cpp +++ b/apps/math_toolbox.cpp @@ -10,17 +10,19 @@ using namespace Poincare; * and the text which would be edited by clicking on the row. When the node is a * subtree, the edited text is set at I18n::Message::Default. */ -const int pointedLayoutPathIntegral[] = {2, 0}; -const int pointedLayoutPathSum[] = {2, 1}; +const int pointedLayoutPathIntegral[] = {0, 0}; +const int pointedLayoutPathSum[] = {0, 1}; const ToolboxMessageTree calculChildren[4] = { ToolboxMessageTree(I18n::Message::DiffCommandWithArg, I18n::Message::DerivateNumber, I18n::Message::DiffCommandWithArg, nullptr, 0), ToolboxMessageTree(I18n::Message::IntCommandWithArg, I18n::Message::Integral, I18n::Message::IntCommandWithArg, nullptr, 0, new IntegralLayout( - new EmptyVisibleLayout(), - new EmptyVisibleLayout(), - new HorizontalLayout(const_cast(Poincare::ExpressionLayout::ExpressionLayoutArray2( + new HorizontalLayout( + const_cast(Poincare::ExpressionLayout::ExpressionLayoutArray2( new EmptyVisibleLayout(), - new StringLayout("dx",2))), 2, false), false), + new StringLayout("dx",2))), 2, false), + new EmptyVisibleLayout(), + new EmptyVisibleLayout(), + false), const_cast(&pointedLayoutPathIntegral[0]), 2), ToolboxMessageTree(I18n::Message::SumCommandWithArg, I18n::Message::Sum, I18n::Message::SumCommandWithArg, nullptr, 0, diff --git a/poincare/include/poincare/expression_layout.h b/poincare/include/poincare/expression_layout.h index 5ec12341f..f366a305f 100644 --- a/poincare/include/poincare/expression_layout.h +++ b/poincare/include/poincare/expression_layout.h @@ -66,6 +66,9 @@ public: virtual bool moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout = nullptr, ExpressionLayout * previousPreviousLayout = nullptr); bool moveDownInside(ExpressionLayoutCursor * cursor); + /* Expression Engine */ + virtual int writeTextInBuffer(char * buffer, int bufferSize) const = 0; + /* Other */ virtual bool isHorizontal() const { return false; } virtual bool isLeftParenthesis() const { return false; } diff --git a/poincare/include/poincare/layout_engine.h b/poincare/include/poincare/layout_engine.h index cf1bc1e00..e1b14d0ef 100644 --- a/poincare/include/poincare/layout_engine.h +++ b/poincare/include/poincare/layout_engine.h @@ -6,13 +6,53 @@ namespace Poincare { class LayoutEngine { + public: + /* Expression to ExpressionLayout */ static ExpressionLayout * createInfixLayout(const Expression * expression, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, const char * operatorName); static ExpressionLayout * createPrefixLayout(const Expression * expression, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, const char * operatorName); - typedef bool (*OperandNeedParenthesis)(const Expression * e); - static int writeInfixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName, OperandNeedParenthesis operandNeedParenthesis = [](const Expression * e) { return e->type() == Expression::Type::Opposite; }); - static int writePrefixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName); + /* Expression to Text */ + typedef bool (*OperandNeedsParenthesis)(const Expression * e); + static int writeInfixExpressionTextInBuffer( + const Expression * expression, + char * buffer, + int bufferSize, + const char * operatorName, + OperandNeedsParenthesis operandNeedsParenthesis = [](const Expression * e) { return e->type() == Expression::Type::Opposite; }); + static int writePrefixExpressionTextInBuffer( + const Expression * expression, + char * buffer, + int bufferSize, + const char * operatorName); + + /* ExpressionLayout to Text */ + typedef bool (*ChildNeedsParenthesis)(const char * operatorName); + static int writeInfixExpressionLayoutTextInBuffer( + const ExpressionLayout * expressionLayout, + char * buffer, + int bufferSize, + const char * operatorName, + int firstChildIndex = 0, + int lastChildIndex = -1, + ChildNeedsParenthesis childNeedsParenthesis = [](const char * operatorName) { + return (operatorName[1] == 0 && (operatorName[0] == powerChar || operatorName[0] == divideChar)); }); //TODO + static int writePrefixExpressionLayoutTextInBuffer( + const ExpressionLayout * expressionLayout, + char * buffer, + int bufferSize, + const char * operatorName, + bool writeFirstChild = true); + + /* Write one char in buffer */ + static int writeOneCharInBuffer(char * buffer, int bufferSize, char charToWrite); + +private: + static constexpr char powerChar = '^'; + static constexpr char divideChar = '/'; + // These two functions return the index of the null-terminating char. + static int writeInfixExpressionOrExpressionLayoutTextInBuffer(const Expression * expression, const ExpressionLayout * expressionLayout, char * buffer, int bufferSize, const char * operatorName, int firstChildIndex, int lastChildIndex, OperandNeedsParenthesis operandNeedsParenthesis, ChildNeedsParenthesis childNeedsParenthesis); + static int writePrefixExpressionOrExpressionLayoutTextInBuffer(const Expression * expression, const ExpressionLayout * expressionLayout, char * buffer, int bufferSize, const char * operatorName, bool writeFirstChild = true); }; } diff --git a/poincare/src/layout/absolute_value_layout.h b/poincare/src/layout/absolute_value_layout.h index 55691659c..0be2b78a8 100644 --- a/poincare/src/layout/absolute_value_layout.h +++ b/poincare/src/layout/absolute_value_layout.h @@ -2,6 +2,7 @@ #define POINCARE_ABSOLUTE_VALUE_LAYOUT_H #include "bracket_layout.h" +#include namespace Poincare { @@ -9,6 +10,9 @@ class AbsoluteValueLayout : public BracketLayout { public: using BracketLayout::BracketLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "abs"); + } protected: KDCoordinate widthMargin() const override { return 2; } bool renderTopBar() const override { return false; } diff --git a/poincare/src/layout/baseline_relative_layout.cpp b/poincare/src/layout/baseline_relative_layout.cpp index 725898139..133d6c563 100644 --- a/poincare/src/layout/baseline_relative_layout.cpp +++ b/poincare/src/layout/baseline_relative_layout.cpp @@ -1,6 +1,7 @@ #include "baseline_relative_layout.h" #include "empty_visible_layout.h" #include +#include #include #include @@ -70,6 +71,27 @@ bool BaselineRelativeLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +int BaselineRelativeLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + if (m_type == Type::Subscript) { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + if (bufferSize == 1) { + return 0; + } + int numberOfChars = LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "_{"); + if (numberOfChars < bufferSize - 1) { + //FIXME what if the buffer is not big enough? + buffer[numberOfChars++] = '}'; + buffer[numberOfChars] = 0; + } + return numberOfChars; + } + assert(m_type == Type::Superscript); + return LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "^"); +} + ExpressionLayout * BaselineRelativeLayout::baseLayout() { return editableChild(0); } diff --git a/poincare/src/layout/baseline_relative_layout.h b/poincare/src/layout/baseline_relative_layout.h index f0cb1f7a0..184f808a6 100644 --- a/poincare/src/layout/baseline_relative_layout.h +++ b/poincare/src/layout/baseline_relative_layout.h @@ -16,6 +16,7 @@ public: void backspaceAtCursor(ExpressionLayoutCursor * cursor) override; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; protected: ExpressionLayout * baseLayout(); ExpressionLayout * indiceLayout(); diff --git a/poincare/src/layout/bracket_layout.cpp b/poincare/src/layout/bracket_layout.cpp index f3433e3f2..993ad2204 100644 --- a/poincare/src/layout/bracket_layout.cpp +++ b/poincare/src/layout/bracket_layout.cpp @@ -1,5 +1,6 @@ #include "bracket_layout.h" #include +#include extern "C" { #include #include @@ -90,6 +91,27 @@ bool BracketLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +int BracketLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + + // Write the opening bracket + int numberOfChar = 0; + buffer[numberOfChar++] = '['; + if (numberOfChar >= bufferSize-1) { return bufferSize-1;} + + // Write the argument + numberOfChar += const_cast(this)->operandLayout()->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the closing bracket + buffer[numberOfChar++] = ']'; + buffer[numberOfChar] = 0; + return numberOfChar; +} + ExpressionLayout * BracketLayout::operandLayout() { return editableChild(0); } diff --git a/poincare/src/layout/bracket_layout.h b/poincare/src/layout/bracket_layout.h index d11f51681..94b63398f 100644 --- a/poincare/src/layout/bracket_layout.h +++ b/poincare/src/layout/bracket_layout.h @@ -12,6 +12,7 @@ public: void backspaceAtCursor(ExpressionLayoutCursor * cursor) override; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; protected: ExpressionLayout * operandLayout(); KDCoordinate externWidthMargin() const { return 2; } diff --git a/poincare/src/layout/bracket_left_layout.h b/poincare/src/layout/bracket_left_layout.h index 1975b2fc0..35af9d32b 100644 --- a/poincare/src/layout/bracket_left_layout.h +++ b/poincare/src/layout/bracket_left_layout.h @@ -2,6 +2,7 @@ #define POINCARE_BRACKET_LEFT_LAYOUT_H #include +#include namespace Poincare { @@ -9,6 +10,9 @@ class BracketLeftLayout : public BracketLeftRightLayout { public: using BracketLeftRightLayout::BracketLeftRightLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeOneCharInBuffer(buffer, bufferSize, '['); + } bool isLeftBracket() const override { return true; } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; diff --git a/poincare/src/layout/bracket_right_layout.h b/poincare/src/layout/bracket_right_layout.h index 788664f3e..bc5af7548 100644 --- a/poincare/src/layout/bracket_right_layout.h +++ b/poincare/src/layout/bracket_right_layout.h @@ -2,6 +2,7 @@ #define POINCARE_BRACKET_RIGHT_LAYOUT_H #include +#include namespace Poincare { @@ -9,6 +10,9 @@ class BracketRightLayout : public BracketLeftRightLayout { public: using BracketLeftRightLayout::BracketLeftRightLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeOneCharInBuffer(buffer, bufferSize, ']'); + } bool isRightBracket() const override { return true; } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; diff --git a/poincare/src/layout/ceiling_layout.h b/poincare/src/layout/ceiling_layout.h index 1dd170093..b09c99f5a 100644 --- a/poincare/src/layout/ceiling_layout.h +++ b/poincare/src/layout/ceiling_layout.h @@ -2,6 +2,7 @@ #define POINCARE_CEILING_LAYOUT_H #include "bracket_layout.h" +#include namespace Poincare { @@ -9,6 +10,9 @@ class CeilingLayout : public BracketLayout { public: using BracketLayout::BracketLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "ceil"); + } protected: bool renderBottomBar() const override { return false; } }; diff --git a/poincare/src/layout/char_layout.h b/poincare/src/layout/char_layout.h index d00625c09..47ba4e336 100644 --- a/poincare/src/layout/char_layout.h +++ b/poincare/src/layout/char_layout.h @@ -2,6 +2,7 @@ #define POINCARE_CHAR_LAYOUT_H #include +#include #include namespace Poincare { @@ -10,6 +11,9 @@ class CharLayout : public StaticLayoutHierarchy<0> { public: CharLayout(char c, KDText::FontSize fontSize = KDText::FontSize::Large); ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeOneCharInBuffer(buffer, bufferSize, m_char); + } char character() { return m_char; } bool moveLeft(ExpressionLayoutCursor * cursor) override; diff --git a/poincare/src/layout/condensed_sum_layout.h b/poincare/src/layout/condensed_sum_layout.h index 20795398d..2647474cd 100644 --- a/poincare/src/layout/condensed_sum_layout.h +++ b/poincare/src/layout/condensed_sum_layout.h @@ -2,6 +2,7 @@ #define POINCARE_CONDENSED_SUM_LAYOUT_H #include +#include namespace Poincare { @@ -13,6 +14,9 @@ public: bool moveRight(ExpressionLayoutCursor * cursor) override; bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; bool moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "sum"); + } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/conjugate_layout.h b/poincare/src/layout/conjugate_layout.h index 366c824a5..203b1f1ca 100644 --- a/poincare/src/layout/conjugate_layout.h +++ b/poincare/src/layout/conjugate_layout.h @@ -2,6 +2,7 @@ #define POINCARE_CONJUGATE_LAYOUT_H #include +#include namespace Poincare { @@ -12,6 +13,9 @@ public: void backspaceAtCursor(ExpressionLayoutCursor * cursor) override; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "conj"); + } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/empty_layout.cpp b/poincare/src/layout/empty_layout.cpp index 88871bb7c..c7af45bf2 100644 --- a/poincare/src/layout/empty_layout.cpp +++ b/poincare/src/layout/empty_layout.cpp @@ -9,6 +9,14 @@ ExpressionLayout * EmptyLayout::clone() const { return layout; } +int EmptyLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[0] = 0; + return 0; +} + void EmptyLayout::addBrother(ExpressionLayoutCursor * cursor, ExpressionLayout * brother) { replaceWith(brother, true); } diff --git a/poincare/src/layout/empty_layout.h b/poincare/src/layout/empty_layout.h index 3688e4f21..5d82724f7 100644 --- a/poincare/src/layout/empty_layout.h +++ b/poincare/src/layout/empty_layout.h @@ -2,6 +2,7 @@ #define POINCARE_EMPTY_LAYOUT_H #include +#include #include namespace Poincare { @@ -13,6 +14,7 @@ public: void addBrother(ExpressionLayoutCursor * cursor, ExpressionLayout * brother) override; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; bool isEmpty() const override { return true; } protected: virtual void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override { return; } diff --git a/poincare/src/layout/floor_layout.h b/poincare/src/layout/floor_layout.h index 1bfb7e8f3..5e5a9785b 100644 --- a/poincare/src/layout/floor_layout.h +++ b/poincare/src/layout/floor_layout.h @@ -2,6 +2,7 @@ #define POINCARE_FLOOR_LAYOUT_H #include "bracket_layout.h" +#include namespace Poincare { @@ -9,6 +10,9 @@ class FloorLayout : public BracketLayout { public: using BracketLayout::BracketLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "floor"); + } protected: bool renderTopBar() const override { return false; } }; diff --git a/poincare/src/layout/fraction_layout.cpp b/poincare/src/layout/fraction_layout.cpp index 889b66994..71b9555aa 100644 --- a/poincare/src/layout/fraction_layout.cpp +++ b/poincare/src/layout/fraction_layout.cpp @@ -178,6 +178,28 @@ bool FractionLayout::moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout return ExpressionLayout::moveDown(cursor, previousLayout, previousPreviousLayout); } +int FractionLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int numberOfChar = 0; + if (numberOfChar >= bufferSize-1) { return bufferSize-1;} + + // Write the first enclosing parenthesis. + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { return bufferSize-1;} + + // Write the content of the fraction + numberOfChar += LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer+numberOfChar, bufferSize-numberOfChar, "/"); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the second enclosing parenthesis. + buffer[numberOfChar++] = ')'; + buffer[numberOfChar] = 0; + return numberOfChar; +} + void FractionLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { KDCoordinate fractionLineY = p.y() + numeratorLayout()->size().height() + k_fractionLineMargin; ctx->fillRect(KDRect(p.x()+Metric::FractionAndConjugateHorizontalMargin, fractionLineY, size().width()-2*Metric::FractionAndConjugateHorizontalMargin, 1), expressionColor); diff --git a/poincare/src/layout/fraction_layout.h b/poincare/src/layout/fraction_layout.h index 296b31ccc..7c551df14 100644 --- a/poincare/src/layout/fraction_layout.h +++ b/poincare/src/layout/fraction_layout.h @@ -2,6 +2,7 @@ #define POINCARE_FRACTION_LAYOUT_H #include +#include namespace Poincare { @@ -14,6 +15,7 @@ public: bool moveRight(ExpressionLayoutCursor * cursor) override; bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; bool moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/grid_layout.cpp b/poincare/src/layout/grid_layout.cpp index 2ad50f177..b73636fc0 100644 --- a/poincare/src/layout/grid_layout.cpp +++ b/poincare/src/layout/grid_layout.cpp @@ -1,6 +1,7 @@ #include "grid_layout.h" #include "empty_visible_layout.h" #include +#include extern "C" { #include #include @@ -137,6 +138,43 @@ bool GridLayout::moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * pr return ExpressionLayout::moveDown(cursor, previousLayout, previousPreviousLayout); } +int GridLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + const ExpressionLayout * editableParent = const_cast(this)->parent(); + assert(editableParent != nullptr); + + // If the grid is a binomial coefficient: + if (editableParent->child(0)->isLeftParenthesis()) { + return LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "binomial"); + } + + assert(editableParent->child(0)->isLeftBracket()); + // The grid is a matrix. + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int numberOfChar = 0; + if (numberOfChar >= bufferSize-1) { return bufferSize-1;} + + for (int i = 0; i < m_numberOfRows; i++) { + buffer[numberOfChar++] = '['; + if (numberOfChar >= bufferSize-1) { return bufferSize-1;} + + numberOfChar += LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer+numberOfChar, bufferSize-numberOfChar, ",", i*m_numberOfColumns, (i+1) * m_numberOfColumns - 1); + + buffer[numberOfChar++] = ']'; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + if (i < m_numberOfRows - 1) { + buffer[numberOfChar++] = ','; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + } + } + + buffer[numberOfChar] = 0; + return numberOfChar; +} + KDCoordinate GridLayout::rowBaseline(int i) { KDCoordinate rowBaseline = 0; for (int j = 0; j < m_numberOfColumns; j++) { diff --git a/poincare/src/layout/grid_layout.h b/poincare/src/layout/grid_layout.h index e2275511b..e5a6bde67 100644 --- a/poincare/src/layout/grid_layout.h +++ b/poincare/src/layout/grid_layout.h @@ -17,6 +17,9 @@ public: bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; bool moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; + /* Expression engine */ + int writeTextInBuffer(char * buffer, int bufferSize) const override; + protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/horizontal_layout.h b/poincare/src/layout/horizontal_layout.h index a3aef1244..c4aa2a027 100644 --- a/poincare/src/layout/horizontal_layout.h +++ b/poincare/src/layout/horizontal_layout.h @@ -2,6 +2,7 @@ #define POINCARE_HORIZONTAL_LAYOUT_H #include +#include namespace Poincare { @@ -21,6 +22,11 @@ public: bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; bool moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; + /* Expression Engine */ + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer, bufferSize, ""); + } + /* Other */ bool isHorizontal() const override { return true; } diff --git a/poincare/src/layout/integral_layout.cpp b/poincare/src/layout/integral_layout.cpp index a9ddb1298..f41c8bf2c 100644 --- a/poincare/src/layout/integral_layout.cpp +++ b/poincare/src/layout/integral_layout.cpp @@ -20,7 +20,7 @@ const uint8_t bottomSymbolPixel[IntegralLayout::k_symbolHeight][IntegralLayout:: }; ExpressionLayout * IntegralLayout::clone() const { - IntegralLayout * layout = new IntegralLayout(const_cast(this)->lowerBoundLayout(), const_cast(this)->upperBoundLayout(), const_cast(this)->integrandLayout(), true); + IntegralLayout * layout = new IntegralLayout(const_cast(this)->integrandLayout(), const_cast(this)->lowerBoundLayout(), const_cast(this)->upperBoundLayout(), true); return layout; } @@ -181,6 +181,53 @@ bool IntegralLayout::moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout return ExpressionLayout::moveDown(cursor, previousLayout, previousPreviousLayout); } +int IntegralLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + + // Write the operator name + int numberOfChar = strlcpy(buffer, "int", bufferSize); + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; + } + + // Write the opening parenthesis + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; + } + + // Write the argument without the "dx" + ExpressionLayout * intLayout = const_cast(this)->integrandLayout(); + numberOfChar += LayoutEngine::writeInfixExpressionLayoutTextInBuffer(intLayout, buffer+numberOfChar, bufferSize-numberOfChar, "", 0, intLayout->numberOfChildren()-2); + // TODO This works because the argument layout should always be an horizontal + // layout. + + // Write the comma + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + buffer[numberOfChar++] = ','; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the lower bound + numberOfChar += const_cast(this)->lowerBoundLayout()->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + + // Write the comma + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + buffer[numberOfChar++] = ','; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the upper bound + numberOfChar += const_cast(this)->upperBoundLayout()->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + + // Write the closing parenthesis + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + buffer[numberOfChar++] = ')'; + buffer[numberOfChar] = 0; + return numberOfChar; +} + void IntegralLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { KDSize integrandSize = integrandLayout()->size(); KDSize upperBoundSize = upperBoundLayout()->size(); @@ -232,7 +279,7 @@ KDPoint IntegralLayout::positionOfChild(ExpressionLayout * child) { } ExpressionLayout * IntegralLayout::upperBoundLayout() { - return editableChild(0); + return editableChild(2); } ExpressionLayout * IntegralLayout::lowerBoundLayout() { @@ -240,7 +287,7 @@ ExpressionLayout * IntegralLayout::lowerBoundLayout() { } ExpressionLayout * IntegralLayout::integrandLayout() { - return editableChild(2); + return editableChild(0); } } diff --git a/poincare/src/layout/integral_layout.h b/poincare/src/layout/integral_layout.h index 70d07eed4..35e9769df 100644 --- a/poincare/src/layout/integral_layout.h +++ b/poincare/src/layout/integral_layout.h @@ -2,6 +2,7 @@ #define POINCARE_INTEGRAL_LAYOUT_H #include +#include namespace Poincare { @@ -11,11 +12,19 @@ public: constexpr static KDCoordinate k_symbolWidth = 4; using StaticLayoutHierarchy::StaticLayoutHierarchy; ExpressionLayout * clone() const override; + + /* Dynamic Layout*/ void backspaceAtCursor(ExpressionLayoutCursor * cursor) override; + + /* Tree navigation */ bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; bool moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; + + /* Expression Engine */ + int writeTextInBuffer(char * buffer, int bufferSize) const override; + protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/nth_root_layout.h b/poincare/src/layout/nth_root_layout.h index d5cceb77f..c04f5c7f2 100644 --- a/poincare/src/layout/nth_root_layout.h +++ b/poincare/src/layout/nth_root_layout.h @@ -2,6 +2,7 @@ #define POINCARE_NTH_ROOT_LAYOUT_H #include +#include namespace Poincare { @@ -11,11 +12,21 @@ public: constexpr static KDCoordinate k_leftRadixWidth = 5; using StaticLayoutHierarchy::StaticLayoutHierarchy; ExpressionLayout * clone() const override; + + /* Dynamic Layout*/ void backspaceAtCursor(ExpressionLayoutCursor * cursor) override; + + /* Tree navigation */ bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; bool moveDown(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; + + /* Expression Engine */ + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "root"); + } + protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/parenthesis_layout.h b/poincare/src/layout/parenthesis_layout.h index 0757c6f5f..f7ed69efd 100644 --- a/poincare/src/layout/parenthesis_layout.h +++ b/poincare/src/layout/parenthesis_layout.h @@ -2,6 +2,7 @@ #define POINCARE_PARENTHESIS_LAYOUT_H #include +#include namespace Poincare { @@ -13,6 +14,9 @@ public: ExpressionLayout * clone() const override; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer, bufferSize, ""); + } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override { }; diff --git a/poincare/src/layout/parenthesis_left_layout.h b/poincare/src/layout/parenthesis_left_layout.h index 538d5c2de..782f9da01 100644 --- a/poincare/src/layout/parenthesis_left_layout.h +++ b/poincare/src/layout/parenthesis_left_layout.h @@ -2,6 +2,7 @@ #define POINCARE_PARENTHESIS_LEFT_LAYOUT_H #include +#include namespace Poincare { @@ -9,6 +10,9 @@ class ParenthesisLeftLayout : public ParenthesisLeftRightLayout { public: using ParenthesisLeftRightLayout::ParenthesisLeftRightLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeOneCharInBuffer(buffer, bufferSize, '('); + } bool isLeftParenthesis() const override { return true; } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; diff --git a/poincare/src/layout/parenthesis_right_layout.h b/poincare/src/layout/parenthesis_right_layout.h index c03d19efd..137494443 100644 --- a/poincare/src/layout/parenthesis_right_layout.h +++ b/poincare/src/layout/parenthesis_right_layout.h @@ -2,6 +2,7 @@ #define POINCARE_PARENTHESIS_RIGHT_LAYOUT_H #include +#include namespace Poincare { @@ -9,6 +10,9 @@ class ParenthesisRightLayout : public ParenthesisLeftRightLayout { public: using ParenthesisLeftRightLayout::ParenthesisLeftRightLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeOneCharInBuffer(buffer, bufferSize, ')'); + } bool isRightParenthesis() const override { return true; } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; diff --git a/poincare/src/layout/product_layout.cpp b/poincare/src/layout/product_layout.cpp index b344736dd..1be482165 100644 --- a/poincare/src/layout/product_layout.cpp +++ b/poincare/src/layout/product_layout.cpp @@ -5,10 +5,14 @@ namespace Poincare { ExpressionLayout * ProductLayout::clone() const { - ProductLayout * layout = new ProductLayout(const_cast(this)->lowerBoundLayout(), const_cast(this)->upperBoundLayout(), const_cast(this)->argumentLayout(), true); + ProductLayout * layout = new ProductLayout(const_cast(this)->argumentLayout(), const_cast(this)->lowerBoundLayout(), const_cast(this)->upperBoundLayout(), true); return layout; } +int ProductLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + return SequenceLayout::writeDerivedClassInBuffer("product", buffer, bufferSize); +} + void ProductLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { KDSize upperBoundSize = upperBoundLayout()->size(); KDSize lowerBoundSize = lowerBoundLayout()->size(); diff --git a/poincare/src/layout/product_layout.h b/poincare/src/layout/product_layout.h index ae68723be..72750ebbb 100644 --- a/poincare/src/layout/product_layout.h +++ b/poincare/src/layout/product_layout.h @@ -2,6 +2,7 @@ #define POINCARE_PRODUCT_LAYOUT_H #include "sequence_layout.h" +#include namespace Poincare { @@ -9,6 +10,7 @@ class ProductLayout : public SequenceLayout { public: using SequenceLayout::SequenceLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; private: diff --git a/poincare/src/layout/sequence_layout.cpp b/poincare/src/layout/sequence_layout.cpp index d6db66cb7..6b5e9cf85 100644 --- a/poincare/src/layout/sequence_layout.cpp +++ b/poincare/src/layout/sequence_layout.cpp @@ -14,7 +14,7 @@ SequenceLayout::SequenceLayout(ExpressionLayout * lowerBound, ExpressionLayout * ParenthesisLeftLayout * parLeft = new ParenthesisLeftLayout(); ParenthesisRightLayout * parRight = new ParenthesisRightLayout(); UneditableHorizontalTrioLayout * horLayout = new UneditableHorizontalTrioLayout(parLeft, argument, parRight, cloneOperands, false); - build(const_cast(Poincare::ExpressionLayout::ExpressionLayoutArray3(lowerBound, upperBound, horLayout)), 3, cloneOperands); + build(const_cast(Poincare::ExpressionLayout::ExpressionLayoutArray3(horLayout, lowerBound, upperBound)), 3, cloneOperands); } void SequenceLayout::backspaceAtCursor(ExpressionLayoutCursor * cursor) { @@ -71,7 +71,7 @@ bool SequenceLayout::moveLeft(ExpressionLayoutCursor * cursor) { // Go Right of the lower bound. if (cursor->position() == ExpressionLayoutCursor::Position::Left && argumentLayout() - && cursor->pointedExpressionLayout() == editableChild(2)) + && cursor->pointedExpressionLayout() == argumentWithParenthesesLayout()) { assert(lowerBoundLayout() != nullptr); cursor->setPointedExpressionLayout(lowerBoundLayout()->editableChild(1)); @@ -113,7 +113,7 @@ bool SequenceLayout::moveRight(ExpressionLayoutCursor * cursor) { // Ask the parent. if (cursor->position() == ExpressionLayoutCursor::Position::Right && argumentLayout() - && cursor->pointedExpressionLayout() == editableChild(2)) + && cursor->pointedExpressionLayout() == argumentWithParenthesesLayout()) { cursor->setPointedExpressionLayout(this); cursor->setPosition(ExpressionLayoutCursor::Position::Right); @@ -171,8 +171,51 @@ char SequenceLayout::XNTChar() const { return 'n'; } +int SequenceLayout::writeDerivedClassInBuffer(const char * operatorName, char * buffer, int bufferSize) const { + assert(operatorName != nullptr); + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + + // Write the operator name + int numberOfChar = strlcpy(buffer, operatorName, bufferSize); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the opening parenthesis + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the argument + numberOfChar += const_cast(this)->argumentLayout()->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the comma + buffer[numberOfChar++] = ','; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the lower bound without the "n=" + numberOfChar += LayoutEngine::writeInfixExpressionLayoutTextInBuffer(const_cast(this)->lowerBoundLayout(), buffer+numberOfChar, bufferSize-numberOfChar, "", 1); + // TODO This works because the lower bound layout should always be an + // horizontal layout. + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the comma + buffer[numberOfChar++] = ','; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the upper bound + numberOfChar += const_cast(this)->upperBoundLayout()->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + + // Write the closing parenthesis + buffer[numberOfChar++] = ')'; + buffer[numberOfChar] = 0; + return numberOfChar; +} + ExpressionLayout * SequenceLayout::upperBoundLayout() { - return editableChild(0); + return editableChild(2); } ExpressionLayout * SequenceLayout::lowerBoundLayout() { @@ -180,11 +223,15 @@ ExpressionLayout * SequenceLayout::lowerBoundLayout() { } ExpressionLayout * SequenceLayout::argumentLayout() { - return editableChild(2)->editableChild(1); + return argumentWithParenthesesLayout()->editableChild(1); +} + +ExpressionLayout * SequenceLayout::argumentWithParenthesesLayout() { + return editableChild(0); } KDSize SequenceLayout::computeSize() { - KDSize argumentSize = editableChild(2)->size(); + KDSize argumentSize = argumentWithParenthesesLayout()->size(); KDSize lowerBoundSize = lowerBoundLayout()->size(); KDSize upperBoundSize = upperBoundLayout()->size(); return KDSize( @@ -209,7 +256,7 @@ KDPoint SequenceLayout::positionOfChild(ExpressionLayout * child) { } else if (child == upperBoundLayout()) { x = max(max(0, (k_symbolWidth-upperBoundSize.width())/2), (lowerBoundSize.width()-upperBoundSize.width())/2); y = baseline() - (k_symbolHeight+1)/2- k_boundHeightMargin-upperBoundSize.height(); - } else if (child == editableChild(2)) { + } else if (child == argumentWithParenthesesLayout()) { x = max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin; y = baseline() - argumentLayout()->baseline(); } else { diff --git a/poincare/src/layout/sequence_layout.h b/poincare/src/layout/sequence_layout.h index ca53827d8..418eb6d94 100644 --- a/poincare/src/layout/sequence_layout.h +++ b/poincare/src/layout/sequence_layout.h @@ -19,9 +19,11 @@ public: protected: constexpr static KDCoordinate k_boundHeightMargin = 2; constexpr static KDCoordinate k_argumentWidthMargin = 2; + int writeDerivedClassInBuffer(const char * operatorName, char * buffer, int bufferSize) const; ExpressionLayout * lowerBoundLayout(); ExpressionLayout * upperBoundLayout(); - virtual ExpressionLayout * argumentLayout(); + ExpressionLayout * argumentLayout(); + ExpressionLayout * argumentWithParenthesesLayout(); KDSize computeSize() override; KDPoint positionOfChild(ExpressionLayout * child) override; private: diff --git a/poincare/src/layout/string_layout.cpp b/poincare/src/layout/string_layout.cpp index e1e835993..af1ec891f 100644 --- a/poincare/src/layout/string_layout.cpp +++ b/poincare/src/layout/string_layout.cpp @@ -85,6 +85,20 @@ bool StringLayout::moveRight(ExpressionLayoutCursor * cursor) { return m_parent->moveRight(cursor); } +int StringLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int numberOfChar = strlcpy(buffer, m_string, bufferSize); + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; + } + + buffer[numberOfChar] = 0; + return numberOfChar; +} + void StringLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { ctx->drawString(m_string, p, m_fontSize, expressionColor, backgroundColor); } diff --git a/poincare/src/layout/string_layout.h b/poincare/src/layout/string_layout.h index 48f405d33..40301736b 100644 --- a/poincare/src/layout/string_layout.h +++ b/poincare/src/layout/string_layout.h @@ -2,6 +2,7 @@ #define POINCARE_STRING_LAYOUT_H #include +#include #include namespace Poincare { @@ -20,6 +21,7 @@ public: char * text() { return m_string; } bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDPoint positionOfChild(ExpressionLayout * child) override; diff --git a/poincare/src/layout/sum_layout.cpp b/poincare/src/layout/sum_layout.cpp index 8d198b249..857ae525e 100644 --- a/poincare/src/layout/sum_layout.cpp +++ b/poincare/src/layout/sum_layout.cpp @@ -24,10 +24,14 @@ const uint8_t symbolPixel[SumLayout::k_symbolHeight][SumLayout::k_symbolWidth] = }; ExpressionLayout * SumLayout::clone() const { - SumLayout * layout = new SumLayout(const_cast(this)->lowerBoundLayout(), const_cast(this)->upperBoundLayout(), const_cast(this)->argumentLayout(), true); + SumLayout * layout = new SumLayout(const_cast(this)->argumentLayout(), const_cast(this)->lowerBoundLayout(), const_cast(this)->upperBoundLayout(), true); return layout; } +int SumLayout::writeTextInBuffer(char * buffer, int bufferSize) const { + return SequenceLayout::writeDerivedClassInBuffer("sum", buffer, bufferSize); +} + void SumLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { KDSize upperBoundSize = upperBoundLayout()->size(); KDSize lowerBoundSize = lowerBoundLayout()->size(); diff --git a/poincare/src/layout/sum_layout.h b/poincare/src/layout/sum_layout.h index 7218e866f..4f8228921 100644 --- a/poincare/src/layout/sum_layout.h +++ b/poincare/src/layout/sum_layout.h @@ -2,6 +2,7 @@ #define POINCARE_SUM_LAYOUT_H #include "sequence_layout.h" +#include namespace Poincare { @@ -9,6 +10,7 @@ class SumLayout : public SequenceLayout { public: using SequenceLayout::SequenceLayout; ExpressionLayout * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; private: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; }; diff --git a/poincare/src/layout/uneditable_horizontal_trio_layout.h b/poincare/src/layout/uneditable_horizontal_trio_layout.h index 094b16d39..484947dd5 100644 --- a/poincare/src/layout/uneditable_horizontal_trio_layout.h +++ b/poincare/src/layout/uneditable_horizontal_trio_layout.h @@ -2,6 +2,7 @@ #define POINCARE_UNEDITABLE_HORIZONTAL_TRIO_LAYOUT_H #include +#include namespace Poincare { @@ -26,6 +27,10 @@ public: bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + /* Expression Engine */ + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeInfixExpressionLayoutTextInBuffer(this, buffer, bufferSize, ""); + } protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout_engine.cpp b/poincare/src/layout_engine.cpp index 2690be522..89a6d6664 100644 --- a/poincare/src/layout_engine.cpp +++ b/poincare/src/layout_engine.cpp @@ -1,5 +1,5 @@ #include -#include "layout/editable_string_layout.h" +#include "layout/string_layout.h" #include "layout/parenthesis_layout.h" #include "layout/horizontal_layout.h" extern "C" { @@ -16,12 +16,12 @@ ExpressionLayout * LayoutEngine::createInfixLayout(const Expression * expression ExpressionLayout** children_layouts = new ExpressionLayout * [2*numberOfOperands-1]; children_layouts[0] = expression->operand(0)->createLayout(); for (int i=1; ioperand(i)->type() == Expression::Type::Opposite ? new ParenthesisLayout(expression->operand(i)->createLayout(floatDisplayMode, complexFormat), false) : expression->operand(i)->createLayout(floatDisplayMode, complexFormat); } /* HorizontalLayout holds the children layouts so they do not need to be * deleted here. */ - ExpressionLayout * layout = new HorizontalLayout(children_layouts, 2*numberOfOperands-1, false); + ExpressionLayout * layout = new HorizontalLayout(children_layouts, 2*numberOfOperands-1); delete[] children_layouts; return layout; } @@ -34,48 +34,80 @@ ExpressionLayout * LayoutEngine::createPrefixLayout(const Expression * expressio int layoutIndex = 0; grandChildrenLayouts[layoutIndex++] = expression->operand(0)->createLayout(floatDisplayMode, complexFormat); for (int i = 1; i < numberOfOperands; i++) { - grandChildrenLayouts[layoutIndex++] = new EditableStringLayout(",", 1); + grandChildrenLayouts[layoutIndex++] = new StringLayout(",", 1); grandChildrenLayouts[layoutIndex++] = expression->operand(i)->createLayout(floatDisplayMode, complexFormat); } /* HorizontalLayout holds the grand children layouts so they do not need to * be deleted */ - ExpressionLayout * argumentLayouts = new HorizontalLayout(grandChildrenLayouts, 2*numberOfOperands-1, false); + ExpressionLayout * argumentLayouts = new HorizontalLayout(grandChildrenLayouts, 2*numberOfOperands-1); delete [] grandChildrenLayouts; ExpressionLayout * childrenLayouts[2]; - childrenLayouts[0] = new EditableStringLayout(operatorName, strlen(operatorName)); + childrenLayouts[0] = new StringLayout(operatorName, strlen(operatorName)); childrenLayouts[1] = new ParenthesisLayout(argumentLayouts, false); /* Same comment as above */ - return new HorizontalLayout(childrenLayouts, 2, false); + return new HorizontalLayout(childrenLayouts, 2); } -int LayoutEngine::writeInfixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName, OperandNeedParenthesis operandNeedParenthesis) { +int LayoutEngine::writeInfixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName, OperandNeedsParenthesis operandNeedsParenthesis) { + return writeInfixExpressionOrExpressionLayoutTextInBuffer(expression, nullptr, buffer, bufferSize, operatorName, 0, -1, operandNeedsParenthesis, [](const char * operatorName) { return true; }); +} + +int LayoutEngine::writePrefixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName) { + return writePrefixExpressionOrExpressionLayoutTextInBuffer(expression, nullptr, buffer, bufferSize, operatorName); +} + +int LayoutEngine::writeInfixExpressionLayoutTextInBuffer(const ExpressionLayout * expressionLayout, char * buffer, int bufferSize, const char * operatorName, int firstChildIndex, int lastChildIndex, ChildNeedsParenthesis childNeedsParenthesis) { + return writeInfixExpressionOrExpressionLayoutTextInBuffer(nullptr, expressionLayout, buffer, bufferSize, operatorName, firstChildIndex, lastChildIndex, [](const Expression * e) { return true; }, childNeedsParenthesis); +} + +int LayoutEngine::writePrefixExpressionLayoutTextInBuffer(const ExpressionLayout * expressionLayout, char * buffer, int bufferSize, const char * operatorName, bool writeFirstChild) { + return writePrefixExpressionOrExpressionLayoutTextInBuffer(nullptr, expressionLayout, buffer, bufferSize, operatorName, writeFirstChild); +} + +int LayoutEngine::writeInfixExpressionOrExpressionLayoutTextInBuffer(const Expression * expression, const ExpressionLayout * expressionLayout, char * buffer, int bufferSize, const char * operatorName, int firstChildIndex, int lastChildIndex, OperandNeedsParenthesis operandNeedsParenthesis, ChildNeedsParenthesis childNeedsParenthesis) { + assert(expression != nullptr || expressionLayout != nullptr); if (bufferSize == 0) { return -1; } buffer[bufferSize-1] = 0; int numberOfChar = 0; - int numberOfOperands = expression->numberOfOperands(); + int numberOfOperands = (expression != nullptr) ? expression->numberOfOperands() : expressionLayout->numberOfChildren(); assert(numberOfOperands > 1); - if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - if (operandNeedParenthesis(expression->operand(0))) { - buffer[numberOfChar++] = '('; - if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; } - numberOfChar += expression->operand(0)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); - if (operandNeedParenthesis(expression->operand(0))) { + + if ((expression != nullptr && operandNeedsParenthesis(expression->operand(firstChildIndex))) + || (expression == nullptr && childNeedsParenthesis(operatorName))) + { + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; + } + } + + numberOfChar += (expression != nullptr) ? expression->operand(firstChildIndex)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar) : expressionLayout->child(firstChildIndex)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + if ((expression != nullptr && operandNeedsParenthesis(expression->operand(firstChildIndex))) + || (expression == nullptr && childNeedsParenthesis(operatorName))) + { buffer[numberOfChar++] = ')'; if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } - for (int i=1; i= bufferSize-1) { return bufferSize-1; } numberOfChar += strlcpy(buffer+numberOfChar, operatorName, bufferSize-numberOfChar); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - if (operandNeedParenthesis(expression->operand(i))) { + if ((expression != nullptr && operandNeedsParenthesis(expression->operand(i))) + || (expression == nullptr && childNeedsParenthesis(operatorName))) + { buffer[numberOfChar++] = '('; if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } - numberOfChar += expression->operand(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); - if (operandNeedParenthesis(expression->operand(i))) { + numberOfChar += (expression != nullptr) ? expression->operand(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar) : expressionLayout->child(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + if ((expression != nullptr && operandNeedsParenthesis(expression->operand(i))) + || (expression == nullptr && childNeedsParenthesis(operatorName))) + { buffer[numberOfChar++] = ')'; if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } @@ -84,24 +116,35 @@ int LayoutEngine::writeInfixExpressionTextInBuffer(const Expression * expression return numberOfChar; } -int LayoutEngine::writePrefixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName) { + +int LayoutEngine::writePrefixExpressionOrExpressionLayoutTextInBuffer(const Expression * expression, const ExpressionLayout * expressionLayout, char * buffer, int bufferSize, const char * operatorName, bool writeFirstChild) { + assert(expression != nullptr || expressionLayout != nullptr); if (bufferSize == 0) { return -1; } buffer[bufferSize-1] = 0; - int numberOfChar = 0; - numberOfChar += strlcpy(buffer, operatorName, bufferSize); - if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + int numberOfChar = strlcpy(buffer, operatorName, bufferSize); + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; + } + buffer[numberOfChar++] = '('; - if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - int numberOfOperands = expression->numberOfOperands(); + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; + } + + int numberOfOperands = (expression != nullptr) ? expression->numberOfOperands() : expressionLayout->numberOfChildren(); assert(numberOfOperands > 0); - numberOfChar += expression->operand(0)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); - for (int i = 1; i < numberOfOperands; i++) { + if (!writeFirstChild) { + assert(numberOfOperands > 1); + } + int firstOperandIndex = writeFirstChild ? 0 : 1; + numberOfChar += (expression != nullptr) ? expression->operand(firstOperandIndex)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar) : expressionLayout->child(firstOperandIndex)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + for (int i = firstOperandIndex + 1; i < numberOfOperands; i++) { if (numberOfChar >= bufferSize-1) { return bufferSize-1; } buffer[numberOfChar++] = ','; if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - numberOfChar += expression->operand(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + numberOfChar += (expression != nullptr) ? expression->operand(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar) : expressionLayout->child(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); } if (numberOfChar >= bufferSize-1) { return bufferSize-1; } buffer[numberOfChar++] = ')'; @@ -109,4 +152,17 @@ int LayoutEngine::writePrefixExpressionTextInBuffer(const Expression * expressio return numberOfChar; } +int LayoutEngine::writeOneCharInBuffer(char * buffer, int bufferSize, char charToWrite) { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + if (bufferSize == 1) { + return 0; + } + buffer[0] = charToWrite; + buffer[1] = 0; + return 1; +} + }