diff --git a/apps/reader/tex_parser.cpp b/apps/reader/tex_parser.cpp index 54f378813..d09b4d84a 100644 --- a/apps/reader/tex_parser.cpp +++ b/apps/reader/tex_parser.cpp @@ -51,6 +51,7 @@ Layout TexParser::popText(char stop) { while (m_text < m_endOfText && *m_text != stop) { switch (*m_text) { + // TODO: Factorize this code case '\\': if (start != m_text) { layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false); @@ -101,28 +102,40 @@ Layout TexParser::popText(char stop) { } Layout TexParser::popCommand() { + // TODO: Factorize this code if (strncmp(k_fracCommand, m_text, strlen(k_fracCommand)) == 0) { - m_text += strlen(k_fracCommand); - if (*m_text == ' ' || *m_text == '{') { + const char * endOfCommand= m_text + strlen(k_fracCommand); + if (*endOfCommand == ' ' || *endOfCommand == '{') { + m_text += strlen(k_fracCommand); return popFracCommand(); } } else if (strncmp(k_sqrtCommand, m_text, strlen(k_sqrtCommand)) == 0) { - m_text += strlen(k_sqrtCommand); - if (*m_text == ' ' || *m_text == '{' || *m_text == '[') { + const char * endOfCommand= m_text + strlen(k_sqrtCommand); + if (*endOfCommand == ' ' || *endOfCommand == '{' || *endOfCommand == '[') { + m_text += strlen(k_sqrtCommand); return popSqrtCommand(); } } else if (strncmp(k_thetaCommand, m_text, strlen(k_thetaCommand)) == 0) { - m_text += strlen(k_thetaCommand); - if (*m_text == ' ') { - return popthetaCommand(); + const char * endOfCommand = m_text + strlen(k_thetaCommand); + if (*endOfCommand == ' ' || *endOfCommand == '\\' || *endOfCommand == '$') { + m_text += strlen(k_thetaCommand); + return popThetaCommand(); } } else if (strncmp(k_piCommand, m_text, strlen(k_piCommand)) == 0) { - m_text += strlen(k_piCommand); - if (*m_text == ' ') { - return poppiCommand(); + const char * endOfCommand = m_text + strlen(k_piCommand); + if (*endOfCommand == ' ' || *endOfCommand == '\\' || *endOfCommand == '$') { + m_text += strlen(k_piCommand); + return popPiCommand(); + } + } + else if (strncmp(k_overRightArrowCommand, m_text, strlen(k_overRightArrowCommand)) == 0) { + const char * endOfCommand = m_text + strlen(k_overRightArrowCommand); + if (*endOfCommand == ' ' || *endOfCommand == '{' || *endOfCommand == '[') { + m_text += strlen(k_overRightArrowCommand); + return popOverrightarrow(); } } @@ -131,27 +144,36 @@ Layout TexParser::popCommand() { } Layout TexParser::popFracCommand() { - return FractionLayout::Builder(popBlock(), popBlock()); + Layout numerator = popBlock(); + Layout denominator = popBlock(); + FractionLayout l = FractionLayout::Builder(numerator, denominator); + return l; } Layout TexParser::popSqrtCommand() { while (*m_text == ' ') { m_text ++; } - m_text++; if (*m_text == '[') { - return NthRootLayout::Builder(popText(']'), popBlock()); + m_text ++; + Layout rootFactor = popText(']'); + Layout belowRoot = popBlock(); + return NthRootLayout::Builder(belowRoot, rootFactor); } else { return NthRootLayout::Builder(popBlock()); } } -Layout TexParser::popthetaCommand() { +Layout TexParser::popOverrightarrow() { + return VectorLayout::Builder(popBlock()); +} + +Layout TexParser::popThetaCommand() { return CodePointLayout::Builder(CodePoint(0x3b8)); } -Layout TexParser::poppiCommand() { +Layout TexParser::popPiCommand() { return CodePointLayout::Builder(CodePoint(0x3c0)); } diff --git a/apps/reader/tex_parser.h b/apps/reader/tex_parser.h index 765d50d7a..38367b42c 100644 --- a/apps/reader/tex_parser.h +++ b/apps/reader/tex_parser.h @@ -20,8 +20,9 @@ private: Layout popCommand(); Layout popFracCommand(); Layout popSqrtCommand(); - Layout poppiCommand(); - Layout popthetaCommand(); + Layout popOverrightarrow(); + Layout popPiCommand(); + Layout popThetaCommand(); const char * m_text; const char * m_endOfText; bool m_hasError; @@ -30,6 +31,7 @@ private: static constexpr char const * k_sqrtCommand = "sqrt"; static constexpr char const * k_thetaCommand = "theta"; static constexpr char const * k_piCommand = "pi"; + static constexpr char const * k_overRightArrowCommand = "overrightarrow"; }; } diff --git a/apps/reader/utility.cpp b/apps/reader/utility.cpp index 24605d14c..591267973 100644 --- a/apps/reader/utility.cpp +++ b/apps/reader/utility.cpp @@ -131,7 +131,7 @@ const char * StartOfPrintableWord(const char * word, const char * start) { if (word == start) { return word; } - UTF8Decoder decoder(word); + UTF8Decoder decoder(start, word); CodePoint codePoint = decoder.previousCodePoint(); const char * result = word; while (codePoint != '\n' && codePoint != ' ' && codePoint != '%' && codePoint != '$') { diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp index 4eaa5c783..f944f2c64 100644 --- a/apps/reader/word_wrap_view.cpp +++ b/apps/reader/word_wrap_view.cpp @@ -40,7 +40,7 @@ void WordWrapTextView::previousPage() { const int charHeight = m_font->glyphSize().height(); const char * endOfFile = text() + m_length; - const char * endOfWord = text() + m_pageOffset - 1; + const char * endOfWord = text() + m_pageOffset; const char * startOfWord = StartOfPrintableWord(endOfWord, text()); KDSize textSize = KDSizeZero; @@ -48,8 +48,8 @@ void WordWrapTextView::previousPage() { KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin); while(startOfWord>=text()) { - startOfWord = StartOfPrintableWord(endOfWord, text()); - endOfWord = EndOfPrintableWord(startOfWord, endOfFile); + startOfWord = StartOfPrintableWord(endOfWord-1, text()); + //endOfWord = EndOfPrintableWord(startOfWord, endOfFile); if (*startOfWord == '%') { if (updateTextColorBackward(startOfWord)) { diff --git a/poincare/Makefile b/poincare/Makefile index eca0b7ed8..380f200ab 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -26,6 +26,7 @@ poincare_src += $(addprefix poincare/src/,\ right_square_bracket_layout.cpp \ sequence_layout.cpp \ sum_layout.cpp \ + vector_layout.cpp \ vertical_offset_layout.cpp \ ) diff --git a/poincare/include/poincare/layout_cursor.h b/poincare/include/poincare/layout_cursor.h index de552e798..b8d5c2f58 100644 --- a/poincare/include/poincare/layout_cursor.h +++ b/poincare/include/poincare/layout_cursor.h @@ -23,6 +23,7 @@ class LayoutCursor final { friend class MatrixLayoutNode; friend class NthRootLayoutNode; friend class SequenceLayoutNode; + friend class VectorLayoutNode; friend class VerticalOffsetLayoutNode; public: constexpr static KDCoordinate k_cursorWidth = 1; diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index e2aaf7a89..d033788aa 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -40,6 +40,7 @@ public: RightParenthesisLayout, RightSquareBracketLayout, SumLayout, + VectorLayout, VectorNormLayout, VerticalOffsetLayout }; diff --git a/poincare/include/poincare/vector_layout.h b/poincare/include/poincare/vector_layout.h new file mode 100644 index 000000000..3d3a3e676 --- /dev/null +++ b/poincare/include/poincare/vector_layout.h @@ -0,0 +1,53 @@ +#ifndef POINCARE_VECTOR_LAYOUT_NODE_H +#define POINCARE_VECTOR_LAYOUT_NODE_H + +#include +#include +#include + +namespace Poincare { + +class VectorLayoutNode final : public LayoutNode { +public: + // Layout + Type type() const override { return Type::VectorLayout; } + + // SerializationHelperInterface + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { + return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "vector", true, 0); + } + + virtual void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection = false); + virtual void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection = false); + + // TreeNode + size_t size() const override { return sizeof(VectorLayoutNode); } + int numberOfChildren() const override { return 1; } +#if POINCARE_TREE_LOG + void logNodeName(std::ostream & stream) const override { + stream << "VectorLayout"; + } +#endif + + constexpr static KDCoordinate k_arrowWidth = 5; + constexpr static KDCoordinate k_arrowHeight = 9; +protected: + virtual KDSize computeSize(); + virtual KDCoordinate computeBaseline(); + virtual KDPoint positionOfChild(LayoutNode * child); +private: + virtual void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor, Layout * selectionStart = nullptr, Layout * selectionEnd = nullptr, KDColor selectionColor = KDColorRed); + constexpr static KDCoordinate k_sideMargin = 2; + constexpr static KDCoordinate k_topMargin = 1; + constexpr static KDCoordinate k_arrowLineHeight = 1; // k_arrowHeight - k_arrowLineHeight must be even +}; + +class VectorLayout final : public Layout { +public: + static VectorLayout Builder(Layout child) { return TreeHandle::FixedArityBuilder({child}); } + VectorLayout() = delete; +}; + +} + +#endif diff --git a/poincare/include/poincare_layouts.h b/poincare/include/poincare_layouts.h index 72cb49ac6..f8a4319af 100644 --- a/poincare/include/poincare_layouts.h +++ b/poincare/include/poincare_layouts.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/poincare/src/tree_handle.cpp b/poincare/src/tree_handle.cpp index ea1712371..3c1a6cdfd 100644 --- a/poincare/src/tree_handle.cpp +++ b/poincare/src/tree_handle.cpp @@ -375,6 +375,7 @@ template VectorCross TreeHandle::FixedArityBuilder template VectorDot TreeHandle::FixedArityBuilder(const Tuple &); template VectorNorm TreeHandle::FixedArityBuilder(const Tuple &); template VectorNormLayout TreeHandle::FixedArityBuilder(const Tuple &); +template VectorLayout TreeHandle::FixedArityBuilder(const Tuple &); template MatrixLayout TreeHandle::NAryBuilder(const Tuple &); } diff --git a/poincare/src/vector_layout.cpp b/poincare/src/vector_layout.cpp new file mode 100644 index 000000000..5d9b816f7 --- /dev/null +++ b/poincare/src/vector_layout.cpp @@ -0,0 +1,78 @@ +#include + +namespace Poincare +{ + const uint8_t arrowMask[VectorLayoutNode::k_arrowHeight][VectorLayoutNode::k_arrowWidth] = { + {0xff, 0xf7, 0xff, 0xff, 0xff}, + {0xf3, 0x2c, 0xd9, 0xff, 0xff}, + {0xff, 0x93, 0x46, 0xfb, 0xff}, + {0xff, 0xfb, 0x46, 0x93, 0xff}, + {0x13, 0x13, 0x13, 0x13, 0xf0}, + {0xff, 0xfb, 0x46, 0x93, 0xff}, + {0xff, 0x93, 0x46, 0xfb, 0xff}, + {0xf3, 0x2c, 0xd9, 0xff, 0xff}, + {0xff, 0xf7, 0xff, 0xff, 0xff} + }; + void VectorLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection) { + if (cursor->layoutNode() == childAtIndex(0) + && cursor->position() == LayoutCursor::Position::Left) + { + // Case: Left of the operand. Go Left of the brackets. + cursor->setLayout(this); + return; + } + assert(cursor->layoutNode() == this); + if (cursor->position() == LayoutCursor::Position::Right) { + // Case: Right of the brackets. Go Right of the operand. + cursor->setLayout(childAtIndex(0)); + return; + } + assert(cursor->position() == LayoutCursor::Position::Left); + // Case: Left of the brackets. Ask the parent. + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); + } + } + + void VectorLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection) { + if (cursor->layoutNode() == childAtIndex(0) + && cursor->position() == LayoutCursor::Position::Right) + { + // Case: Right of the operand. Go Right of the brackets. + cursor->setLayout(this); + return; + } + assert(cursor->layoutNode() == this); + if (cursor->position() == LayoutCursor::Position::Left) { + // Case: Left of the brackets. Go Left of the operand. + cursor->setLayout(childAtIndex(0)); + return; + } + assert(cursor->position() == LayoutCursor::Position::Right); + // Case: Right of the brackets. Ask the parent. + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorRight(cursor, shouldRecomputeLayout); + } + } + KDSize VectorLayoutNode::computeSize() { + KDSize size = childAtIndex(0)->layoutSize(); + return KDSize(2 * k_sideMargin + size.width() + k_arrowWidth + k_sideMargin, k_topMargin + (k_arrowHeight+k_arrowLineHeight)/2 + size.height()); + } + + KDCoordinate VectorLayoutNode::computeBaseline() { + return childAtIndex(0)->baseline() + (k_arrowHeight+k_arrowLineHeight)/2 + k_arrowLineHeight + k_topMargin; + } + + KDPoint VectorLayoutNode::positionOfChild(LayoutNode * child) { + assert(child == childAtIndex(0)); + return KDPoint(k_sideMargin * 2, k_topMargin + (k_arrowHeight+k_arrowLineHeight)/2 + k_arrowLineHeight); + } + void VectorLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor, Layout * selectionStart, Layout * selectionEnd, KDColor selectionColor) { + KDColor workingBuffer[k_arrowWidth * k_arrowHeight]; + ctx->fillRect(KDRect(p.x() + k_sideMargin, p.y() + k_topMargin + (k_arrowHeight-k_arrowLineHeight)/2, 2 * k_sideMargin + childAtIndex(0)->layoutSize().width(), k_arrowLineHeight), expressionColor); + ctx->blendRectWithMask(KDRect(p.x() + 2 * k_sideMargin + childAtIndex(0)->layoutSize().width(), p.y() + k_topMargin, k_arrowWidth, k_arrowHeight), expressionColor, (const uint8_t *)arrowMask, workingBuffer); + } + +}