Files
Upsilon/poincare/src/bracket_layout.cpp

153 lines
5.8 KiB
C++

#include <poincare/bracket_layout.h>
#include <escher/metric.h>
#include <assert.h>
#include <stdlib.h>
namespace Poincare {
static inline KDCoordinate max(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
void BracketLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) {
assert(cursor->layoutNode() == this);
if (cursor->position() == LayoutCursor::Position::Right) {
// Case: Right. Go Left.
cursor->setPosition(LayoutCursor::Position::Left);
return;
}
assert(cursor->position() == LayoutCursor::Position::Left);
// Case: Left. Ask the parent.
LayoutNode * parentLayout = parent();
if (parentLayout != nullptr) {
parentLayout->moveCursorLeft(cursor, shouldRecomputeLayout);
}
}
void BracketLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) {
assert(cursor->layoutNode() == this);
if (cursor->position() == LayoutCursor::Position::Left) {
// Case: Left. Go Right.
cursor->setPosition(LayoutCursor::Position::Right);
return;
}
assert(cursor->position() == LayoutCursor::Position::Right);
// Case: Right. Ask the parent.
LayoutNode * parentLayout = parent();
if (parentLayout != nullptr) {
parentLayout->moveCursorRight(cursor, shouldRecomputeLayout);
}
}
void BracketLayoutNode::invalidAllSizesPositionsAndBaselines() {
m_childHeightComputed = false;
LayoutNode::invalidAllSizesPositionsAndBaselines();
}
KDCoordinate BracketLayoutNode::computeBaseline() {
LayoutNode * parentLayout = parent();
assert(parentLayout != nullptr);
int idxInParent = parentLayout->indexOfChild(this);
int numberOfSiblings = parentLayout->numberOfChildren();
if (((isLeftParenthesis() || isLeftBracket()) && idxInParent == numberOfSiblings - 1)
|| ((isRightParenthesis() || isRightBracket()) && idxInParent == 0)
|| ((isLeftParenthesis() || isLeftBracket()) && idxInParent < numberOfSiblings - 1 && parentLayout->childAtIndex(idxInParent + 1)->isVerticalOffset()))
{
/* The bracket does not have siblings on its open direction, or it is a left
* bracket that is base of a superscript layout. In the latter case, it
* should have a default baseline, else it creates an infinite loop as the
* bracket needs the superscript baseline, which needs the bracket baseline.*/
return layoutSize().height()/2;
}
int currentNumberOfOpenBrackets = 1;
KDCoordinate result = 0;
int increment = (isLeftParenthesis() || isLeftBracket()) ? 1 : -1;
for (int i = idxInParent + increment; i >= 0 && i < numberOfSiblings; i+=increment) {
LayoutNode * sibling = parentLayout->childAtIndex(i);
if ((isLeftParenthesis() && sibling->isRightParenthesis())
|| (isLeftBracket() && sibling->isRightBracket())
|| (isRightParenthesis() && sibling->isLeftParenthesis())
|| (isRightBracket() && sibling->isLeftBracket()))
{
if (i == idxInParent + increment) {
/* If the bracket is immediately closed, we set the baseline to half the
* bracket height. */
return layoutSize().height()/2;
}
currentNumberOfOpenBrackets--;
if (currentNumberOfOpenBrackets == 0) {
break;
}
} else if ((isLeftParenthesis() && sibling->isLeftParenthesis())
|| (isLeftBracket() && sibling->isLeftBracket())
|| (isRightParenthesis() && sibling->isRightParenthesis())
|| (isRightBracket() && sibling->isRightBracket()))
{
currentNumberOfOpenBrackets++;
}
result = max(result, sibling->baseline());
}
return result + (layoutSize().height() - childHeight()) / 2;
}
KDCoordinate BracketLayoutNode::childHeight() {
if (!m_childHeightComputed) {
m_childHeight = computeChildHeight();
m_childHeightComputed = true;
}
return m_childHeight;
}
KDCoordinate BracketLayoutNode::computeChildHeight() {
LayoutNode * parentLayout = parent();
assert(parentLayout != nullptr);
KDCoordinate result = Metric::MinimalBracketAndParenthesisHeight;
int idxInParent = parentLayout->indexOfChild(this);
int numberOfSiblings = parentLayout->numberOfChildren();
if ((isLeftParenthesis() || isLeftBracket())
&& idxInParent < numberOfSiblings - 1
&& parentLayout->childAtIndex(idxInParent + 1)->isVerticalOffset())
{
/* If a left bracket is the base of a superscript layout, it should have a
* a default height, else it creates an infinite loop because the bracket
* needs the superscript height, which needs the bracket height. */
return result;
}
KDCoordinate maxUnderBaseline = 0;
KDCoordinate maxAboveBaseline = 0;
int currentNumberOfOpenBrackets = 1;
int increment = (isLeftParenthesis() || isLeftBracket()) ? 1 : -1;
for (int i = idxInParent + increment; i >= 0 && i < numberOfSiblings; i+= increment) {
LayoutNode * sibling = parentLayout->childAtIndex(i);
if ((isLeftParenthesis() && sibling->isRightParenthesis())
|| (isLeftBracket() && sibling->isRightBracket())
|| (isRightParenthesis() && sibling->isLeftParenthesis())
|| (isRightBracket() && sibling->isLeftBracket()))
{
currentNumberOfOpenBrackets--;
if (currentNumberOfOpenBrackets == 0) {
break;
}
} else if ((isLeftParenthesis() && sibling->isLeftParenthesis())
|| (isLeftBracket() && sibling->isLeftBracket())
|| (isRightParenthesis() && sibling->isRightParenthesis())
|| (isRightBracket() && sibling->isRightBracket()))
{
currentNumberOfOpenBrackets++;
}
KDCoordinate siblingHeight = sibling->layoutSize().height();
KDCoordinate siblingBaseline = sibling->baseline();
maxUnderBaseline = max(maxUnderBaseline, siblingHeight - siblingBaseline);
maxAboveBaseline = max(maxAboveBaseline, siblingBaseline);
}
return max(result, maxUnderBaseline + maxAboveBaseline);
}
KDPoint BracketLayoutNode::positionOfChild(LayoutNode * child) {
assert(false);
return KDPointZero;
}
}