diff --git a/kandinsky/include/kandinsky/unicode/code_point.h b/kandinsky/include/kandinsky/unicode/code_point.h index 033e96407..fc32a7b1a 100644 --- a/kandinsky/include/kandinsky/unicode/code_point.h +++ b/kandinsky/include/kandinsky/unicode/code_point.h @@ -16,8 +16,12 @@ private: uint32_t m_code; }; -static constexpr CodePoint KDCodePointNull = 0x0; -static constexpr CodePoint KDCodePointTabulation = 0x9; -static constexpr CodePoint KDCodePointLineFeed = 0xA; +static constexpr CodePoint KDCodePointNull = 0x0; +static constexpr CodePoint KDCodePointTabulation = 0x9; +static constexpr CodePoint KDCodePointLineFeed = 0xA; +static constexpr CodePoint KDCodePointMiddleDot = 0xB7; +static constexpr CodePoint KDCodePointMultiplicationSign = 0xD7; +static constexpr CodePoint KDCodePointLatinLetterSmallCapitalE = 0x1d07; +static constexpr CodePoint KDCodePointRightwardsArrow = 0x2192; #endif diff --git a/poincare/Makefile b/poincare/Makefile index 4a41680f9..cc7f7e2df 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -5,6 +5,7 @@ src += $(addprefix poincare/src/,\ bracket_layout.cpp \ bracket_pair_layout.cpp \ char_layout.cpp \ + code_point_layout.cpp\ condensed_sum_layout.cpp \ conjugate_layout.cpp \ empty_layout.cpp \ diff --git a/poincare/include/poincare/code_point_layout.h b/poincare/include/poincare/code_point_layout.h new file mode 100644 index 000000000..06f5adb91 --- /dev/null +++ b/poincare/include/poincare/code_point_layout.h @@ -0,0 +1,76 @@ +#ifndef POINCARE_CODEPOINT_LAYOUT_NODE_H +#define POINCARE_CODEPOINT_LAYOUT_NODE_H + +#include +#include +#include + +namespace Poincare { + +/* TODO: Make several code point classes depending on codepoint size? + * (m_codePoint sometimes fits in a char, no need for a whole CodePoint */ + +class CodePointLayoutNode final : public LayoutNode { +public: + static constexpr const KDFont * k_defaultFont = KDFont::LargeFont; + CodePointLayoutNode(CodePoint c = KDCodePointNull, const KDFont * font = k_defaultFont) : + LayoutNode(), + m_codePoint(c), + m_font(font) + {} + + // CodePointLayout + CodePoint codePoint() const { return m_codePoint; } + const KDFont * font() const { return m_font; } + + // LayoutNode + void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; + void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + bool isCodePoint() const override { return true; } + bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const override; + bool canBeOmittedMultiplicationLeftFactor() const override; + bool canBeOmittedMultiplicationRightFactor() const override; + + // TreeNode + size_t size() const override { return sizeof(CodePointLayoutNode); } + int numberOfChildren() const override { return 0; } +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "CodePointLayout"; + } + virtual void logAttributes(std::ostream & stream) const override { + stream << " CodePoint=\"" << m_codePoint << "\""; + } +#endif + +protected: + // LayoutNode + KDSize computeSize() override; + KDCoordinate computeBaseline() override; + KDPoint positionOfChild(LayoutNode * child) override { + assert(false); + return KDPointZero; + } + +private: + void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; + bool isMultiplicationCodePoint() const; + CodePoint m_codePoint; + const KDFont * m_font; +}; + +class CodePointLayout final : public Layout { +public: + CodePointLayout(const CodePointLayoutNode * n) : Layout(n) {} + static CharLayout Builder(CodePoint c, const KDFont * font = KDFont::LargeFont); + const KDFont * font() const { return const_cast(this)->node()->font(); } + CodePoint codePoint() const { return const_cast(this)->node()->codePoint(); } +private: + using Layout::node; + CodePointLayoutNode * node() { return static_cast(Layout::node()); } +}; + +} + +#endif diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index 2dfffdcd9..bf70fba41 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -50,6 +50,7 @@ public: bool isVerticalOffset() const { return const_cast(this)->node()->isVerticalOffset(); } bool isLeftParenthesis() const { return const_cast(this)->node()->isLeftParenthesis(); } bool isChar() const { return const_cast(this)->node()->isChar(); } + bool isCodePoint() const { return const_cast(this)->node()->isCodePoint(); } bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { return const_cast(this)->node()->isCollapsable(numberOfOpenParenthesis, goingLeft); } int leftCollapsingAbsorbingChildIndex() const { return const_cast(this)->node()->leftCollapsingAbsorbingChildIndex(); } int rightCollapsingAbsorbingChildIndex() const { return const_cast(this)->node()->rightCollapsingAbsorbingChildIndex(); } diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index 40a2afe41..c10120091 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -105,6 +105,7 @@ public: virtual bool isEmpty() const { return false; } virtual bool isMatrix() const { return false; } virtual bool isChar() const { return false; } + virtual bool isCodePoint() const { return false; } virtual bool hasUpperLeftIndex() const { return false; } virtual char XNTChar() const { LayoutNode * p = parent(); diff --git a/poincare/include/poincare/serialization_helper.h b/poincare/include/poincare/serialization_helper.h index 2135d0aea..c2406302d 100644 --- a/poincare/include/poincare/serialization_helper.h +++ b/poincare/include/poincare/serialization_helper.h @@ -2,11 +2,12 @@ #define POINCARE_SERIALIZATION_HELPER_H #include +#include namespace Poincare { namespace SerializationHelper { - /* SerializableReference to Text */ + // SerializableReference to text int Infix( const TreeNode * node, char * buffer, @@ -26,8 +27,10 @@ namespace SerializationHelper { const char * operatorName, bool writeFirstChild = true); - /* Write one char in buffer */ - int Char(char * buffer, int bufferSize, char charToWrite); + // Write one char in buffer + int Char(char * buffer, int bufferSize, char charToWrite); // TODO REMOVE + // Write one code point in a buffer + int CodePoint(char * buffer, int bufferSize, CodePoint c); }; } diff --git a/poincare/include/poincare_layouts.h b/poincare/include/poincare_layouts.h index 743d99bac..3cf5ee953 100644 --- a/poincare/include/poincare_layouts.h +++ b/poincare/include/poincare_layouts.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/poincare/src/code_point_layout.cpp b/poincare/src/code_point_layout.cpp new file mode 100644 index 000000000..2e052164c --- /dev/null +++ b/poincare/src/code_point_layout.cpp @@ -0,0 +1,108 @@ +#include +#include +#include + +namespace Poincare { + +// LayoutNode +void CodePointLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + if (cursor->position() == LayoutCursor::Position::Right) { + cursor->setPosition(LayoutCursor::Position::Left); + return; + } + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); + } +} + +void CodePointLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + if (cursor->position() == LayoutCursor::Position::Left) { + cursor->setPosition(LayoutCursor::Position::Right); + return; + } + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorRight(cursor, shouldRecomputeLayout); + } +} + +int CodePointLayoutNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + return SerializationHelper::CodePoint(buffer, bufferSize, m_codePoint); +} + +bool CodePointLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { + if (*numberOfOpenParenthesis <= 0) { + if (m_codePoint == '+' + || m_codePoint == KDCodePointRightwardsArrow + || m_codePoint == '=' + || m_codePoint == ',') + { + return false; + } + if (m_codePoint == '-') { + /* If the expression is like 3ᴇ-200, we want '-' to be collapsable. + * Otherwise, '-' is not collapsable. */ + Layout thisRef = CodePointLayout(this); + Layout parent = thisRef.parent(); + if (!parent.isUninitialized()) { + int indexOfThis = parent.indexOfChild(thisRef); + if (indexOfThis > 0) { + Layout leftBrother = parent.childAtIndex(indexOfThis-1); + if (leftBrother.isCodePoint() + && static_cast(leftBrother).codePoint() == KDCodePointLatinLetterSmallCapitalE) + { + return true; + } + } + } + return false; + } + } + return true; +} + +bool CodePointLayoutNode::canBeOmittedMultiplicationLeftFactor() const { + if (isMultiplicationCodePoint()) { + return false; + } + return LayoutNode::canBeOmittedMultiplicationRightFactor(); +} + +bool CodePointLayoutNode::canBeOmittedMultiplicationRightFactor() const { + if (m_codePoint == '!' || isMultiplicationCodePoint()) { + return false; + } + return LayoutNode::canBeOmittedMultiplicationRightFactor(); +} + +// Sizing and positioning +KDSize CodePointLayoutNode::computeSize() { + return m_font->glyphSize(); +} + +KDCoordinate CodePointLayoutNode::computeBaseline() { + return m_font->glyphSize().height()/2; +} + +void CodePointLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { + constexpr int bufferSize = sizeof(CodePoint)/sizeof(char) + 1; // Null-terminating char + char buffer[bufferSize]; + SerializationHelper::CodePoint(buffer, bufferSize, m_codePoint); + ctx->drawString(buffer, p, m_font, expressionColor, backgroundColor); +} + +bool CodePointLayoutNode::isMultiplicationCodePoint() const { + return m_codePoint == '*' + || m_codePoint == KDCodePointMultiplicationSign + || m_codePoint == KDCodePointMiddleDot; +} + +CodePointLayout CodePointLayout::Builder(CodePoint c, const KDFont * font) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(CodePointLayoutNode)); + CodePointLayoutNode * node = new (bufferNode) CodePointLayoutNode(c, font); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); +} + +} diff --git a/poincare/src/serialization_helper.cpp b/poincare/src/serialization_helper.cpp index 3375d3b6f..c255bd160 100644 --- a/poincare/src/serialization_helper.cpp +++ b/poincare/src/serialization_helper.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -149,4 +150,21 @@ int SerializationHelper::Char(char * buffer, int bufferSize, char charToWrite) { return 1; } +int SerializationHelper::CodePoint(char * buffer, int bufferSize, class CodePoint c) { + if (bufferSize == 0) { + return -1; + } + if (bufferSize == 1) { + buffer[0] = 0; + return 0; + } + constexpr int maxCodePointSize = sizeof(class CodePoint)/sizeof(char) + 1; // Null-terminating char + char helpBuffer[maxCodePointSize]; + size_t size = UTF8Decoder::CodePointToChars(c, helpBuffer, maxCodePointSize); + assert(size < maxCodePointSize); + helpBuffer[size] = 0; + strlcpy(buffer, helpBuffer, bufferSize); + return strlen(buffer); +} + }