[expression_editor/poincare] Move Up in an ExpressionLayout.

Change-Id: I75fd91233729afd4eb77d8e9d53c102a632643ea
This commit is contained in:
Léa Saviot
2017-12-15 18:10:14 +01:00
parent 96c2f9aeba
commit fc8cd6c06e
29 changed files with 372 additions and 5 deletions

View File

@@ -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()));
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -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;
};

View File

@@ -2,6 +2,7 @@
#define POINCARE_EXPRESSION_LAYOUT_CURSOR_H
#include <poincare/expression_layout.h>
#include <kandinsky/point.h>
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;

View File

@@ -1,4 +1,6 @@
#include <poincare/expression_layout_cursor.h>
#include <poincare/src/layout/string_layout.h>
#include <assert.h>
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);
}
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
};
}

View File

@@ -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<ExpressionLayoutCursor::Position *>(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;
}
}
}
}

View File

@@ -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;
};
}

View File

@@ -1,6 +1,8 @@
#include <assert.h>
#include <stdlib.h>
#include "string_layout.h"
#include <poincare/expression_layout_cursor.h>
#include <ion/display.h>
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<ExpressionLayoutCursor::Position *>(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);
}
}
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;