diff --git a/apps/expression_editor/expression_view_with_cursor.cpp b/apps/expression_editor/expression_view_with_cursor.cpp index 0c83c41ce..3f501434a 100644 --- a/apps/expression_editor/expression_view_with_cursor.cpp +++ b/apps/expression_editor/expression_view_with_cursor.cpp @@ -44,8 +44,8 @@ void ExpressionViewWithCursor::layoutCursorSubview() { } else if (m_cursor->position() == ExpressionLayoutCursor::Position::Inside) { cursorX += m_cursor->positionInside() * KDText::charSize().width(); } - KDPoint cursorTopLeftPosition(cursorX, expressionViewOrigin.y() + cursoredExpressionViewOrigin.y() + m_cursor->pointedExpressionLayout()->baseline()-k_cursorHeight/2); - m_cursorView.setFrame(KDRect(cursorTopLeftPosition, 1, k_cursorHeight)); + KDPoint cursorTopLeftPosition(cursorX, expressionViewOrigin.y() + cursoredExpressionViewOrigin.y() + m_cursor->pointedExpressionLayout()->baseline()-m_cursor->cursorHeight()/2); + m_cursorView.setFrame(KDRect(cursorTopLeftPosition, 1, m_cursor->cursorHeight())); } } diff --git a/apps/expression_editor/expression_view_with_cursor.h b/apps/expression_editor/expression_view_with_cursor.h index 991bc4ba1..79353173c 100644 --- a/apps/expression_editor/expression_view_with_cursor.h +++ b/apps/expression_editor/expression_view_with_cursor.h @@ -21,7 +21,6 @@ private: Top, Bottom }; - constexpr static KDCoordinate k_cursorHeight = 18; int numberOfSubviews() const override { return 2; } View * subviewAtIndex(int index) override; void layoutSubviews() override; diff --git a/kandinsky/include/kandinsky/point.h b/kandinsky/include/kandinsky/point.h index 67003b8e7..892aa92c7 100644 --- a/kandinsky/include/kandinsky/point.h +++ b/kandinsky/include/kandinsky/point.h @@ -17,6 +17,7 @@ public: bool operator !=(const KDPoint &other) const { return !(operator ==(other)); } + uint16_t squareDistanceTo(KDPoint other) const; private: KDCoordinate m_x; KDCoordinate m_y; diff --git a/kandinsky/include/kandinsky/rect.h b/kandinsky/include/kandinsky/rect.h index b149a35cf..d5c93ae82 100644 --- a/kandinsky/include/kandinsky/rect.h +++ b/kandinsky/include/kandinsky/rect.h @@ -45,6 +45,8 @@ public: KDCoordinate left() const { return m_x; } KDPoint topLeft() const { return KDPoint(left(), top()); } + KDPoint topRight() const { return KDPoint(right(), top()); } + KDPoint bottomLeft() const { return KDPoint(left(), bottom()); } KDPoint bottomRight() const { return KDPoint(right(), bottom()); } bool operator ==(const KDRect &other) const { @@ -61,6 +63,8 @@ public: KDRect intersectedWith(const KDRect & other) const; KDRect unionedWith(const KDRect & other) const; // Returns the smallest rectangle containing r1 and r2 bool contains(KDPoint p) const; + bool isAbove(KDPoint p) const; + bool isUnder(KDPoint p) const; bool isEmpty() const; private: diff --git a/kandinsky/src/point.cpp b/kandinsky/src/point.cpp index 4fe0a7afb..796232558 100644 --- a/kandinsky/src/point.cpp +++ b/kandinsky/src/point.cpp @@ -7,3 +7,7 @@ KDPoint KDPoint::translatedBy(KDPoint other) const { KDPoint KDPoint::opposite() const { return KDPoint(-m_x, -m_y); } + +uint16_t KDPoint::squareDistanceTo(KDPoint other) const { + return (m_x-other.x()) * (m_x-other.x()) + (m_y-other.y()) * (m_y-other.y()); +} diff --git a/kandinsky/src/rect.cpp b/kandinsky/src/rect.cpp index a9702ce45..9cf0df654 100644 --- a/kandinsky/src/rect.cpp +++ b/kandinsky/src/rect.cpp @@ -105,6 +105,14 @@ bool KDRect::contains(KDPoint p) const { return (p.x() >= x() && p.x() <= right() && p.y() >= y() && p.y() <= bottom()); } +bool KDRect::isAbove(KDPoint p) const { + return (p.y() >= y()); +} + +bool KDRect::isUnder(KDPoint p) const { + return (p.y() <= bottom()); +} + KDRect KDRect::translatedBy(KDPoint p) const { return KDRect(x() + p.x(), y() + p.y(), width(), height()); } diff --git a/poincare/include/poincare/expression_layout.h b/poincare/include/poincare/expression_layout.h index 2f49c2b8e..b54e8d058 100644 --- a/poincare/include/poincare/expression_layout.h +++ b/poincare/include/poincare/expression_layout.h @@ -9,6 +9,11 @@ class ExpressionLayoutCursor; class ExpressionLayout { public: + enum class VerticalDirection { + Up, + Down + }; + ExpressionLayout(); virtual ~ExpressionLayout() = default; @@ -25,6 +30,8 @@ public: /* Tree navigation */ virtual bool moveLeft(ExpressionLayoutCursor * cursor) { return false; } //TODO should be virtual pure? virtual bool moveRight(ExpressionLayoutCursor * cursor) { return false; } //TODO should be virtual pure? + virtual bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout = nullptr, ExpressionLayout * previousPreviousLayout = nullptr); + bool moveUpInside(ExpressionLayoutCursor * cursor); protected: virtual void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) = 0; virtual KDSize computeSize() = 0; @@ -32,7 +39,15 @@ protected: virtual KDPoint positionOfChild(ExpressionLayout * child) = 0; KDCoordinate m_baseline; ExpressionLayout * m_parent; + virtual void moveCursorInsideAtDirection ( + VerticalDirection direction, + ExpressionLayoutCursor * cursor, + ExpressionLayout ** childResult, + void * resultPosition, + int * resultPositionInside, + int * resultScore); private: + bool moveInside(VerticalDirection direction, ExpressionLayoutCursor * cursor); bool m_sized, m_positioned; KDRect m_frame; }; diff --git a/poincare/include/poincare/expression_layout_cursor.h b/poincare/include/poincare/expression_layout_cursor.h index 89b2a0462..0fe1f689e 100644 --- a/poincare/include/poincare/expression_layout_cursor.h +++ b/poincare/include/poincare/expression_layout_cursor.h @@ -2,6 +2,7 @@ #define POINCARE_EXPRESSION_LAYOUT_CURSOR_H #include +#include namespace Poincare { @@ -26,6 +27,7 @@ public: void setPosition(Position position) { m_position = position; } int positionInside() const { return m_positionInside; } void setPositionInside(int positionInside) { m_positionInside = positionInside; } + KDCoordinate cursorHeight() const { return k_cursorHeight; } /* Move */ bool moveLeft(); @@ -33,7 +35,14 @@ public: bool moveUp(); bool moveDown(); + /* Comparison */ + bool positionIsEquivalentTo(ExpressionLayout * expressionLayout, Position position, int positionIndex = 0); + + /* Position */ + KDPoint middleLeftPoint(); + KDPoint middleLeftPointOfCursor(ExpressionLayout * expressionLayout, Position position, int positionInside = 0); private: + constexpr static KDCoordinate k_cursorHeight = 18; ExpressionLayout * m_pointedExpressionLayout; Position m_position; int m_positionInside; diff --git a/poincare/src/expression_layout_cursor.cpp b/poincare/src/expression_layout_cursor.cpp index 17c38ae12..1b45f7d32 100644 --- a/poincare/src/expression_layout_cursor.cpp +++ b/poincare/src/expression_layout_cursor.cpp @@ -1,4 +1,6 @@ #include +#include +#include namespace Poincare { @@ -11,12 +13,34 @@ bool ExpressionLayoutCursor::moveRight() { } bool ExpressionLayoutCursor::moveUp() { - return false; //TODO + return m_pointedExpressionLayout->moveUp(this); } bool ExpressionLayoutCursor::moveDown() { return false; //TODO } +bool ExpressionLayoutCursor::positionIsEquivalentTo(ExpressionLayout * expressionLayout, Position position, int positionIndex) { + assert(expressionLayout != nullptr); + return middleLeftPoint() == middleLeftPointOfCursor(expressionLayout, position, positionIndex); +} + +KDPoint ExpressionLayoutCursor::middleLeftPoint() { + return middleLeftPointOfCursor(m_pointedExpressionLayout, m_position, m_positionInside); +} + +KDPoint ExpressionLayoutCursor::middleLeftPointOfCursor(ExpressionLayout * expressionLayout, Position position, int positionInside) { + KDPoint layoutOrigin = expressionLayout->absoluteOrigin(); + KDCoordinate y = layoutOrigin.y() + expressionLayout->baseline() - k_cursorHeight/2; + if (position == Position::Left) { + return KDPoint(layoutOrigin.x(), y); + } + if (position == Position::Right) { + return KDPoint(layoutOrigin.x() + expressionLayout->size().width(), y); + } + assert(position == Position::Inside); + return KDPoint(layoutOrigin.x() + positionInside * KDText::charSize().width(), y); +} + } diff --git a/poincare/src/layout/baseline_relative_layout.h b/poincare/src/layout/baseline_relative_layout.h index d88e26708..1b75f6822 100644 --- a/poincare/src/layout/baseline_relative_layout.h +++ b/poincare/src/layout/baseline_relative_layout.h @@ -29,9 +29,9 @@ protected: KDPoint positionOfChild(ExpressionLayout * child) override; ExpressionLayout * m_baseLayout; ExpressionLayout * m_indiceLayout; + Type m_type; private: constexpr static KDCoordinate k_indiceHeight = 5; - Type m_type; }; } diff --git a/poincare/src/layout/condensed_sum_layout.cpp b/poincare/src/layout/condensed_sum_layout.cpp index 401c684af..1a4169024 100644 --- a/poincare/src/layout/condensed_sum_layout.cpp +++ b/poincare/src/layout/condensed_sum_layout.cpp @@ -108,6 +108,23 @@ bool CondensedSumLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +bool CondensedSumLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // If the cursor is inside the subscript layout, move it to the superscript. + if (m_subscriptLayout && previousLayout == m_subscriptLayout) { + assert(m_superscriptLayout != nullptr); + return m_superscriptLayout->moveUpInside(cursor); + } + // If the cursor is Left of the base layout, move it to the superscript. + if (m_baseLayout + && previousLayout == m_baseLayout + && cursor->positionIsEquivalentTo(m_baseLayout, ExpressionLayoutCursor::Position::Left)) + { + assert(m_superscriptLayout != nullptr); + return m_superscriptLayout->moveUpInside(cursor); + } + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); +} + void CondensedSumLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { // Nothing to draw } diff --git a/poincare/src/layout/condensed_sum_layout.h b/poincare/src/layout/condensed_sum_layout.h index 6e9ae9dd2..f2aa85bc9 100644 --- a/poincare/src/layout/condensed_sum_layout.h +++ b/poincare/src/layout/condensed_sum_layout.h @@ -16,6 +16,7 @@ public: CondensedSumLayout& operator=(CondensedSumLayout&& other) = delete; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/editable_baseline_relative_layout.cpp b/poincare/src/layout/editable_baseline_relative_layout.cpp index f217e8a33..7078f9b8f 100644 --- a/poincare/src/layout/editable_baseline_relative_layout.cpp +++ b/poincare/src/layout/editable_baseline_relative_layout.cpp @@ -85,6 +85,52 @@ bool EditableBaselineRelativeLayout::moveRight(ExpressionLayoutCursor * cursor) return false; } +bool EditableBaselineRelativeLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // If the baseline is a superscript: + if (m_type == BaselineRelativeLayout::Type::Superscript) { + // If the cursor is Right of the base layout, move it to the indice. + if (m_baseLayout + && previousLayout == m_baseLayout + && cursor->positionIsEquivalentTo(m_baseLayout, ExpressionLayoutCursor::Position::Right)) + { + assert(m_indiceLayout != nullptr); + cursor->setPointedExpressionLayout(m_indiceLayout); + cursor->setPosition(ExpressionLayoutCursor::Position::Left); + cursor->setPositionInside(0); + return true; + } + // If the cursor is Right, move it to the indice. + if (cursor->positionIsEquivalentTo(this, ExpressionLayoutCursor::Position::Right)) { + assert(m_indiceLayout != nullptr); + cursor->setPointedExpressionLayout(m_indiceLayout); + cursor->setPosition(ExpressionLayoutCursor::Position::Right); + cursor->setPositionInside(0); + return true; + } + } + // If the baseline is a subscript: + if (m_type == BaselineRelativeLayout::Type::Subscript + && m_indiceLayout + && previousLayout == m_indiceLayout) + { + // If the cursor is Left of the indice layout, move it to the base. + if (cursor->positionIsEquivalentTo(m_indiceLayout, ExpressionLayoutCursor::Position::Left)) { + assert(m_baseLayout != nullptr); + cursor->setPointedExpressionLayout(m_baseLayout); + cursor->setPosition(ExpressionLayoutCursor::Position::Right); + cursor->setPositionInside(0); + return true; + } + // If the cursor is Right of the indice layout, move it Right. + if (cursor->positionIsEquivalentTo(m_indiceLayout, ExpressionLayoutCursor::Position::Right)) { + cursor->setPointedExpressionLayout(this); + cursor->setPosition(ExpressionLayoutCursor::Position::Right); + cursor->setPositionInside(0); + return true; + } + } + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); +} } diff --git a/poincare/src/layout/editable_baseline_relative_layout.h b/poincare/src/layout/editable_baseline_relative_layout.h index 9839e8628..e9bddbffd 100644 --- a/poincare/src/layout/editable_baseline_relative_layout.h +++ b/poincare/src/layout/editable_baseline_relative_layout.h @@ -10,6 +10,7 @@ public: using BaselineRelativeLayout::BaselineRelativeLayout; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; }; } diff --git a/poincare/src/layout/editable_string_layout.cpp b/poincare/src/layout/editable_string_layout.cpp index 98f085fff..1dfd613b8 100644 --- a/poincare/src/layout/editable_string_layout.cpp +++ b/poincare/src/layout/editable_string_layout.cpp @@ -89,4 +89,29 @@ bool EditableStringLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +void EditableStringLayout::moveCursorInsideAtDirection ( + VerticalDirection direction, + ExpressionLayoutCursor * cursor, + ExpressionLayout ** childResult, + void * resultPosition, + int * resultPositionInside, + int * resultScore) +{ + ExpressionLayout::moveCursorInsideAtDirection(direction, cursor, childResult, resultPosition, resultPositionInside, resultScore); + ExpressionLayoutCursor::Position * castedResultPosition = static_cast(resultPosition); + // Check the distance to Inside cursors. + size_t stringLength = strlen(m_string); + int currentDistance = 0; + KDPoint cursorMiddleLeft = cursor->middleLeftPoint(); + for (int i = 1; i < stringLength; i++) { + currentDistance = cursor->middleLeftPointOfCursor(this, ExpressionLayoutCursor::Position::Inside, i).squareDistanceTo(cursorMiddleLeft); + if (currentDistance < *resultScore) { + *childResult = this; + *castedResultPosition = ExpressionLayoutCursor::Position::Inside; + *resultPositionInside = i; + *resultScore = currentDistance; + } + } +} + } diff --git a/poincare/src/layout/editable_string_layout.h b/poincare/src/layout/editable_string_layout.h index 92e0e9730..75e8b77d9 100644 --- a/poincare/src/layout/editable_string_layout.h +++ b/poincare/src/layout/editable_string_layout.h @@ -11,6 +11,14 @@ public: using StringLayout::StringLayout; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; +private: + void moveCursorInsideAtDirection ( + VerticalDirection direction, + ExpressionLayoutCursor * cursor, + ExpressionLayout ** childResult, + void * resultPosition, + int * resultPositionInside, + int * resultScore) override; }; } diff --git a/poincare/src/layout/expression_layout.cpp b/poincare/src/layout/expression_layout.cpp index 560eed6f8..f3f8a4dfb 100644 --- a/poincare/src/layout/expression_layout.cpp +++ b/poincare/src/layout/expression_layout.cpp @@ -1,6 +1,8 @@ #include #include #include "string_layout.h" +#include +#include namespace Poincare { @@ -57,4 +59,76 @@ void ExpressionLayout::setParent(ExpressionLayout* parent) { m_parent = parent; } +bool ExpressionLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + if (m_parent) { + return m_parent->moveUp(cursor, this, previousLayout); + } + return false; +} + +bool ExpressionLayout::moveUpInside(ExpressionLayoutCursor * cursor) { + return moveInside(VerticalDirection::Up, cursor); +} + +bool ExpressionLayout::moveInside(VerticalDirection direction, ExpressionLayoutCursor * cursor) { + ExpressionLayout * chilResult = nullptr; + ExpressionLayout ** childResultPtr = &chilResult; + ExpressionLayoutCursor::Position resultPosition = ExpressionLayoutCursor::Position::Left; + int resultPositionInside = 0; + // The distance between the cursor and its next position cannot be greater + // than this initial value of score. + int resultScore = Ion::Display::Width*Ion::Display::Width + Ion::Display::Height*Ion::Display::Height; + + moveCursorInsideAtDirection(direction, cursor, childResultPtr, &resultPosition, &resultPositionInside, &resultScore); + + // If there is a valid result + if (*childResultPtr == nullptr) { + return false; + } + cursor->setPointedExpressionLayout(*childResultPtr); + cursor->setPosition(resultPosition); + cursor->setPositionInside(resultPositionInside); + return true; +} + +void ExpressionLayout::moveCursorInsideAtDirection ( + VerticalDirection direction, + ExpressionLayoutCursor * cursor, + ExpressionLayout ** childResult, + void * resultPosition, + int * resultPositionInside, + int * resultScore) +{ + ExpressionLayoutCursor::Position * castedResultPosition = static_cast(resultPosition); + KDPoint cursorMiddleLeft = cursor->middleLeftPoint(); + bool layoutIsUnderOrAbove = direction == VerticalDirection::Up ? m_frame.isAbove(cursorMiddleLeft) : m_frame.isUnder(cursorMiddleLeft); + bool layoutContains = m_frame.contains(cursorMiddleLeft); + + if (layoutIsUnderOrAbove) { + // Check the distance to a Left cursor. + int currentDistance = cursor->middleLeftPointOfCursor(this, ExpressionLayoutCursor::Position::Left, 0).squareDistanceTo(cursorMiddleLeft); + if (currentDistance <= *resultScore ){ + *childResult = this; + *castedResultPosition = ExpressionLayoutCursor::Position::Left; + *resultPositionInside = 0; + *resultScore = currentDistance; + } + + // Check the distance to a Right cursor. + currentDistance = cursor->middleLeftPointOfCursor(this, ExpressionLayoutCursor::Position::Right, 0).squareDistanceTo(cursorMiddleLeft); + if (currentDistance < *resultScore) { + *childResult = this; + *castedResultPosition = ExpressionLayoutCursor::Position::Right; + *resultPositionInside = 0; + *resultScore = currentDistance; + } + } + if (layoutIsUnderOrAbove || layoutContains) { + int childIndex = 0; + while (child(childIndex++)) { + child(childIndex-1)->moveCursorInsideAtDirection(direction, cursor, childResult, castedResultPosition, resultPositionInside, resultScore); + } + } +} + } diff --git a/poincare/src/layout/fraction_layout.cpp b/poincare/src/layout/fraction_layout.cpp index 5f7a0043a..48b0e8778 100644 --- a/poincare/src/layout/fraction_layout.cpp +++ b/poincare/src/layout/fraction_layout.cpp @@ -72,6 +72,20 @@ bool FractionLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +bool FractionLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // If the cursor is inside denominator, move it to the numerator. + if (m_denominator_layout && previousLayout == m_denominator_layout) { + assert(m_numerator_layout != nullptr); + return m_numerator_layout->moveUpInside(cursor); + } + // If the cursor is Left or Right, move it to the numerator. + if (cursor->pointedExpressionLayout() == this){ + assert(m_numerator_layout != nullptr); + return m_numerator_layout->moveUpInside(cursor); + } + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); +} + void FractionLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { KDCoordinate fractionLineY = p.y() + m_numerator_layout->size().height() + k_fractionLineMargin; ctx->fillRect(KDRect(p.x()+k_fractionBorderMargin, fractionLineY, size().width()-2*k_fractionBorderMargin, 1), expressionColor); diff --git a/poincare/src/layout/fraction_layout.h b/poincare/src/layout/fraction_layout.h index 8f777040e..d5a69c9cd 100644 --- a/poincare/src/layout/fraction_layout.h +++ b/poincare/src/layout/fraction_layout.h @@ -16,6 +16,7 @@ public: FractionLayout& operator=(FractionLayout&& other) = delete; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) 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 6df7a62b3..c6110f2ba 100644 --- a/poincare/src/layout/grid_layout.cpp +++ b/poincare/src/layout/grid_layout.cpp @@ -100,6 +100,16 @@ bool GridLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +bool GridLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // If the cursor is child that is not on the top row, move it inside the upper + // neighbourg. + int childIndex = indexOfChild(previousLayout); + if (childIndex >- 1 && !childIsTopOfGrid(childIndex)) { + return m_entryLayouts[childIndex - m_numberOfColumns]->moveUpInside(cursor); + } + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); +} + KDCoordinate GridLayout::rowBaseline(int i) { KDCoordinate rowBaseline = 0; for (int j = 0; j < m_numberOfColumns; j++) { @@ -196,10 +206,15 @@ bool GridLayout::childIsLeftOfGrid(int index) const { assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns); return (index - m_numberOfColumns * (int)(index / m_numberOfColumns)) == 0; } + bool GridLayout::childIsRightOfGrid(int index) const { assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns); return (index - m_numberOfColumns * (int)(index / m_numberOfColumns)) == m_numberOfColumns - 1; } +bool GridLayout::childIsTopOfGrid(int index) const { + assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns); + return index < m_numberOfColumns; +} } diff --git a/poincare/src/layout/grid_layout.h b/poincare/src/layout/grid_layout.h index 0cab38840..e3b6d25f2 100644 --- a/poincare/src/layout/grid_layout.h +++ b/poincare/src/layout/grid_layout.h @@ -17,6 +17,7 @@ public: GridLayout& operator=(GridLayout&& other) = delete; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; @@ -32,6 +33,7 @@ private: int indexOfChild(ExpressionLayout * eL) const; bool childIsLeftOfGrid(int index) const; bool childIsRightOfGrid(int index) const; + bool childIsTopOfGrid(int index) const; ExpressionLayout ** m_entryLayouts; int m_numberOfRows; int m_numberOfColumns; diff --git a/poincare/src/layout/horizontal_layout.cpp b/poincare/src/layout/horizontal_layout.cpp index 429931df8..2635e7648 100644 --- a/poincare/src/layout/horizontal_layout.cpp +++ b/poincare/src/layout/horizontal_layout.cpp @@ -124,6 +124,39 @@ bool HorizontalLayout::moveRight(ExpressionLayoutCursor * cursor) { return m_children_layouts[childIndex+1]->moveRight(cursor); } +bool HorizontalLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // Prevent looping fom child to parent + if (previousPreviousLayout == this) { + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); + } + // If the cursor Left or Right of a child, try moving it up from its brother. + int previousLayoutIndex = indexOfChild(previousLayout); + if (previousLayoutIndex > -1) { + ExpressionLayout * brother = nullptr; + ExpressionLayoutCursor::Position newPosition = ExpressionLayoutCursor::Position::Right; + if (cursor->position() == ExpressionLayoutCursor::Position::Left && previousLayoutIndex > 0) { + brother = m_children_layouts[previousLayoutIndex - 1]; + newPosition = ExpressionLayoutCursor::Position::Right; + } + if (cursor->position() == ExpressionLayoutCursor::Position::Right && previousLayoutIndex < m_number_of_children - 1) { + brother = m_children_layouts[previousLayoutIndex + 1]; + newPosition = ExpressionLayoutCursor::Position::Left; + } + if (brother && cursor->positionIsEquivalentTo(brother, newPosition)) { + ExpressionLayout * previousPointedLayout = cursor->pointedExpressionLayout(); + ExpressionLayoutCursor::Position previousPosition = cursor->position(); + cursor->setPointedExpressionLayout(brother); + cursor->setPosition(newPosition); + if (brother->moveUp(cursor, this, previousLayout)) { + return true; + } + cursor->setPointedExpressionLayout(previousPointedLayout); + cursor->setPosition(previousPosition); + } + } + return ExpressionLayout::moveUp(cursor, previousLayout); +} + void HorizontalLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { } @@ -168,6 +201,9 @@ KDPoint HorizontalLayout::positionOfChild(ExpressionLayout * child) { } int HorizontalLayout::indexOfChild(ExpressionLayout * eL) const { + if (eL == nullptr) { + return -1; + } for (int i = 0; i < m_number_of_children; i++) { if (m_children_layouts[i] == eL) { return i; diff --git a/poincare/src/layout/horizontal_layout.h b/poincare/src/layout/horizontal_layout.h index c081589fb..33f0ba434 100644 --- a/poincare/src/layout/horizontal_layout.h +++ b/poincare/src/layout/horizontal_layout.h @@ -16,6 +16,7 @@ public: HorizontalLayout& operator=(HorizontalLayout&& other) = delete; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/integral_layout.cpp b/poincare/src/layout/integral_layout.cpp index 1ce316836..c1e755fc8 100644 --- a/poincare/src/layout/integral_layout.cpp +++ b/poincare/src/layout/integral_layout.cpp @@ -117,6 +117,23 @@ bool IntegralLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +bool IntegralLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // If the cursor is inside the lower bound, move it to the upper bound. + if (m_lowerBoundLayout && previousLayout == m_lowerBoundLayout) { + assert(m_upperBoundLayout != nullptr); + return m_upperBoundLayout->moveUpInside(cursor); + } + // If the cursor is Left of the integrand, move it to the upper bound. + if (m_integrandLayout + && previousLayout == m_integrandLayout + && cursor->positionIsEquivalentTo(m_integrandLayout, ExpressionLayoutCursor::Position::Left)) + { + assert(m_upperBoundLayout != nullptr); + return m_upperBoundLayout->moveUpInside(cursor); + } + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); +} + void IntegralLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { KDSize integrandSize = m_integrandLayout->size(); KDSize upperBoundSize = m_upperBoundLayout->size(); diff --git a/poincare/src/layout/integral_layout.h b/poincare/src/layout/integral_layout.h index 0a3ad388d..d28d98347 100644 --- a/poincare/src/layout/integral_layout.h +++ b/poincare/src/layout/integral_layout.h @@ -18,6 +18,7 @@ public: constexpr static KDCoordinate k_symbolWidth = 4; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) 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.cpp b/poincare/src/layout/nth_root_layout.cpp index 0e8727fba..238f504b6 100644 --- a/poincare/src/layout/nth_root_layout.cpp +++ b/poincare/src/layout/nth_root_layout.cpp @@ -121,6 +121,31 @@ bool NthRootLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +bool NthRootLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // If the cursor is Left of the radicand, move it to the index. + if (m_radicandLayout + && previousLayout == m_radicandLayout + && cursor->positionIsEquivalentTo(m_radicandLayout, ExpressionLayoutCursor::Position::Left)) + { + assert(m_indexLayout != nullptr); + cursor->setPointedExpressionLayout(m_indexLayout); + cursor->setPosition(ExpressionLayoutCursor::Position::Right); + cursor->setPositionInside(0); + return true; + } + // If the cursor is Left, move it to the index. + if (cursor->pointedExpressionLayout() == this + && cursor->position() == ExpressionLayoutCursor::Position::Left) + { + assert(m_indexLayout != nullptr); + cursor->setPointedExpressionLayout(m_indexLayout); + cursor->setPosition(ExpressionLayoutCursor::Position::Left); + cursor->setPositionInside(0); + return true; + } + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); +} + void NthRootLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { KDSize radicandSize = m_radicandLayout->size(); KDSize indexSize = m_indexLayout != nullptr ? m_indexLayout->size() : KDSize(k_leftRadixWidth,0); diff --git a/poincare/src/layout/nth_root_layout.h b/poincare/src/layout/nth_root_layout.h index b4727b531..14ed3f2f2 100644 --- a/poincare/src/layout/nth_root_layout.h +++ b/poincare/src/layout/nth_root_layout.h @@ -18,6 +18,7 @@ public: constexpr static KDCoordinate k_leftRadixWidth = 5; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; protected: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; KDSize computeSize() override; diff --git a/poincare/src/layout/sequence_layout.cpp b/poincare/src/layout/sequence_layout.cpp index 2ec7658c9..535182d6e 100644 --- a/poincare/src/layout/sequence_layout.cpp +++ b/poincare/src/layout/sequence_layout.cpp @@ -107,6 +107,23 @@ bool SequenceLayout::moveRight(ExpressionLayoutCursor * cursor) { return false; } +bool SequenceLayout::moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) { + // If the cursor is inside the lower bound, move it to the upper bound. + if (m_lowerBoundLayout && previousLayout == m_lowerBoundLayout) { + assert(m_upperBoundLayout != nullptr); + return m_upperBoundLayout->moveUpInside(cursor); + } + // If the cursor is Left of the argument, move it to the upper bound. + if (m_argumentLayout + && previousLayout == m_argumentLayout + && cursor->positionIsEquivalentTo(m_argumentLayout, ExpressionLayoutCursor::Position::Left)) + { + assert(m_upperBoundLayout != nullptr); + return m_upperBoundLayout->moveUpInside(cursor); + } + return ExpressionLayout::moveUp(cursor, previousLayout, previousPreviousLayout); +} + KDSize SequenceLayout::computeSize() { KDSize argumentSize = m_argumentLayout->size(); KDSize lowerBoundSize = m_lowerBoundLayout->size(); diff --git a/poincare/src/layout/sequence_layout.h b/poincare/src/layout/sequence_layout.h index bcef26d56..aaf9f9bb2 100644 --- a/poincare/src/layout/sequence_layout.h +++ b/poincare/src/layout/sequence_layout.h @@ -18,6 +18,7 @@ public: constexpr static KDCoordinate k_symbolWidth = 9; bool moveLeft(ExpressionLayoutCursor * cursor) override; bool moveRight(ExpressionLayoutCursor * cursor) override; + bool moveUp(ExpressionLayoutCursor * cursor, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) override; protected: constexpr static KDCoordinate k_boundHeightMargin = 2; ExpressionLayout * m_lowerBoundLayout;