Files
Upsilon/poincare/src/grid_layout_node.cpp

271 lines
9.1 KiB
C++

#include <poincare/grid_layout_node.h>
#include <poincare/empty_layout_node.h>
#include <poincare/layout_helper.h>
namespace Poincare {
static inline KDCoordinate max(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
// LayoutNode
void GridLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) {
if (cursor->layoutNode() == this && cursor->position() == LayoutCursor::Position::Right) {
// Case: Right. Go to the last entry.
cursor->setLayoutNode(childAtIndex(numberOfChildren() - 1));
return;
}
int childIndex = indexOfChild(cursor->layoutNode());
if (childIndex >= 0) {
// Case: The cursor points to a grid's child.
assert(cursor->position() == LayoutCursor::Position::Left);
if (childIsLeftOfGrid(childIndex)) {
// Case: Left of a child on the left of the grid. Go Left of the grid
cursor->setLayoutNode(this);
return;
}
// Case: Left of another child. Go Right of its sibling on the left.
cursor->setLayoutNode(childAtIndex(childIndex - 1));
cursor->setPosition(LayoutCursor::Position::Right);
return;
}
// Case: Left. Ask the parent.
assert(cursor->layoutNode() == this);
LayoutNode * parentNode = parent();
if (parentNode != nullptr) {
parentNode->moveCursorLeft(cursor, shouldRecomputeLayout);
}
}
void GridLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) {
if (cursor->layoutNode() == this && cursor->position() == LayoutCursor::Position::Left) {
// Case: Left. Go to the first entry.
assert(numberOfChildren() >= 1);
cursor->setLayoutNode(childAtIndex(0));
return;
}
int childIndex = indexOfChild(cursor->layoutNode());
if (childIndex >= 0 && cursor->position() == LayoutCursor::Position::Right) {
// Case: The cursor points to a grid's child.
if (childIsRightOfGrid(childIndex)) {
// Case: Right of a child on the right of the grid. Go Right of the grid.
cursor->setLayoutNode(this);
return;
}
// Case: Right of another child. Go Left of its sibling on the right.
cursor->setLayoutNode(childAtIndex(childIndex + 1));
cursor->setPosition(LayoutCursor::Position::Left);
return;
}
// Case: Right. Ask the parent.
assert(cursor->layoutNode() == this);
LayoutNode * parentNode = parent();
if (parentNode != nullptr) {
parentNode->moveCursorRight(cursor, shouldRecomputeLayout);
}
}
void GridLayoutNode::moveCursorUp(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited) {
/* If the cursor is child that is not on the top row, move it inside its upper
* neighbour.*/
int childIndex = m_numberOfColumns;
while (childIndex < numberOfChildren()) {
if (cursor->layoutNode()->hasAncestor(childAtIndex(childIndex), true)) {
childAtIndex(childIndex - m_numberOfColumns)->moveCursorUpInDescendants(cursor, shouldRecomputeLayout);
return;
}
childIndex++;
}
LayoutNode::moveCursorUp(cursor, shouldRecomputeLayout, equivalentPositionVisited);
}
void GridLayoutNode::moveCursorDown(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited) {
int childIndex = 0;
while (childIndex < numberOfChildren() - m_numberOfColumns) {
if (cursor->layoutNode()->hasAncestor(childAtIndex(childIndex), true)) {
childAtIndex(childIndex + m_numberOfColumns)->moveCursorDownInDescendants(cursor, shouldRecomputeLayout);
return;
}
childIndex++;
}
LayoutNode::moveCursorDown(cursor, shouldRecomputeLayout, equivalentPositionVisited);
}
void GridLayoutNode::didAddChildAtIndex(int newNumberOfChildren) {
GridLayoutRef(this).setDimensions(1, newNumberOfChildren);
}
// Protected
void GridLayoutNode::addEmptyRow(EmptyLayoutNode::Color color) {
GridLayoutRef thisRef = GridLayoutRef(this);
int previousNumberOfChildren = numberOfChildren();
int columnsCount = m_numberOfColumns;
int previousRowCount = m_numberOfRows;
for (int i = 0; i < columnsCount; i++) {
thisRef.addChildAtIndex(
EmptyLayoutRef(color),
previousNumberOfChildren,
previousNumberOfChildren + i,
nullptr);
// WARNING: Do not access "this" afterwards
}
thisRef.setDimensions(previousRowCount + 1, columnsCount);
}
void GridLayoutNode::addEmptyColumn(EmptyLayoutNode::Color color) {
GridLayoutRef thisRef = GridLayoutRef(this);
int previousNumberOfChildren = numberOfChildren();
int rowsCount = m_numberOfRows;
int futureColumnsCount = m_numberOfColumns + 1;
for (int i = 0; i < rowsCount; i++) {
thisRef.addChildAtIndex(
EmptyLayoutRef(color),
i*futureColumnsCount + futureColumnsCount-1,
previousNumberOfChildren + i,
nullptr);
// WARNING: Do not access "this" afterwards
}
thisRef.setDimensions(rowsCount, futureColumnsCount);
}
void GridLayoutNode::deleteRowAtIndex(int index) {
assert(index >= 0 && index < m_numberOfRows);
LayoutRef thisRef = LayoutRef(this);
for (int i = 0; i < m_numberOfColumns; i++) {
thisRef.removeChildAtIndexInPlace(index * m_numberOfColumns);
}
m_numberOfRows--;
}
void GridLayoutNode::deleteColumnAtIndex(int index) {
assert(index >= 0 && index < m_numberOfColumns);
LayoutRef thisRef = LayoutRef(this);
for (int i = (m_numberOfRows - 1) * m_numberOfColumns + index; i > -1; i-= m_numberOfColumns) {
thisRef.removeChildAtIndexInPlace(i);
}
m_numberOfColumns--;
}
bool GridLayoutNode::childIsLeftOfGrid(int index) const {
assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns);
return columnAtChildIndex(index) == 0;
}
bool GridLayoutNode::childIsRightOfGrid(int index) const {
assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns);
return columnAtChildIndex(index) == m_numberOfColumns - 1;
}
bool GridLayoutNode::childIsTopOfGrid(int index) const {
assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns);
return rowAtChildIndex(index) == 0;
}
bool GridLayoutNode::childIsBottomOfGrid(int index) const {
assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns);
return rowAtChildIndex(index) == m_numberOfRows - 1;
}
int GridLayoutNode::rowAtChildIndex(int index) const {
assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns);
return (int)(index / m_numberOfColumns);
}
int GridLayoutNode::columnAtChildIndex(int index) const {
assert(index >= 0 && index < m_numberOfRows*m_numberOfColumns);
return index - m_numberOfColumns * rowAtChildIndex(index);
}
int GridLayoutNode::indexAtRowColumn(int rowIndex, int columnIndex) const {
assert(rowIndex >= 0 && rowIndex < m_numberOfRows);
assert(columnIndex >= 0 && columnIndex < m_numberOfColumns);
return rowIndex * m_numberOfColumns + columnIndex;
}
KDSize GridLayoutNode::computeSize() {
return gridSize();
}
KDCoordinate GridLayoutNode::computeBaseline() {
return (height()+1)/2;
}
KDPoint GridLayoutNode::positionOfChild(LayoutNode * l) {
int rowIndex = 0;
int columnIndex = 0;
for (int i = 0; i < m_numberOfRows; i++) {
for (int j = 0; j < m_numberOfColumns; j++) {
if (l == childAtIndex(i*m_numberOfColumns+j)) {
rowIndex = i;
columnIndex = j;
break;
}
}
}
KDCoordinate x = 0;
for (int j = 0; j < columnIndex; j++) {
x += columnWidth(j);
}
x += (columnWidth(columnIndex) - l->layoutSize().width())/2+ columnIndex * k_gridEntryMargin;
KDCoordinate y = 0;
for (int i = 0; i < rowIndex; i++) {
y += rowHeight(i);
}
y += rowBaseline(rowIndex) - l->baseline() + rowIndex * k_gridEntryMargin;
return KDPoint(x, y);
}
// Private
KDCoordinate GridLayoutNode::rowBaseline(int i) {
KDCoordinate rowBaseline = 0;
for (int j = 0; j < m_numberOfColumns; j++) {
rowBaseline = max(rowBaseline, childAtIndex(i*m_numberOfColumns+j)->baseline());
}
return rowBaseline;
}
KDCoordinate GridLayoutNode::rowHeight(int i) const {
KDCoordinate rowHeight = 0;
KDCoordinate baseline = const_cast<GridLayoutNode *>(this)->rowBaseline(i);
for (int j = 0; j < m_numberOfColumns; j++) {
LayoutNode * currentChild = const_cast<GridLayoutNode *>(this)->childAtIndex(i*m_numberOfColumns+j);
rowHeight = max(rowHeight, currentChild->layoutSize().height() - currentChild->baseline());
}
return baseline+rowHeight;
}
KDCoordinate GridLayoutNode::height() const {
KDCoordinate totalHeight = 0;
for (int i = 0; i < m_numberOfRows; i++) {
totalHeight += rowHeight(i);
}
totalHeight += (m_numberOfRows-1)*k_gridEntryMargin;
return totalHeight;
}
KDCoordinate GridLayoutNode::columnWidth(int j) const {
KDCoordinate columnWidth = 0;
for (int i = 0; i < m_numberOfRows; i++) {
columnWidth = max(columnWidth, const_cast<GridLayoutNode *>(this)->childAtIndex(i*m_numberOfColumns+j)->layoutSize().width());
}
return columnWidth;
}
KDCoordinate GridLayoutNode::width() const {
KDCoordinate totalWidth = 0;
for (int j = 0; j < m_numberOfColumns; j++) {
totalWidth += columnWidth(j);
}
totalWidth += (m_numberOfColumns-1)*k_gridEntryMargin;
return totalWidth;
}
// Grid Layout Reference
void GridLayoutRef::setDimensions(int rows, int columns) {
assert(rows * columns == numberOfChildren());
setNumberOfRows(rows);
setNumberOfColumns(columns);
}
}