Merge remote-tracking branch 'upstream/master' into omega-hotfix

This commit is contained in:
Quentin Guidée
2020-02-20 23:41:50 +01:00
18 changed files with 92 additions and 40 deletions

View File

@@ -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];
}

View File

@@ -13,6 +13,7 @@ public:
ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController);
// Responder
void viewDidDisappear() override;
void didEnterResponderChain(Responder * previousFirstResponder) override;
//ListViewDataSource

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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(); }

View File

@@ -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. */

View File

@@ -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);

View File

@@ -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<HistoryViewCell *>(m_selectableTableView.selectedCell());
*previousCell = static_cast<HistoryViewCell *>(m_selectableTableView.cellAtLocation(previousSelectedCellX, previousSelectedCellY));

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -3,6 +3,7 @@
#include "shared/continuous_function.h"
#include <escher/metric.h>
#include <ion/unicode/utf8_decoder.h>
#include <poincare/exception_checkpoint.h>
#include <poincare/layout_helper.h>
#include <poincare/matrix_layout.h>
#include <poincare/preferences.h>
@@ -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 {

View File

@@ -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

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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;
}