diff --git a/apps/reader/tex_parser.cpp b/apps/reader/tex_parser.cpp index b490fe775..2156c4e1c 100644 --- a/apps/reader/tex_parser.cpp +++ b/apps/reader/tex_parser.cpp @@ -79,6 +79,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); @@ -129,23 +130,30 @@ 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 == '{') { + if (isCommandEnded(*(m_text + strlen(k_fracCommand)))) { + 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 == '[') { + if (strncmp(k_sqrtCommand, m_text, strlen(k_sqrtCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_sqrtCommand)))) { + m_text += strlen(k_sqrtCommand); return popSqrtCommand(); } } + if (strncmp(k_overrightArrowCommand, m_text, strlen(k_overrightArrowCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_overrightArrowCommand)))) { + m_text += strlen(k_overrightArrowCommand); + return popOverrightarrowCommand(); + } + } for (int i = 0; i < k_NumberOfSymbols; i++) { if (strncmp(k_SymbolsCommands[i], m_text, strlen(k_SymbolsCommands[i])) == 0) { - m_text += strlen(k_SymbolsCommands[i]); - if (*m_text == ' ' || *m_text == '\\' || *m_text == '$') { + if (isCommandEnded(*(m_text + strlen(k_SymbolsCommands[i])))) { + m_text += strlen(k_SymbolsCommands[i]); return popSymbolCommand(i); } } @@ -153,8 +161,8 @@ Layout TexParser::popCommand() { for (int i = 0; i < k_NumberOfFunctionCommands; i++) { if (strncmp(k_FunctionCommands[i], m_text, strlen(k_FunctionCommands[i])) == 0) { - m_text += strlen(k_FunctionCommands[i]); - if (*m_text == ' ' || *m_text == '(' || *m_text == '{') { + if (isCommandEnded(*(m_text + strlen(k_FunctionCommands[i])))) { + m_text += strlen(k_FunctionCommands[i]); return LayoutHelper::String(k_FunctionCommands[i], strlen(k_FunctionCommands[i])); } } @@ -187,10 +195,16 @@ Layout TexParser::popSqrtCommand() { } } +Layout TexParser::popOverrightarrowCommand() { + return VectorLayout::Builder(popBlock()); +} + Layout TexParser::popSymbolCommand(int SymbolIndex) { uint32_t codePoint = k_SymbolsCodePoints[SymbolIndex]; return CodePointLayout::Builder(codePoint); } - +inline bool TexParser::isCommandEnded(char c) const { + return !(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z'); +} } \ No newline at end of file diff --git a/apps/reader/tex_parser.h b/apps/reader/tex_parser.h index e62c8413b..691543946 100644 --- a/apps/reader/tex_parser.h +++ b/apps/reader/tex_parser.h @@ -22,6 +22,7 @@ private: // Expressions Layout popFracCommand(); Layout popSqrtCommand(); + Layout popOverrightarrowCommand(); //Symbols Layout popSymbolCommand(int SymbolIndex); @@ -30,9 +31,12 @@ private: const char * m_endOfText; bool m_hasError; + inline bool isCommandEnded(char c) const; + // Expressions that require specific handling static constexpr char const * k_fracCommand = "frac"; static constexpr char const * k_sqrtCommand = "sqrt"; + static constexpr char const * k_overrightArrowCommand = "overrightarrow"; static constexpr int const k_NumberOfSymbols = 70; static constexpr int const k_NumberOfFunctionCommands = 32; 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); + } + +}