#include "scrollable_multiple_expressions_view.h" #include #include #include using namespace Poincare; namespace Shared { AbstractScrollableMultipleExpressionsView::ContentCell::ContentCell() : m_rightExpressionView(), m_approximateSign(k_font, k_defaultApproximateMessage, 0.5f, 0.5f, Palette::GreyVeryDark), m_centeredExpressionView(), m_selectedSubviewPosition(SubviewPosition::Center), m_displayCenter(true) { } KDColor AbstractScrollableMultipleExpressionsView::ContentCell::backgroundColor() const { KDColor background = m_even ? KDColorWhite : Palette::BackgroundApps; return background; } void AbstractScrollableMultipleExpressionsView::ContentCell::setHighlighted(bool highlight) { // Do not call HighlightCell::setHighlighted to avoid marking all cell as dirty m_highlighted = highlight; KDColor defaultColor = backgroundColor(); KDColor color = highlight && m_selectedSubviewPosition == SubviewPosition::Center ? Palette::Select : defaultColor; m_centeredExpressionView.setBackgroundColor(color); color = highlight && m_selectedSubviewPosition == SubviewPosition::Right ? Palette::Select : defaultColor; m_rightExpressionView.setBackgroundColor(color); m_approximateSign.setBackgroundColor(defaultColor); if (leftExpressionView()) { color = highlight && m_selectedSubviewPosition == SubviewPosition::Left ? Palette::Select : defaultColor; leftExpressionView()->setBackgroundColor(color); } } void AbstractScrollableMultipleExpressionsView::ContentCell::setEven(bool even) { EvenOddCell::setEven(even); KDColor defaultColor = backgroundColor(); m_centeredExpressionView.setBackgroundColor(defaultColor); m_rightExpressionView.setBackgroundColor(defaultColor); m_approximateSign.setBackgroundColor(defaultColor); if (leftExpressionView()) { leftExpressionView()->setBackgroundColor(defaultColor); } } void AbstractScrollableMultipleExpressionsView::ContentCell::reloadTextColor() { if (displayCenter()) { m_rightExpressionView.setTextColor(Palette::GreyVeryDark); } else { m_rightExpressionView.setTextColor(KDColorBlack); } } KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOptimalDisplay() const { return privateMinimalSizeForOptimalDisplay(false); } KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOptimalDisplayFullSize() const { return privateMinimalSizeForOptimalDisplay(true); } void AbstractScrollableMultipleExpressionsView::ContentCell::setSelectedSubviewPosition(AbstractScrollableMultipleExpressionsView::SubviewPosition subviewPosition) { m_selectedSubviewPosition = subviewPosition; setHighlighted(isHighlighted()); } void AbstractScrollableMultipleExpressionsView::ContentCell::setDisplayCenter(bool display) { m_displayCenter = display; reloadTextColor(); layoutSubviews(); } Poincare::Layout AbstractScrollableMultipleExpressionsView::ContentCell::layout() const { if (m_selectedSubviewPosition == SubviewPosition::Center) { return m_centeredExpressionView.layout(); } else if (m_selectedSubviewPosition == SubviewPosition::Right) { return m_rightExpressionView.layout(); } assert(m_selectedSubviewPosition == SubviewPosition::Left); assert(leftExpressionView()); return leftExpressionView()->layout(); } int AbstractScrollableMultipleExpressionsView::ContentCell::numberOfSubviews() const { int nbOfSubviews = 1; if (displayCenter()) { nbOfSubviews += 2; } if (leftExpressionView()) { nbOfSubviews += 1; } return nbOfSubviews; } KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::baseline(KDCoordinate * leftBaseline, KDCoordinate * centerBaseline, KDCoordinate * rightBaseline) const { // Left view KDCoordinate leftViewBaseline = (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) ? leftExpressionView()->layout().baseline() : 0; if (leftBaseline != nullptr) { *leftBaseline = leftViewBaseline; } // Center view KDCoordinate centerViewBaseline = displayCenter() ? m_centeredExpressionView.layout().baseline() : 0; if (centerBaseline != nullptr) { *centerBaseline = centerViewBaseline; } // Right view KDCoordinate rightViewBaseline = m_rightExpressionView.layout().isUninitialized() ? 0 : m_rightExpressionView.layout().baseline(); if (rightBaseline != nullptr) { *rightBaseline = rightViewBaseline; } return std::max(std::max(leftViewBaseline, centerViewBaseline), rightViewBaseline); } void AbstractScrollableMultipleExpressionsView::ContentCell::subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) { // Subviews sizes KDSize leftSize = leftExpressionView() ? leftExpressionView()->minimalSizeForOptimalDisplay() : KDSizeZero; KDSize centerSize = displayCenter() ? m_centeredExpressionView.minimalSizeForOptimalDisplay() : KDSizeZero; KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); // Compute baselines KDCoordinate leftBaseline = 0; KDCoordinate centerBaseline = 0; KDCoordinate rightBaseline = 0; KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); // Layout left view KDCoordinate currentWidth = 0; if (leftExpressionView()) { assert(leftFrame != nullptr); *leftFrame = KDRect(currentWidth, viewBaseline - leftBaseline, leftSize); currentWidth += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; } // Layout center expression if (displayCenter()) { assert(centerFrame != nullptr && approximateSignFrame != nullptr); KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); *centerFrame = KDRect(currentWidth, viewBaseline - centerBaseline, centerSize); currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + centerSize.width(); *approximateSignFrame = KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize); currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + approximateSignSize.width(); } // Layout right expression assert(rightFrame != nullptr); *rightFrame = KDRect(currentWidth, viewBaseline - rightBaseline, rightSize); } KDSize AbstractScrollableMultipleExpressionsView::ContentCell::privateMinimalSizeForOptimalDisplay(bool forceFullDisplay) const { KDCoordinate width = 0; // Compute baselines KDCoordinate leftBaseline = 0; KDCoordinate centerBaseline = 0; KDCoordinate rightBaseline = 0; KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); KDSize leftSize = KDSizeZero; if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) { leftSize = leftExpressionView()->minimalSizeForOptimalDisplay(); width += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; } KDSize centerSize = KDSizeZero; if (displayCenter() || (forceFullDisplay && !m_centeredExpressionView.layout().isUninitialized())) { centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); width += centerSize.width() + 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); } KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); width += rightSize.width(); KDCoordinate height = viewBaseline + std::max( std::max( centerSize.height() - centerBaseline, rightSize.height() - rightBaseline), leftSize.height() - leftBaseline); return KDSize(width, height); } View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(int index) { bool leftIsVisible = leftExpressionView() != nullptr; if (leftIsVisible && index == 0) { return leftExpressionView(); } View * views[3] = {&m_rightExpressionView, &m_approximateSign, &m_centeredExpressionView}; return views[index - leftIsVisible]; } KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::StandardApproximateViewAndMarginsSize() { return 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + k_font->stringSize(I18n::translate(k_defaultApproximateMessage)).width(); } void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool force) { if (bounds().width() <= 0 || bounds().height() <= 0) { // TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews return; } KDRect leftFrame = KDRectZero; KDRect centerFrame = KDRectZero; KDRect approximateSignFrame = KDRectZero; KDRect rightFrame = KDRectZero; subviewFrames(&leftFrame, ¢erFrame, &approximateSignFrame, &rightFrame); if (leftExpressionView() != nullptr) { leftExpressionView()->setFrame(leftFrame, force); } if (centeredExpressionView() != nullptr) { centeredExpressionView()->setFrame(centerFrame, force); } m_approximateSign.setFrame(approximateSignFrame, force); if (rightExpressionView() != nullptr) { rightExpressionView()->setFrame(rightFrame, force); } } AbstractScrollableMultipleExpressionsView::AbstractScrollableMultipleExpressionsView(Responder * parentResponder, View * contentCell) : ScrollableView(parentResponder, contentCell, this) { setDecoratorType(ScrollView::Decorator::Type::Arrows); } void AbstractScrollableMultipleExpressionsView::setLayouts(Poincare::Layout leftLayout, Poincare::Layout centerLayout, Poincare::Layout rightLayout) { bool updateRightLayout = contentCell()->rightExpressionView()->setLayout(rightLayout); bool updateCenterLayout = contentCell()->centeredExpressionView()->setLayout(centerLayout); bool updateLeftLayout = false; if (contentCell()->leftExpressionView()) { updateLeftLayout = contentCell()->leftExpressionView()->setLayout(leftLayout); } if (updateLeftLayout || updateCenterLayout || updateRightLayout) { contentCell()->reloadTextColor(); // Reload expressions layouts contentCell()->layoutSubviews(); // Reload the scroll content view layout (the content size might have changed) layoutSubviews(); // Do no reload scroll here as 'setLayouts' is called every time the table is re-layout (when scrolling for instance) } } void AbstractScrollableMultipleExpressionsView::setEqualMessage(I18n::Message equalSignMessage) { contentCell()->approximateSign()->setMessage(equalSignMessage); } void AbstractScrollableMultipleExpressionsView::reloadScroll() { if (selectedSubviewPosition() == SubviewPosition::Right) { // Scroll to the right extremity scrollToContentPoint(KDPoint(contentCell()->bounds().width(), 0), true); } else { // Scroll to the left extremity ScrollableView::reloadScroll(); } } void AbstractScrollableMultipleExpressionsView::setDisplayCenter(bool display) { contentCell()->setDisplayCenter(display); layoutSubviews(); } bool AbstractScrollableMultipleExpressionsView::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Left || event == Ion::Events::Right ) { bool leftIsVisible = false; KDCoordinate leftWidth = 0; if (contentCell()->leftExpressionView()) { leftWidth = contentCell()->leftExpressionView()->minimalSizeForOptimalDisplay().width(); leftIsVisible = leftWidth - contentOffset().x() > 0; } KDCoordinate rightExpressionWidth = contentCell()->rightExpressionView()->minimalSizeForOptimalDisplay().width(); bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - contentOffset().x() < bounds().width(); bool centeredExpressionIsVisibleOnTheLeft = false; bool centeredExpressionIsVisibleOnTheRight = false; if (contentCell()->displayCenter()) { KDCoordinate centerExpressionWidth = contentCell()->centeredExpressionView()->minimalSizeForOptimalDisplay().width(); KDCoordinate signWidth = contentCell()->approximateSign()->minimalSizeForOptimalDisplay().width(); centeredExpressionIsVisibleOnTheLeft = leftWidth + k_horizontalMargin + centerExpressionWidth - contentOffset().x() > 0; centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2 * k_horizontalMargin - contentOffset().x() < bounds().width(); } // Select center if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && centeredExpressionIsVisibleOnTheLeft) || (event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && centeredExpressionIsVisibleOnTheRight)) { setSelectedSubviewPosition(SubviewPosition::Center); return true; } // Select left if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && leftIsVisible) || (event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Center && leftIsVisible)) { setSelectedSubviewPosition(SubviewPosition::Left); return true; } if ((event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Center && rightExpressionIsVisible) || (event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && rightExpressionIsVisible)) { setSelectedSubviewPosition(SubviewPosition::Right); return true; } } return ScrollableView::handleEvent(event); } }