[escher/layout_field] Better handling of layout recomputation

After a selection event, there is no need to recompute the layout. We
still need to dirty the union of the previous and next selection
rectangles.
This commit is contained in:
Léa Saviot
2019-12-23 17:32:22 +01:00
parent f9b738a41e
commit c29ab14f20
7 changed files with 62 additions and 30 deletions

View File

@@ -86,6 +86,7 @@ private:
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
void layoutCursorSubview(bool force);
KDRect computeSelectionRect() const;
Poincare::LayoutCursor m_cursor;
ExpressionView m_expressionView;
TextCursorView m_cursorView;

View File

@@ -56,6 +56,7 @@ bool IsBefore(Layout& l1, Layout& l2, bool strict) {
}
void LayoutField::ContentView::addSelection(Layout addedLayout) {
KDRect rectBefore = computeSelectionRect();
if (selectionIsEmpty()) {
/*
* ---------- -> +++ is the previous previous selection
@@ -121,7 +122,10 @@ void LayoutField::ContentView::addSelection(Layout addedLayout) {
}
}
}
//reloadRectFromAndToPositions(left, right); TODO LEA
KDRect rectAfter = computeSelectionRect();
// We need to update the background color for selected/unselected layouts
markRectAsDirty(rectBefore.unionedWith(rectAfter));
}
bool LayoutField::ContentView::resetSelection() {
@@ -222,6 +226,20 @@ void LayoutField::ContentView::layoutCursorSubview(bool force) {
m_cursorView.setFrame(KDRect(cursorTopLeftPosition, LayoutCursor::k_cursorWidth, m_cursor.cursorHeight()), force);
}
KDRect LayoutField::ContentView::computeSelectionRect() const {
if (selectionIsEmpty()) {
return KDRectZero;
}
if (m_selectionStart == m_selectionEnd) {
return KDRect(m_selectionStart.absoluteOrigin(), m_selectionStart.layoutSize());
}
Layout selectionParent = m_selectionStart.parent();
assert(m_selectionEnd.parent() == selectionParent);
assert(selectionParent.type() == LayoutNode::Type::HorizontalLayout);
KDRect selectionRectInParent = static_cast<HorizontalLayout &>(selectionParent).relativeSelectionRect(&m_selectionStart, &m_selectionEnd);
return selectionRectInParent.translatedBy(selectionParent.absoluteOrigin());
}
void LayoutField::setEditing(bool isEditing) {
KDSize previousLayoutSize = m_contentView.minimalSizeForOptimalDisplay();
if (m_contentView.setEditing(isEditing)) {
@@ -317,7 +335,6 @@ bool LayoutField::handleEvent(Ion::Events::Event event) {
didHandleEvent = true;
} else if (privateHandleSelectionEvent(event, &shouldRecomputeLayout)) {
didHandleEvent = true;
shouldRecomputeLayout = true; //TODO LEA
} else if (privateHandleEvent(event)) {
shouldRecomputeLayout = true;
didHandleEvent = true;

View File

@@ -8,9 +8,11 @@ namespace Poincare {
/* WARNING: A HorizontalLayout should never have a HorizontalLayout child. For
* instance, use addOrMergeChildAtIndex to add a LayoutNode safely. */
class HorizontalLayout;
class HorizontalLayoutNode final : public LayoutNode {
friend class Layout;
friend class HorizontalLayout;
public:
HorizontalLayoutNode() :
@@ -55,6 +57,7 @@ protected:
KDSize computeSize() override;
KDCoordinate computeBaseline() override;
KDPoint positionOfChild(LayoutNode * l) override;
KDRect relativeSelectionRect(const Layout * selectionStart, const Layout * selectionEnd) const;
private:
bool willAddChildAtIndex(LayoutNode * l, int * index, int * currentNumberOfChildren, LayoutCursor * cursor) override;
@@ -93,6 +96,8 @@ public:
Layout squashUnaryHierarchyInPlace();
void serializeChildren(int firstIndex, int lastIndex, char * buffer, int bufferSize);
KDRect relativeSelectionRect(const Layout * selectionStart, const Layout * selectionEnd) const { return static_cast<HorizontalLayoutNode *>(node())->relativeSelectionRect(selectionStart, selectionEnd); }
private:
void removeEmptyChildBeforeInsertionAtIndex(int * index, int * currentNumberOfChildren, bool shouldRemoveOnLeft, LayoutCursor * cursor = nullptr);
};

View File

@@ -37,8 +37,8 @@ public:
void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor, Layout * selectionStart = nullptr, Layout * selectionEnd = nullptr, KDColor selectionColor = KDColorRed) {
return node()->render(ctx, p, expressionColor, backgroundColor, selectionStart, selectionEnd, selectionColor);
}
KDSize layoutSize() { return node()->layoutSize(); }
KDPoint absoluteOrigin() { return node()->absoluteOrigin(); }
KDSize layoutSize() const { return node()->layoutSize(); }
KDPoint absoluteOrigin() const { return node()->absoluteOrigin(); }
KDCoordinate baseline() { return node()->baseline(); }
void invalidAllSizesPositionsAndBaselines() { return node()->invalidAllSizesPositionsAndBaselines(); }

View File

@@ -56,8 +56,8 @@ public:
}
/* Comparison */
inline bool operator==(const TreeHandle& t) { return m_identifier == t.identifier(); }
inline bool operator!=(const TreeHandle& t) { return m_identifier != t.identifier(); }
inline bool operator==(const TreeHandle& t) const { return m_identifier == t.identifier(); }
inline bool operator!=(const TreeHandle& t) const { return m_identifier != t.identifier(); }
/* Clone */
TreeHandle clone() const;

View File

@@ -287,6 +287,37 @@ KDPoint HorizontalLayoutNode::positionOfChild(LayoutNode * l) {
return KDPoint(x, y);
}
KDRect HorizontalLayoutNode::relativeSelectionRect(const Layout * selectionStart, const Layout * selectionEnd) const {
assert(selectionStart != nullptr && !selectionStart->isUninitialized());
assert(selectionEnd != nullptr && !selectionEnd->isUninitialized());
HorizontalLayout thisLayout = HorizontalLayout(const_cast<HorizontalLayoutNode *>(this));
assert(thisLayout.hasChild(*selectionStart));
assert(thisLayout.hasChild(*selectionEnd));
assert(thisLayout.indexOfChild(*selectionStart) <= thisLayout.indexOfChild(*selectionEnd));
// Compute the positions
KDCoordinate selectionXStart = const_cast<HorizontalLayoutNode *>(this)->positionOfChild(selectionStart->node()).x();
KDCoordinate selectionXEnd = const_cast<HorizontalLayoutNode *>(this)->positionOfChild(selectionEnd->node()).x() + selectionEnd->layoutSize().width();
KDCoordinate drawWidth = selectionXEnd - selectionXStart;
// Compute the height
int firstSelectedNodeIndex = thisLayout.indexOfChild(*selectionStart);
int secondSelectedNodeIndex = thisLayout.indexOfChild(*selectionEnd);
if (firstSelectedNodeIndex == 0 && secondSelectedNodeIndex == numberOfChildren() - 1) {
return KDRect(KDPointZero, const_cast<HorizontalLayoutNode *>(this)->layoutSize());
}
KDCoordinate maxUnderBaseline = 0;
KDCoordinate maxAboveBaseline = 0;
for (int i = firstSelectedNodeIndex; i <= secondSelectedNodeIndex; i++) {
Layout childi = thisLayout.childAtIndex(i);
KDSize childSize = childi.layoutSize();
maxUnderBaseline = maxCoordinate(maxUnderBaseline, childSize.height() - childi.baseline());
maxAboveBaseline = maxCoordinate(maxAboveBaseline, childi.baseline());
}
return KDRect(KDPoint(selectionXStart, const_cast<HorizontalLayoutNode *>(this)->baseline() - maxAboveBaseline), KDSize(drawWidth, maxUnderBaseline + maxAboveBaseline));
}
// Private
bool HorizontalLayoutNode::willAddChildAtIndex(LayoutNode * l, int * index, int * currentNumberOfChildren, LayoutCursor * cursor) {
@@ -427,29 +458,8 @@ void HorizontalLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expression
&& thisLayout.hasChild(*selectionStart);
if (childrenAreSelected) {
assert(thisLayout.hasChild(*selectionEnd));
// Compute the positions
KDCoordinate selectionXStart = positionOfChild(selectionStart->node()).x();
KDCoordinate selectionXEnd = positionOfChild(selectionEnd->node()).x() + selectionEnd->layoutSize().width();
KDCoordinate drawX = p.x() + selectionXStart;
KDCoordinate drawWidth = selectionXEnd - selectionXStart;
// Compute the height
int firstSelectedNodeIndex = thisLayout.indexOfChild(*selectionStart);
int secondSelectedNodeIndex = thisLayout.indexOfChild(*selectionEnd);
if (firstSelectedNodeIndex == 0 && secondSelectedNodeIndex == numberOfChildren() - 1) {
ctx->fillRect(KDRect(KDPoint(drawX, p.y()), KDSize(drawWidth, s.height())), selectionColor);
return;
}
KDCoordinate maxUnderBaseline = 0;
KDCoordinate maxAboveBaseline = 0;
for (int i = firstSelectedNodeIndex; i <= secondSelectedNodeIndex; i++) {
Layout childi = thisLayout.childAtIndex(i);
KDSize childSize = childi.layoutSize();
maxUnderBaseline = maxCoordinate(maxUnderBaseline, childSize.height() - childi.baseline());
maxAboveBaseline = maxCoordinate(maxAboveBaseline, childi.baseline());
}
ctx->fillRect(KDRect(KDPoint(drawX, p.y() + baseline() - maxAboveBaseline), KDSize(drawWidth, maxUnderBaseline + maxAboveBaseline)), selectionColor);
KDRect selectionRectangle = HorizontalLayout(this).relativeSelectionRect(selectionStart, selectionEnd);
ctx->fillRect(selectionRectangle.translatedBy(p), selectionColor);
}
}

View File

@@ -80,7 +80,6 @@ void LayoutCursor::select(Direction direction, bool * shouldRecomputeLayout, Lay
} else {
selectUpDown(direction == Direction::Up, shouldRecomputeLayout, selection);
}
*shouldRecomputeLayout = true;
}
/* Layout modification */