Files
Upsilon/poincare/src/layout/vertical_offset_layout.cpp
Léa Saviot 40ef1d1caa [poincare] Fix cursor position bug when deleting VerticalOffsetLayout.
Change-Id: Ie53030e5bff6876c75a785a61f6c43604bee2b05
2018-01-29 14:10:38 +01:00

273 lines
9.8 KiB
C++

#include "vertical_offset_layout.h"
#include "empty_visible_layout.h"
#include <ion/charset.h>
#include <poincare/expression_layout_cursor.h>
#include <poincare/layout_engine.h>
#include <string.h>
#include <assert.h>
namespace Poincare {
VerticalOffsetLayout::VerticalOffsetLayout(ExpressionLayout * indice, Type type, bool cloneOperands) :
StaticLayoutHierarchy(indice, cloneOperands),
m_type(type)
{
}
ExpressionLayout * VerticalOffsetLayout::clone() const {
VerticalOffsetLayout * layout = new VerticalOffsetLayout(const_cast<VerticalOffsetLayout *>(this)->indiceLayout(), m_type, true);
return layout;
}
void VerticalOffsetLayout::backspaceAtCursor(ExpressionLayoutCursor * cursor) {
if (cursor->pointedExpressionLayout() == indiceLayout()) {
assert(cursor->position() == ExpressionLayoutCursor::Position::Left);
ExpressionLayout * base = baseLayout();
if (indiceLayout()->isEmpty()) {
int indexInParent = m_parent->indexOfChild(this);
if (base->isEmpty()) {
// Case: Empty base and indice.
// Remove both the base and the indice layout.
ExpressionLayout * parent = m_parent;
cursor->setPointedExpressionLayout(this);
cursor->setPosition(ExpressionLayoutCursor::Position::Right);
parent->removePointedChildAtIndexAndMoveCursor(indexInParent, true, cursor);
parent->removePointedChildAtIndexAndMoveCursor(indexInParent-1, true, cursor);
return;
}
// Case: Empty indice only.
// Delete the layout.
cursor->setPointedExpressionLayout(base);
cursor->setPosition(ExpressionLayoutCursor::Position::Right);
m_parent->removeChildAtIndex(indexInParent, true);
return;
}
// Case: Non-empty indice.
// Move Left of the VerticalOffsetLayout.
cursor->setPointedExpressionLayout(this);
cursor->setPosition(ExpressionLayoutCursor::Position::Left);
return;
}
if (cursor->pointedExpressionLayout() == this
&& cursor->position() == ExpressionLayoutCursor::Position::Right)
{
// Case: Right.
// Move to the indice.
cursor->setPointedExpressionLayout(indiceLayout());
return;
}
ExpressionLayout::backspaceAtCursor(cursor);
}
bool VerticalOffsetLayout::moveLeft(ExpressionLayoutCursor * cursor, bool * shouldRecomputeLayout) {
// Case: Left of the indice.
// Go Left.
if (indiceLayout()
&& cursor->pointedExpressionLayout() == indiceLayout()
&& cursor->position() == ExpressionLayoutCursor::Position::Left)
{
cursor->setPointedExpressionLayout(this);
cursor->setPosition(ExpressionLayoutCursor::Position::Left);
return true;
}
assert(cursor->pointedExpressionLayout() == this);
// Case: Right.
// Go to the indice.
if (cursor->position() == ExpressionLayoutCursor::Position::Right) {
assert(indiceLayout() != nullptr);
cursor->setPointedExpressionLayout(indiceLayout());
return true;
}
// Case: Left.
// Ask the parent.
assert(cursor->position() == ExpressionLayoutCursor::Position::Left);
if (m_parent) {
return m_parent->moveLeft(cursor, shouldRecomputeLayout);
}
return false;
}
bool VerticalOffsetLayout::moveRight(ExpressionLayoutCursor * cursor, bool * shouldRecomputeLayout) {
// Case: Right of the indice.
// Go Right.
if (indiceLayout()
&& cursor->pointedExpressionLayout() == indiceLayout()
&& cursor->position() == ExpressionLayoutCursor::Position::Right)
{
cursor->setPointedExpressionLayout(this);
return true;
}
assert(cursor->pointedExpressionLayout() == this);
// Case: Left.
// Go to the indice.
if (cursor->position() == ExpressionLayoutCursor::Position::Left) {
assert(indiceLayout() != nullptr);
cursor->setPointedExpressionLayout(indiceLayout());
return true;
}
// Case: Right.
// Ask the parent.
assert(cursor->position() == ExpressionLayoutCursor::Position::Right);
if (m_parent) {
return m_parent->moveRight(cursor, shouldRecomputeLayout);
}
return false;
}
bool VerticalOffsetLayout::moveUp(ExpressionLayoutCursor * cursor, bool * shouldRecomputeLayout, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) {
// Case: Superscript.
if (m_type == VerticalOffsetLayout::Type::Superscript) {
// Case: Right.
// Move to the indice.
if (cursor->positionIsEquivalentTo(this, ExpressionLayoutCursor::Position::Right)) {
assert(indiceLayout() != nullptr);
cursor->setPointedExpressionLayout(indiceLayout());
cursor->setPosition(ExpressionLayoutCursor::Position::Right);
return true;
}
// Case: Left.
// Move to the indice.
if (cursor->positionIsEquivalentTo(this, ExpressionLayoutCursor::Position::Left)) {
assert(indiceLayout() != nullptr);
cursor->setPointedExpressionLayout(indiceLayout());
cursor->setPosition(ExpressionLayoutCursor::Position::Left);
return true;
}
}
// Case: Subscript.
// Case: Left or Right of the indice.
// Put the cursor at the same position, pointing this.
if (m_type == VerticalOffsetLayout::Type::Subscript
&& indiceLayout() != nullptr
&& (cursor->positionIsEquivalentTo(indiceLayout(), ExpressionLayoutCursor::Position::Left)
|| cursor->positionIsEquivalentTo(indiceLayout(), ExpressionLayoutCursor::Position::Right)))
{
cursor->setPointedExpressionLayout(this);
return true;
}
return ExpressionLayout::moveUp(cursor, shouldRecomputeLayout, previousLayout, previousPreviousLayout);
}
bool VerticalOffsetLayout::moveDown(ExpressionLayoutCursor * cursor, bool * shouldRecomputeLayout, ExpressionLayout * previousLayout, ExpressionLayout * previousPreviousLayout) {
// Case: Subscript.
if (m_type == VerticalOffsetLayout::Type::Subscript) {
// Case: Right.
// Move to the indice.
if (cursor->positionIsEquivalentTo(this, ExpressionLayoutCursor::Position::Right)) {
assert(indiceLayout() != nullptr);
cursor->setPointedExpressionLayout(indiceLayout());
cursor->setPosition(ExpressionLayoutCursor::Position::Right);
return true;
}
// Case: Left.
// Move to the indice.
if (cursor->positionIsEquivalentTo(this, ExpressionLayoutCursor::Position::Left)) {
assert(indiceLayout() != nullptr);
cursor->setPointedExpressionLayout(indiceLayout());
cursor->setPosition(ExpressionLayoutCursor::Position::Left);
return true;
}
}
// Case: Superscript.
// Case: Left or Right of the indice.
// Put the cursor at the same position, pointing this.
if (m_type == VerticalOffsetLayout::Type::Superscript
&& indiceLayout() != nullptr
&& cursor->pointedExpressionLayout() == indiceLayout())
{
cursor->setPointedExpressionLayout(this);
return true;
}
return ExpressionLayout::moveDown(cursor, shouldRecomputeLayout, previousLayout, previousPreviousLayout);
}
int VerticalOffsetLayout::writeTextInBuffer(char * buffer, int bufferSize) const {
if (m_type == Type::Subscript) {
if (bufferSize == 0) {
return -1;
}
buffer[bufferSize-1] = 0;
if (bufferSize == 1) {
return 0;
}
// If the layout is a subscript, write "_{indice}"
int numberOfChar = LayoutEngine::writeOneCharInBuffer(buffer, bufferSize, '_');
if (numberOfChar >= bufferSize-1) { return bufferSize-1; }
numberOfChar += LayoutEngine::writeOneCharInBuffer(buffer+numberOfChar, bufferSize-numberOfChar, '{');
if (numberOfChar >= bufferSize-1) { return bufferSize-1; }
numberOfChar += const_cast<VerticalOffsetLayout *>(this)->indiceLayout()->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar);
if (numberOfChar >= bufferSize-1) { return bufferSize-1; }
numberOfChar += LayoutEngine::writeOneCharInBuffer(buffer+numberOfChar, bufferSize-numberOfChar, '}');
if (numberOfChar >= bufferSize-1) { return bufferSize-1; }
return numberOfChar;
}
assert(m_type == Type::Superscript);
// If the layout is a superscript, write "^(indice)"
int numberOfChar = LayoutEngine::writePrefixExpressionLayoutTextInBuffer(this, buffer, bufferSize, "^");
// Add a multiplication if omitted.
int indexInParent = -1;
if (m_parent) {
indexInParent = m_parent->indexOfChild(this);
}
if (indexInParent >= 0 && indexInParent < (m_parent->numberOfChildren() - 1) && m_parent->isHorizontal() && m_parent->child(indexInParent + 1)->canBeOmittedMultiplicationRightFactor()) {
buffer[numberOfChar++] = Ion::Charset::MiddleDot;
if (numberOfChar >= bufferSize-1) { return bufferSize-1;}
}
buffer[numberOfChar] = 0;
return numberOfChar;
}
ExpressionLayout * VerticalOffsetLayout::indiceLayout() {
return editableChild(0);
}
ExpressionLayout * VerticalOffsetLayout::baseLayout() {
int indexInParent = parent()->indexOfChild(this);
assert(indexInParent > 0);
return editableParent()->editableChild(indexInParent - 1);
}
void VerticalOffsetLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) {
// There is nothing to draw for a subscript/superscript, only the position of the child matters
}
KDSize VerticalOffsetLayout::computeSize() {
KDSize indiceSize = indiceLayout()->size();
KDCoordinate width = indiceSize.width();
KDCoordinate height = 0;
if (m_type == Type::Subscript) {
height = positionOfChild(indiceLayout()).y()+ indiceLayout()->size().height();
} else {
height = indiceLayout()->size().height() + baseLayout()->baseline() - k_indiceHeight;
}
return KDSize(width, height);
}
void VerticalOffsetLayout::computeBaseline() {
if (m_type == Type::Subscript) {
m_baseline = 0;
} else {
m_baseline = size().height();
}
m_baselined = true;
}
KDPoint VerticalOffsetLayout::positionOfChild(ExpressionLayout * child) {
assert(child == indiceLayout());
if (m_type == Type::Superscript) {
return KDPointZero;
}
assert(m_type == Type::Subscript);
ExpressionLayout * base = baseLayout();
return KDPoint(0, base->size().height() - base->baseline() - k_indiceHeight);
}
}