diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 05fcd88a5..0c2d2edf9 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -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; diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index b9989b779..7091697bf 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -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(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; diff --git a/poincare/include/poincare/horizontal_layout.h b/poincare/include/poincare/horizontal_layout.h index e8842f556..381965694 100644 --- a/poincare/include/poincare/horizontal_layout.h +++ b/poincare/include/poincare/horizontal_layout.h @@ -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(node())->relativeSelectionRect(selectionStart, selectionEnd); } private: void removeEmptyChildBeforeInsertionAtIndex(int * index, int * currentNumberOfChildren, bool shouldRemoveOnLeft, LayoutCursor * cursor = nullptr); }; diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index ce2894900..f53582fc8 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -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(); } diff --git a/poincare/include/poincare/tree_handle.h b/poincare/include/poincare/tree_handle.h index 254a78618..68f99b41a 100644 --- a/poincare/include/poincare/tree_handle.h +++ b/poincare/include/poincare/tree_handle.h @@ -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; diff --git a/poincare/src/horizontal_layout.cpp b/poincare/src/horizontal_layout.cpp index 8998adb96..bbb355bdf 100644 --- a/poincare/src/horizontal_layout.cpp +++ b/poincare/src/horizontal_layout.cpp @@ -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(this)); + assert(thisLayout.hasChild(*selectionStart)); + assert(thisLayout.hasChild(*selectionEnd)); + assert(thisLayout.indexOfChild(*selectionStart) <= thisLayout.indexOfChild(*selectionEnd)); + + // Compute the positions + KDCoordinate selectionXStart = const_cast(this)->positionOfChild(selectionStart->node()).x(); + KDCoordinate selectionXEnd = const_cast(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(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(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); } } diff --git a/poincare/src/layout_cursor.cpp b/poincare/src/layout_cursor.cpp index 553068739..c649d7e25 100644 --- a/poincare/src/layout_cursor.cpp +++ b/poincare/src/layout_cursor.cpp @@ -80,7 +80,6 @@ void LayoutCursor::select(Direction direction, bool * shouldRecomputeLayout, Lay } else { selectUpDown(direction == Direction::Up, shouldRecomputeLayout, selection); } - *shouldRecomputeLayout = true; } /* Layout modification */