[apps/calculation] Fix calculation height computation

It was done in two places before, which created inconsistencies
This commit is contained in:
Léa Saviot
2020-05-26 17:23:06 +02:00
committed by EmilieNumworks
parent 0379327b06
commit a56a73b0ba
12 changed files with 141 additions and 178 deletions

View File

@@ -78,7 +78,13 @@ KDCoordinate IllustratedListController::rowHeight(int j) {
return 0;
}
Shared::ExpiringPointer<Calculation> calculation = m_calculationStore.calculationAtIndex(calculationIndex);
return calculation->height(App::app()->localContext(), Metric::CommonSmallMargin, ScrollableThreeExpressionsView::k_margin, true, true) + Metric::CellSeparatorThickness;
constexpr bool expanded = true;
KDCoordinate result = calculation->memoizedHeight(expanded);
if (result < 0) {
result = ScrollableThreeExpressionsCell::Height(calculation.pointer());
calculation->setMemoizedHeight(expanded, result);
}
return result + Metric::CellSeparatorThickness;
}
int IllustratedListController::typeAtLocation(int i, int j) {

View File

@@ -65,6 +65,17 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
layoutSubviews();
}
KDCoordinate ScrollableThreeExpressionsCell::Height(Calculation * calculation) {
ScrollableThreeExpressionsCell cell;
cell.setCalculation(calculation);
KDRect leftFrame = KDRectZero;
KDRect centerFrame = KDRectZero;
KDRect approximateSignFrame = KDRectZero;
KDRect rightFrame = KDRectZero;
cell.subviewFrames(&leftFrame, &centerFrame, &approximateSignFrame, &rightFrame);
return leftFrame.unionedWith(centerFrame).unionedWith(rightFrame).height();
}
void ScrollableThreeExpressionsCell::didBecomeFirstResponder() {
reinitSelection();
Container::activeApp()->setFirstResponder(&m_view);

View File

@@ -17,6 +17,9 @@ public:
}
void resetMemoization();
void setCalculation(Calculation * calculation);
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
return m_contentCell.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
}
private:
class ContentCell : public Shared::AbstractScrollableMultipleExpressionsView::ContentCell {
public:
@@ -29,12 +32,13 @@ private:
};
ContentCell * contentCell() override { return &m_contentCell; };
const ContentCell * constContentCell() const override { return &m_contentCell; };
const ContentCell * constContentCell() const override { return &m_contentCell; };
ContentCell m_contentCell;
};
class ScrollableThreeExpressionsCell : public TableCell, public Responder {
public:
static KDCoordinate Height(Calculation * calculation);
ScrollableThreeExpressionsCell() :
Responder(nullptr),
m_view(this) {}
@@ -59,6 +63,9 @@ public:
void setSelectedSubviewPosition(ScrollableThreeExpressionsView::SubviewPosition subviewPosition) { m_view.setSelectedSubviewPosition(subviewPosition); }
void reinitSelection();
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
return m_view.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
}
private:
// Remove label margin added by TableCell because they're already handled by ScrollableThreeExpressionsView
KDCoordinate labelMargin() const override { return 0; }

View File

@@ -125,125 +125,12 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul
}
}
KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canBeSingleLine) {
/* WARNING: this method must return the same result as
* Calculation::HistoryViewCell::layoutSubviews. */
KDCoordinate result = expanded ? m_expandedHeight : m_height;
if (result >= 0) {
// Height already computed
return result;
}
// Get input information
Layout inputLayout = createInputLayout();
KDCoordinate inputHeight = inputLayout.layoutSize().height();
KDCoordinate inputWidth = inputLayout.layoutSize().width();
KDCoordinate inputBaseline = inputLayout.baseline();
// Get output information
KDCoordinate outputWidth;
KDCoordinate outputBaseline;
KDCoordinate outputHeightBelowBaseline;
DisplayOutput displayType = displayOutput(context);
/* If the display output is ExactAndApproximateToggle, we want to use the
* expanded width to compute if the calculaton is in single line or not. */
bool shouldComputeMaxOutputWidthForSingleLine = !forceSingleLine && (!expanded && displayType == DisplayOutput::ExactAndApproximateToggle);
{
bool displaysExactOnly = displayType == DisplayOutput::ExactOnly;
bool displayApproximateOnly = displayType == DisplayOutput::ApproximateOnly
|| (!expanded
&& (displayType == DisplayOutput::ExactAndApproximateToggle));
KDCoordinate exactOutputWidth = 0;
KDCoordinate exactOutputBaseline = 0;
KDCoordinate exactOutputBelowBaseline = 0;
// Get exact output info if needed
bool displaysExact = displaysExactOnly || !displayApproximateOnly;
if (displaysExact || shouldComputeMaxOutputWidthForSingleLine) {
bool couldNotCreateExactLayout = false;
Poincare::Layout exactLayout = createExactOutputLayout(&couldNotCreateExactLayout);
if (couldNotCreateExactLayout) {
if (!displaysExactOnly) {
displayType = DisplayOutput::ApproximateOnly;
forceDisplayOutput(displayType);
displaysExactOnly = false;
displayApproximateOnly = true;
shouldComputeMaxOutputWidthForSingleLine = false;
outputWidth = 0;
} else {
/* We should only display the exact result, but we cannot create it
* -> raise an exception. */
ExceptionCheckpoint::Raise();
}
} else {
KDSize exactSize = exactLayout.layoutSize();
outputWidth = exactSize.width();
if (displaysExact) {
exactOutputWidth = exactSize.width();
exactOutputBaseline = exactLayout.baseline();
exactOutputBelowBaseline = exactSize.height() - exactOutputBaseline;
}
}
}
KDCoordinate approximateOutputWidth = 0;
KDCoordinate approximateOutputBaseline = 0;
KDCoordinate approximateOutputBelowBaseline = 0;
// Get approximate output info if needed
bool displaysApproximate = displayApproximateOnly || !displaysExactOnly;
if (displaysApproximate || shouldComputeMaxOutputWidthForSingleLine) {
bool couldNotCreateApproximateLayout = false;
Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
Poincare::ExceptionCheckpoint::Raise();
}
KDSize approximateOutputSize = approximateLayout.layoutSize();
outputWidth += approximateOutputSize.width();
if (displaysApproximate) {
approximateOutputWidth = approximateOutputSize.width();
approximateOutputBaseline = approximateLayout.baseline();
approximateOutputBelowBaseline = approximateOutputSize.height() - approximateOutputBaseline;
}
}
// Compute the output info
if (shouldComputeMaxOutputWidthForSingleLine || (exactOutputWidth > 0 && approximateOutputWidth > 0)) {
outputWidth += AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize();
}
outputBaseline = std::max(exactOutputBaseline, approximateOutputBaseline);
outputHeightBelowBaseline = std::max(exactOutputBelowBaseline, approximateOutputBelowBaseline);
}
if (forceSingleLine || canBeSingleLine(inputWidth, outputWidth)) {
result = std::max(inputBaseline, outputBaseline) // Above the baseline
+ std::max(static_cast<KDCoordinate>(inputHeight - inputBaseline), outputHeightBelowBaseline) // Below the baseline
+ 2 * verticalMarginAroundLayouts;
void Calculation::setMemoizedHeight(bool expanded, KDCoordinate height) {
if (expanded) {
m_expandedHeight = height;
} else {
result = inputHeight + outputBaseline + outputHeightBelowBaseline + 4 * verticalMarginAroundLayouts;
m_height = height;
}
// Add the top and bottom margins
result += 2 * topBottomMargin;
/* For all display outputs except ExactAndApproximateToggle, the selected
* height and the usual height are identical. We update both heights in
* theses cases. */
if (displayOutput(context) != DisplayOutput::ExactAndApproximateToggle) {
m_height = result;
m_expandedHeight = result;
} else {
if (expanded) {
m_expandedHeight = result;
} else {
m_height = result;
}
}
return result;
}
Calculation::DisplayOutput Calculation::displayOutput(Context * context) {

View File

@@ -84,8 +84,8 @@ public:
Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout);
// Memoization of height
typedef bool (*LayoutsCanBeSingleLineFunction)(KDCoordinate inputWidth, KDCoordinate outputWidth);
KDCoordinate height(Poincare::Context * context, KDCoordinate topBottomMargin, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { assert(false); return true; });
KDCoordinate memoizedHeight(bool expanded) { return expanded ? m_expandedHeight : m_height; }
void setMemoizedHeight(bool expanded, KDCoordinate height);
// Displayed output
DisplayOutput displayOutput(Poincare::Context * context);

View File

@@ -206,13 +206,13 @@ KDCoordinate HistoryController::rowHeight(int j) {
return 0;
}
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(j);
return calculation->height(
App::app()->localContext(),
HistoryViewCell::k_margin,
HistoryViewCell::k_inputOutputViewsVerticalMargin,
j == selectedRow() && selectedSubviewType() == SubviewType::Output,
false,
&HistoryViewCell::LayoutsCanBeSingleLine);
bool expanded = j == selectedRow() && selectedSubviewType() == SubviewType::Output;
KDCoordinate result = calculation->memoizedHeight(expanded);
if (result < 0) {
result = HistoryViewCell::Height(calculation.pointer(), expanded);
calculation->setMemoizedHeight(expanded, result);
}
return result;
}
int HistoryController::typeAtLocation(int i, int j) {

View File

@@ -34,12 +34,22 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType,
/* HistoryViewCell */
KDCoordinate HistoryViewCell::Height(Calculation * calculation, bool expanded) {
HistoryViewCell cell(nullptr);
cell.setCalculation(calculation, expanded);
KDRect ellipsisFrame = KDRectZero;
KDRect inputFrame = KDRectZero;
KDRect outputFrame = KDRectZero;
cell.computeSubviewFrames(Ion::Display::Width, KDCOORDINATE_MAX, &ellipsisFrame, &inputFrame, &outputFrame);
return k_margin + inputFrame.unionedWith(outputFrame).height() + k_margin;
}
HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
Responder(parentResponder),
m_calculationCRC32(0),
m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
m_inputView(this, k_inputOutputViewsHorizontalMargin, k_inputOutputViewsVerticalMargin),
m_inputView(this, k_inputViewHorizontalMargin, k_inputOutputViewsVerticalMargin),
m_scrollableOutputView(this),
m_calculationExpanded(false),
m_calculationSingleLine(false)
@@ -154,23 +164,37 @@ View * HistoryViewCell::subviewAtIndex(int index) {
return views[index];
}
bool HistoryViewCell::LayoutsCanBeSingleLine(KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) {
return ViewsCanBeSingleLine(inputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin, outputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin);
}
bool HistoryViewCell::ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth) {
// k_margin is the separation between the input and output.
return (inputViewWidth + k_margin + outputViewWidth) < Ion::Display::Width - Metric::EllipsisCellWidth;
}
void HistoryViewCell::layoutSubviews(bool force) {
KDCoordinate maxFrameWidth = bounds().width();
if (displayedEllipsis()) {
m_ellipsis.setFrame(KDRect(maxFrameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, bounds().height()), force);
maxFrameWidth -= Metric::EllipsisCellWidth;
} else {
m_ellipsis.setFrame(KDRectZero, force); // Required to mark previous rect as dirty
KDRect frameBounds = bounds();
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 ellipsisFrame = KDRectZero;
KDRect inputFrame = KDRectZero;
KDRect outputFrame = KDRectZero;
computeSubviewFrames(frameBounds.width(), frameBounds.height(), &ellipsisFrame, &inputFrame, &outputFrame);
m_ellipsis.setFrame(ellipsisFrame, force); // Required even if ellipsisFrame is KDRectZero, to mark previous rect as dirty
m_inputView.setFrame(inputFrame,force);
m_scrollableOutputView.setFrame(outputFrame, force);
}
void HistoryViewCell::computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame) {
assert(ellipsisFrame != nullptr && inputFrame != nullptr && outputFrame != nullptr);
if (displayedEllipsis()) {
*ellipsisFrame = KDRect(frameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, frameHeight);
frameWidth -= Metric::EllipsisCellWidth;
} else {
*ellipsisFrame = KDRectZero;
}
KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay();
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
@@ -193,18 +217,16 @@ void HistoryViewCell::layoutSubviews(bool force) {
outputY += inputSize.height();
}
m_inputView.setFrame(KDRect(
*inputFrame = KDRect(
0,
inputY,
std::min(maxFrameWidth, inputSize.width()),
inputSize.height()),
force);
m_scrollableOutputView.setFrame(KDRect(
std::max(0, maxFrameWidth - outputSize.width()),
std::min(frameWidth, inputSize.width()),
inputSize.height());
*outputFrame = KDRect(
std::max(0, frameWidth - outputSize.width()),
outputY,
std::min(maxFrameWidth, outputSize.width()),
outputSize.height()),
force);
std::min(frameWidth, outputSize.width()),
outputSize.height());
}
void HistoryViewCell::resetMemoization() {

View File

@@ -33,9 +33,9 @@ class HistoryViewCell : public ::EvenOddCell, public Responder {
public:
constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin;
constexpr static KDCoordinate k_inputOutputViewsVerticalMargin = k_margin;
constexpr static KDCoordinate k_inputOutputViewsHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
constexpr static KDCoordinate k_inputViewHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
static KDCoordinate Height(Calculation * calculation, bool expanded);
HistoryViewCell(Responder * parentResponder = nullptr);
static bool LayoutsCanBeSingleLine(KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth);
static bool ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth);
void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None);
void setEven(bool even) override;
@@ -58,6 +58,7 @@ public:
Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; }
private:
constexpr static KDCoordinate k_resultWidth = 80;
void computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame);
void reloadScroll();
void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType);
bool displayedEllipsis() const {

View File

@@ -121,6 +121,41 @@ KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::baseline(KD
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, &centerBaseline, &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;
@@ -169,35 +204,25 @@ KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::StandardApp
}
void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool force) {
// 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, &centerBaseline, &rightBaseline);
// Layout left view
KDCoordinate currentWidth = 0;
if (leftExpressionView()) {
leftExpressionView()->setFrame(KDRect(currentWidth, viewBaseline - leftBaseline, leftSize), force);
currentWidth += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
if (bounds().width() <= 0 || bounds().height() <= 0) {
// TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews
return;
}
// Layout center expression
if (displayCenter()) {
KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay();
m_centeredExpressionView.setFrame(KDRect(currentWidth, viewBaseline - centerBaseline, centerSize), force);
currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + centerSize.width();
m_approximateSign.setFrame(KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize), force);
currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + approximateSignSize.width();
KDRect leftFrame = KDRectZero;
KDRect centerFrame = KDRectZero;
KDRect approximateSignFrame = KDRectZero;
KDRect rightFrame = KDRectZero;
subviewFrames(&leftFrame, &centerFrame, &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);
}
// Layout right expression
m_rightExpressionView.setFrame(KDRect(currentWidth, viewBaseline - rightBaseline, rightSize), force);
}
AbstractScrollableMultipleExpressionsView::AbstractScrollableMultipleExpressionsView(Responder * parentResponder, View * contentCell) :

View File

@@ -68,6 +68,7 @@ protected:
int numberOfSubviews() const override;
virtual Poincare::Layout layout() const override;
KDCoordinate baseline(KDCoordinate * leftBaseline = nullptr, KDCoordinate * centerBaseline = nullptr, KDCoordinate * rightBaseline = nullptr) const;
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame);
private:
constexpr static const KDFont * k_font = KDFont::LargeFont;
const static I18n::Message k_defaultApproximateMessage = I18n::Message::AlmostEqual;

View File

@@ -117,6 +117,9 @@ KDRect ScrollView::visibleContentRect() {
}
void ScrollView::layoutSubviews(bool force) {
if (bounds().isEmpty()) {
return;
}
KDRect r1 = KDRectZero;
KDRect r2 = KDRectZero;
KDRect innerFrame = decorator()->layoutIndicators(minimalSizeForOptimalDisplay(), contentOffset(), bounds(), &r1, &r2, force);

View File

@@ -174,5 +174,5 @@ KDRect KDRect::movedTo(KDPoint p) const {
}
bool KDRect::isEmpty() const {
return (width() == 0 || height() == 0);
return (width() == 0 || height() == 0); //TODO <= 0
}