diff --git a/apps/calculation/additional_outputs/expressions_list_controller.cpp b/apps/calculation/additional_outputs/expressions_list_controller.cpp index 515ec3b4a..be7a5ed36 100644 --- a/apps/calculation/additional_outputs/expressions_list_controller.cpp +++ b/apps/calculation/additional_outputs/expressions_list_controller.cpp @@ -24,6 +24,13 @@ int ExpressionsListController::reusableCellCount(int type) { return k_maxNumberOfCells; } +void ExpressionsListController::viewDidDisappear() { + // Reset cell memoization to avoid taking extra space in the pool + for (int i = 0; i < k_maxNumberOfCells; i++) { + m_cells[i].setLayout(Layout()); + } +} + HighlightCell * ExpressionsListController::reusableCell(int index, int type) { return &m_cells[index]; } diff --git a/apps/calculation/additional_outputs/expressions_list_controller.h b/apps/calculation/additional_outputs/expressions_list_controller.h index 64152f6be..afc59e9c1 100644 --- a/apps/calculation/additional_outputs/expressions_list_controller.h +++ b/apps/calculation/additional_outputs/expressions_list_controller.h @@ -13,6 +13,7 @@ public: ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController); // Responder + void viewDidDisappear() override; void didEnterResponderChain(Responder * previousFirstResponder) override; //ListViewDataSource diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.cpp b/apps/calculation/additional_outputs/illustrated_list_controller.cpp index 67bc939e6..53bfeb41c 100644 --- a/apps/calculation/additional_outputs/illustrated_list_controller.cpp +++ b/apps/calculation/additional_outputs/illustrated_list_controller.cpp @@ -42,6 +42,10 @@ void IllustratedListController::viewDidDisappear() { Poincare::Symbol s = Poincare::Symbol::Builder(expressionSymbol()); context->setExpressionForSymbolAbstract(m_savedExpression, s); } + // Reset cell memoization to avoid taking extra space in the pool + for (int i = 0; i < k_maxNumberOfAdditionalCalculations; i++) { + m_additionalCalculationCells[i].resetMemoization(); + } } int IllustratedListController::numberOfRows() const { diff --git a/apps/calculation/additional_outputs/list_controller.cpp b/apps/calculation/additional_outputs/list_controller.cpp index 788bdea1e..fe2836fed 100644 --- a/apps/calculation/additional_outputs/list_controller.cpp +++ b/apps/calculation/additional_outputs/list_controller.cpp @@ -32,8 +32,12 @@ bool ListController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK || event == Ion::Events::EXE) { char buffer[Constant::MaxSerializedExpressionSize]; textAtIndex(buffer, Constant::MaxSerializedExpressionSize, selectedRow()); - m_editExpressionController->insertTextBody(buffer); + /* The order is important here: we dismiss the pop-up first because it + * clears the Poincare pool from the layouts used to display the pop-up. + * Thereby it frees memory to do Poincare computations required by + * insertTextBody. */ Container::activeApp()->dismissModalViewController(); + m_editExpressionController->insertTextBody(buffer); Container::activeApp()->setFirstResponder(m_editExpressionController); return true; } diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp index e5bc0b540..d90fc628d 100644 --- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp +++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp @@ -4,11 +4,15 @@ namespace Calculation { +void ScrollableThreeExpressionsView::resetMemoization() { + setLayouts(Poincare::Layout(), Poincare::Layout(), Poincare::Layout()); +} + void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) { Poincare::Context * context = App::app()->localContext(); // Clean the layouts to make room in the pool - setLayouts(Poincare::Layout(), Poincare::Layout(), Poincare::Layout()); + resetMemoization(); // Create the input layout Poincare::Layout inputLayout = calculation->createInputLayout(); diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h index 6bb8155f1..42c18becb 100644 --- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h +++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h @@ -14,6 +14,7 @@ public: setMargins(Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin); // Left Right margins are already added by TableCell setBackgroundColor(KDColorWhite); } + void resetMemoization(); void setCalculation(Calculation * calculation); private: class ContentCell : public Shared::AbstractScrollableMultipleExpressionsView::ContentCell { @@ -50,6 +51,7 @@ public: View * labelView() const override { return (View *)&m_view; } void setHighlighted(bool highlight) override { m_view.evenOddCell()->setHighlighted(highlight); } + void resetMemoization() { m_view.resetMemoization(); } void setCalculation(Calculation * calculation); void setDisplayCenter(bool display); ScrollableThreeExpressionsView::SubviewPosition selectedSubviewPosition() { return m_view.selectedSubviewPosition(); } diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 576168df7..0fb60cb04 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -313,6 +313,12 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) { return m_displayOutput; } +void Calculation::forceDisplayOutput(DisplayOutput d) { + m_displayOutput = d; + // Reset heights memoization as it might have changed when we modify the display output + m_height = -1; + m_expandedHeight = -1; +} bool Calculation::shouldOnlyDisplayExactOutput() { /* If the input is a "store in a function", do not display the approximate * result. This prevents x->f(x) from displaying x = undef. */ diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 5dec4fc0a..b084226df 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -88,7 +88,7 @@ public: // Displayed output DisplayOutput displayOutput(Poincare::Context * context); - void forceDisplayOutput(DisplayOutput d) { m_displayOutput = d; } + void forceDisplayOutput(DisplayOutput d); bool shouldOnlyDisplayExactOutput(); EqualSign exactAndApproximateDisplayedOutputsAreEqual(Poincare::Context * context); diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index c62e8b50c..292a6199e 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -231,6 +231,10 @@ void HistoryController::historyViewCellDidChangeSelection(HistoryViewCell ** cel m_selectableTableView.reloadData(); } + // It might be necessary to scroll to the sub type if the cell overflows the screen + if (selectedRow() >= 0) { + m_selectableTableView.scrollToSubviewOfTypeOfCellAtLocation(type, m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow()); + } // Fill the selected cell and the previous selected cell because cells repartition might have changed *cell = static_cast(m_selectableTableView.selectedCell()); *previousCell = static_cast(m_selectableTableView.cellAtLocation(previousSelectedCellX, previousSelectedCellY)); diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 90908a6af..2b7f34572 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -309,9 +309,6 @@ bool HistoryViewCell::handleEvent(Ion::Events::Event event) { otherSubviewType = HistoryViewCellDataSource::SubviewType::Output; } m_dataSource->setSelectedSubviewType(otherSubviewType, true); - CalculationSelectableTableView * tableView = (CalculationSelectableTableView *)parentResponder(); - tableView->scrollToSubviewOfTypeOfCellAtLocation(otherSubviewType, tableView->selectedColumn(), tableView->selectedRow()); - Container::activeApp()->setFirstResponder(this); return true; } return false; diff --git a/apps/calculation/selectable_table_view.cpp b/apps/calculation/selectable_table_view.cpp index c00bed4b1..e2d81e53c 100644 --- a/apps/calculation/selectable_table_view.cpp +++ b/apps/calculation/selectable_table_view.cpp @@ -24,18 +24,6 @@ void CalculationSelectableTableView::scrollToCell(int i, int j) { KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(dataSource()->numberOfRows()) - maxContentHeightDisplayableWithoutScrolling(); setContentOffset(KDPoint(contentOffsetX, contentOffsetY)); } - if (dataSource()->numberOfRows() > j && dataSource()->numberOfColumns() > i && dataSource()->rowHeight(j) > bounds().height()) { - KDCoordinate contentOffsetX = contentOffset().x(); - KDCoordinate contentOffsetY = contentOffset().y(); - if (contentOffsetY > dataSource()->cumulatedHeightFromIndex(j) && contentOffsetY > dataSource()->cumulatedHeightFromIndex(j+1)) { - // Let's scroll the tableView to align the top of the cell to the top - contentOffsetY = dataSource()->cumulatedHeightFromIndex(j); - } else { - // Let's scroll the tableView to align the bottom of the cell to the bottom - contentOffsetY = dataSource()->cumulatedHeightFromIndex(j+1) - maxContentHeightDisplayableWithoutScrolling(); - } - setContentOffset(KDPoint(contentOffsetX, contentOffsetY)); - } } void CalculationSelectableTableView::scrollToSubviewOfTypeOfCellAtLocation(HistoryViewCellDataSource::SubviewType subviewType, int i, int j) { @@ -44,10 +32,8 @@ void CalculationSelectableTableView::scrollToSubviewOfTypeOfCellAtLocation(Histo } /* As we scroll, the selected calculation does not use the same history view * cell, thus, we want to deselect the previous used history view cell. */ - if (selectedRow() >= 0) { - HighlightCell * previousCell = selectedCell(); - previousCell->setHighlighted(false); - } + unhighlightSelectedCell(); + /* Main part of the scroll */ KDCoordinate contentOffsetX = contentOffset().x(); KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(j+1) - maxContentHeightDisplayableWithoutScrolling(); @@ -58,16 +44,13 @@ void CalculationSelectableTableView::scrollToSubviewOfTypeOfCellAtLocation(Histo contentOffsetY = dataSource()->cumulatedHeightFromIndex(j); } } - /* For the same reason, we have to rehighlight the new history view cell and - * inform the delegate which history view cell is highlighted even if the - * selected calculation has not changed. */ setContentOffset(KDPoint(contentOffsetX, contentOffsetY)); - HighlightCell * cell = cellAtLocation(i, j); + /* For the same reason, we have to rehighlight the new history view cell and + * reselect the first responder. */ + HistoryViewCell * cell = (HistoryViewCell *)(selectedCell()); assert(cell); cell->setHighlighted(true); - if (m_delegate) { - m_delegate->tableViewDidChangeSelection(this, selectedColumn(), selectedRow()); - } + Container::activeApp()->setFirstResponder(cell); } diff --git a/apps/shared/function_app.cpp b/apps/shared/function_app.cpp index 5a80460b5..7cede0ee8 100644 --- a/apps/shared/function_app.cpp +++ b/apps/shared/function_app.cpp @@ -26,7 +26,7 @@ void FunctionApp::Snapshot::storageDidChangeForRecord(const Ion::Storage::Record void FunctionApp::willBecomeInactive() { if (m_modalViewController.isDisplayingModal()) { - m_modalViewController.dismissModalViewController(); + m_modalViewController.dismissModalViewController(true); } if (inputViewController()->isDisplayingModal()) { inputViewController()->abortEditionAndDismiss(); diff --git a/apps/solver/app.cpp b/apps/solver/app.cpp index 55a2a390d..0d50e5859 100644 --- a/apps/solver/app.cpp +++ b/apps/solver/app.cpp @@ -60,7 +60,7 @@ App::App(Snapshot * snapshot) : void App::willBecomeInactive() { if (m_modalViewController.isDisplayingModal()) { - m_modalViewController.dismissModalViewController(); + m_modalViewController.dismissModalViewController(true); } if (inputViewController()->isDisplayingModal()) { inputViewController()->abortEditionAndDismiss(); diff --git a/apps/variable_box_controller.cpp b/apps/variable_box_controller.cpp index 7d5943674..36b9845c3 100644 --- a/apps/variable_box_controller.cpp +++ b/apps/variable_box_controller.cpp @@ -3,6 +3,7 @@ #include "shared/continuous_function.h" #include #include +#include #include #include #include @@ -235,10 +236,20 @@ Layout VariableBoxController::expressionLayoutForRecord(Storage::Record record, assert(m_firstMemoizedLayoutIndex >= 0); } assert(index >= m_firstMemoizedLayoutIndex && index < m_firstMemoizedLayoutIndex + k_maxNumberOfDisplayedRows); + Layout result; if (m_layouts[index-m_firstMemoizedLayoutIndex].isUninitialized()) { - m_layouts[index-m_firstMemoizedLayoutIndex] = GlobalContext::LayoutForRecord(record); + /* Creating the layout of a very long variable might throw a pool exception. + * We want to catch it and return a dummy layout instead, otherwise the user + * won't be able to open the variable box again, until she deletes the + * problematic variable -> and she has no help to remember its name, as she + * can't open the variable box. */ + Poincare::ExceptionCheckpoint ecp; + if (ExceptionRun(ecp)) { + result = GlobalContext::LayoutForRecord(record); + } } - return m_layouts[index-m_firstMemoizedLayoutIndex]; + m_layouts[index-m_firstMemoizedLayoutIndex] = result; + return result; } const char * VariableBoxController::extension() const { diff --git a/escher/include/escher/selectable_table_view.h b/escher/include/escher/selectable_table_view.h index 462041a37..f10a606be 100644 --- a/escher/include/escher/selectable_table_view.h +++ b/escher/include/escher/selectable_table_view.h @@ -32,10 +32,9 @@ public: bool selectCellAtLocation(int i, int j, bool setFirstResponder = true, bool withinTemporarySelection = false); HighlightCell * selectedCell(); protected: + void unhighlightSelectedCell(); SelectableTableViewDataSource * m_selectionDataSource; SelectableTableViewDelegate * m_delegate; -private: - void unhighlightSelectedCell(); }; #endif diff --git a/escher/src/scroll_view.cpp b/escher/src/scroll_view.cpp index 77a7a8855..332e9dfe2 100644 --- a/escher/src/scroll_view.cpp +++ b/escher/src/scroll_view.cpp @@ -81,8 +81,33 @@ void ScrollView::scrollToContentPoint(KDPoint p, bool allowOverscroll) { } void ScrollView::scrollToContentRect(KDRect rect, bool allowOverscroll) { - scrollToContentPoint(rect.topLeft(), allowOverscroll); - scrollToContentPoint(rect.bottomRight(), allowOverscroll); + KDPoint tl = rect.topLeft(); + KDPoint br = rect.bottomRight(); + KDRect visibleRect = visibleContentRect(); + /* We first check that we can display the whole rect. If we can't, we focus + * the croll to the closest part of the rect. */ + if (visibleRect.height() < rect.height()) { + // The visible rect is too small to display 'rect' + if (rect.top() >= visibleRect.top()) { + // We scroll to display the top part of rect + br = KDPoint(br.x(), rect.top() + visibleRect.height()); + } else { + // We scroll to display the bottom part of rect + tl = KDPoint(tl.x(), rect.bottom() - visibleRect.height()); + } + } + if (visibleRect.width() < rect.width()) { + // The visible rect is too small to display 'rect' + if (rect.left() >= visibleRect.left()) { + // We scroll to display the left part of rect + br = KDPoint(rect.left() + visibleRect.width(), br.y()); + } else { + // We scroll to display the right part of rect + tl = KDPoint(rect.right() - visibleRect.width(), tl.y()); + } + } + scrollToContentPoint(tl, allowOverscroll); + scrollToContentPoint(br, allowOverscroll); } KDRect ScrollView::visibleContentRect() { diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp index 8f0a325b5..90ec61731 100644 --- a/poincare/src/function.cpp +++ b/poincare/src/function.cpp @@ -39,7 +39,7 @@ int FunctionNode::getVariables(Context * context, isVariableTest isVariable, cha Function f(this); Expression e = SymbolAbstract::Expand(f, context, true); if (e.isUninitialized()) { - return 0; + return nextVariableIndex; } return e.node()->getVariables(context, isVariable, variables, maxSizeVariable, nextVariableIndex); } diff --git a/python/port/port.cpp b/python/port/port.cpp index a1405ae6e..5c56064cc 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -33,6 +33,10 @@ void MicroPython::ExecutionEnvironment::runCode(const char * str) { assert(sCurrentExecutionEnvironment == nullptr); sCurrentExecutionEnvironment = this; + /* Set the user interruption now, as it is needed for the normal execution and + * for the exception handling (because of print). */ + mp_hal_set_interrupt_char((int)Ion::Keyboard::Key::Back); + nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_lexer_t *lex = mp_lexer_new_from_str_len(0, str, strlen(str), false); @@ -41,9 +45,7 @@ void MicroPython::ExecutionEnvironment::runCode(const char * str) { // TODO: add a parameter when other input types (file, eval) are required mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_SINGLE_INPUT); mp_obj_t module_fun = mp_compile(&pt, lex->source_name, MP_EMIT_OPT_NONE, true); - mp_hal_set_interrupt_char((int)Ion::Keyboard::Key::Back); mp_call_function_0(module_fun); - mp_hal_set_interrupt_char(-1); // Disable interrupt nlr_pop(); } else { // Uncaught exception /* mp_obj_print_exception is supposed to handle error printing. However, @@ -81,6 +83,9 @@ void MicroPython::ExecutionEnvironment::runCode(const char * str) { /* End of mp_obj_print_exception. */ } + // Disable the user interruption + mp_hal_set_interrupt_char(-1); + assert(sCurrentExecutionEnvironment == this); sCurrentExecutionEnvironment = nullptr; }