Fixed some conflicts

This commit is contained in:
Quentin Guidée
2020-02-12 17:42:58 +01:00
687 changed files with 13048 additions and 4057 deletions

View File

@@ -31,11 +31,11 @@ endef
define rule_for
ifeq ($(strip $(5)),with_local_version)
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3)))
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3))) | $(if $(findstring official,${MAKECMDGOALS}),official_authorization)
@ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
$(Q) $(4)
endif
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/.
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. $(if $(findstring official,${MAKECMDGOALS}),official_authorization)
@ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
$(Q) $(4)
endef

View File

@@ -31,6 +31,10 @@ app_src += $(addprefix apps/,\
variable_box_empty_controller.cpp \
)
tests_src += apps/exam_mode_configuration_official.cpp
apps_official += apps/exam_mode_configuration_official.cpp
apps_non_official += apps/exam_mode_configuration_non_official.cpp
apps_launch_on_boarding_src += apps/apps_container_launch_on_boarding.cpp
apps_launch_default_src += apps/apps_container_launch_default.cpp
apps_prompt_none_src += apps/apps_container_prompt_none.cpp
@@ -90,12 +94,13 @@ $(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp
$(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png))
all_app_src = $(app_src) $(epsilon_src) $(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(tests_src)
all_app_src = $(app_src) $(epsilon_src) $(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src)
$(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h
$(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h
apps_tests_src = $(app_calculation_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_solver_test_src)
apps_tests_src = $(app_calculation_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src)
apps_tests_src += $(addprefix apps/,\
global_preferences.cpp \
)
@@ -110,10 +115,15 @@ $(foreach img,$(image_list), $(eval $(call rule_for, \
# Configure variants
apps_all_src = $(app_src)
apps_all_src += $(apps_launch_default_src) $(apps_launch_on_boarding_src
apps_all_src += $(apps_official) $(apps_non_official)
apps_all_src += $(apps_launch_default_src) $(apps_launch_on_boarding_src)
apps_all_src += $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src)
apps_default_src = $(app_src) $(apps_launch_default_src) $(apps_prompt_none_src)
apps_onboarding_src = $(app_src) $(apps_launch_on_boarding_src) $(apps_prompt_none_src)
apps_onboarding_update_src = $(app_src) $(apps_launch_on_boarding_src) $(apps_prompt_update_src)
apps_onboarding_beta_src = $(app_src) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src)
apps_default_src = $(app_src) $(apps_non_official) $(apps_launch_default_src) $(apps_prompt_none_src)
apps_official_default_src = $(app_src) $(apps_official) $(apps_launch_default_src) $(apps_prompt_none_src)
apps_onboarding_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src)
apps_official_onboarding_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src)
apps_onboarding_update_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src)
apps_official_onboarding_update_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src)
apps_onboarding_beta_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src)
apps_official_onboarding_beta_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src)

View File

@@ -1,6 +1,7 @@
#include "apps_container.h"
#include "apps_container_storage.h"
#include "global_preferences.h"
#include "exam_mode_configuration.h"
#include <ion.h>
#include <poincare/init.h>
#include <poincare/exception_checkpoint.h>
@@ -35,7 +36,7 @@ AppsContainer::AppsContainer() :
m_hardwareTestSnapshot(),
m_usbConnectedSnapshot()
{
m_emptyBatteryWindow.setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height));
m_emptyBatteryWindow.setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height), false);
#if __EMSCRIPTEN__
/* AppsContainer::poincareCircuitBreaker uses Ion::Keyboard::scan(), which
* calls emscripten_sleep. If we set the poincare circuit breaker, we would
@@ -246,7 +247,7 @@ bool AppsContainer::switchTo(App::Snapshot * snapshot) {
void AppsContainer::run() {
KDRect screenRect = KDRect(0, 0, Ion::Display::Width, Ion::Display::Height);
window()->setFrame(screenRect);
window()->setFrame(screenRect, false);
/* We push a white screen here, because fetching the exam mode takes some time
* and it is visible when reflashing a N0100 (there is some noise on the
* screen before the logo appears). */
@@ -363,6 +364,7 @@ void AppsContainer::redrawWindow(bool force) {
}
void AppsContainer::activateExamMode(GlobalPreferences::ExamMode examMode) {
<<<<<<< HEAD
assert(examMode == GlobalPreferences::ExamMode::Standard || examMode == GlobalPreferences::ExamMode::Dutch || examMode == GlobalPreferences::ExamMode::NoSym);
reset();
Preferences * preferences = Preferences::sharedPreferences();
@@ -397,6 +399,11 @@ void AppsContainer::activateExamMode(GlobalPreferences::ExamMode examMode) {
* confusing states when the battery is charging and states when the Dutch
* exam mode is on. */
// Ion::LED::setColor(examMode == GlobalPreferences::ExamMode::Dutch ? KDColorYellow : KDColorRed);
=======
assert(examMode != GlobalPreferences::ExamMode::Off && examMode != GlobalPreferences::ExamMode::Unknown);
reset();
Ion::LED::setColor(ExamModeConfiguration::examModeColor(examMode));
>>>>>>> upstream/master
Ion::LED::setBlinking(1000, 0.1f);
}

View File

@@ -58,11 +58,11 @@ View * AppsWindow::subviewAtIndex(int index) {
return m_contentView;
}
void AppsWindow::layoutSubviews() {
void AppsWindow::layoutSubviews(bool force) {
KDCoordinate titleHeight = m_hideTitleBarView ? 0 : Metric::TitleBarHeight;
m_titleBarView.setFrame(KDRect(0, 0, bounds().width(), titleHeight));
m_titleBarView.setFrame(KDRect(0, 0, bounds().width(), titleHeight), force);
if (m_contentView != nullptr) {
m_contentView->setFrame(KDRect(0, titleHeight, bounds().width(), bounds().height()-titleHeight));
m_contentView->setFrame(KDRect(0, titleHeight, bounds().width(), bounds().height()-titleHeight), force);
}
}

View File

@@ -17,7 +17,7 @@ public:
void hideTitleBarView(bool hide);
private:
int numberOfSubviews() const override;
void layoutSubviews() override;
void layoutSubviews(bool force = false) override;
View * subviewAtIndex(int index) override;
TitleBarView m_titleBarView;
bool m_hideTitleBarView;

View File

@@ -7,12 +7,25 @@ app_calculation_test_src += $(addprefix apps/calculation/,\
)
app_calculation_src = $(addprefix apps/calculation/,\
additional_outputs/complex_graph_cell.cpp \
additional_outputs/complex_model.cpp \
additional_outputs/complex_list_controller.cpp \
additional_outputs/expression_with_equal_sign_view.cpp \
additional_outputs/expressions_list_controller.cpp \
additional_outputs/illustrated_list_controller.cpp \
additional_outputs/illustration_cell.cpp \
additional_outputs/integer_list_controller.cpp \
additional_outputs/scrollable_three_expressions_cell.cpp \
additional_outputs/list_controller.cpp \
additional_outputs/rational_list_controller.cpp \
additional_outputs/trigonometry_graph_cell.cpp \
additional_outputs/trigonometry_list_controller.cpp \
additional_outputs/trigonometry_model.cpp \
app.cpp \
edit_expression_controller.cpp \
expression_field.cpp \
history_view_cell.cpp \
history_controller.cpp \
scrollable_expression_view.cpp \
selectable_table_view.cpp \
)

View File

@@ -0,0 +1,98 @@
#include "complex_graph_cell.h"
using namespace Shared;
using namespace Poincare;
namespace Calculation {
ComplexGraphView::ComplexGraphView(ComplexModel * complexModel) :
CurveView(complexModel),
m_complex(complexModel)
{
}
void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(rect, KDColorWhite);
// Draw grid, axes and graduations
drawGrid(ctx, rect);
drawAxes(ctx, rect);
drawLabelsAndGraduations(ctx, rect, Axis::Vertical, true);
drawLabelsAndGraduations(ctx, rect, Axis::Horizontal, true);
float real = m_complex->real();
float imag = m_complex->imag();
assert(!std::isnan(real) && !std::isnan(imag) && !std::isinf(real) && !std::isinf(imag));
/* Draw the segment from the origin to the dot (real, imag) of equation
* x(t) = t*real and y(t) = t*imag with t in [0,1] */
drawCurve(ctx, rect, 0.0f, 1.0f, 0.01f,
[](float t, void * model, void * context) {
ComplexModel * complexModel = (ComplexModel *)model;
return Poincare::Coordinate2D<float>(complexModel->real()*t, complexModel->imag()*t);
}, m_complex, nullptr, false, Palette::GreyDark, false);
/* Draw the partial ellipse indicating the angle θ
* - the ellipse parameters are a = |real|/5 and b = |imag|/5,
* - the parametric ellipse equation is x(t) = a*cos(th*t) and y(t) = b*sin(th*t)
* with th computed in order to be the intersection of the line forming an
* angle θ with the abscissa and the ellipsis
* - we draw the ellipse for t in [0,1] to represent it from the abscissa axis
* to the phase of the complex
*/
/* Compute th: th is the intersection of ellipsis of equation (a*cos(t), b*sin(t))
* and the line of equation (real*t,imag*t).
* (a*cos(t), b*sin(t)) = (real*t,imag*t) --> tan(t) = sign(a)*sign(b) (± π)
* --> t = π/4 [π/2] according to sign(a) and sign(b). */
float th = real < 0.0f ? 3.0f*M_PI/4.0f : M_PI/4.0f;
th = imag < 0.0f ? -th : th;
// Compute ellipsis parameters a and b
float factor = 5.0f;
float a = std::fabs(real)/factor;
float b = std::fabs(imag)/factor;
// Avoid flat ellipsis for edge cases (for real = 0, the case imag = 0 is excluded)
if (real == 0.0f) {
a = 1.0f/factor;
th = imag < 0.0f ? -M_PI/2.0f : M_PI/2.0f;
}
std::complex<float> parameters(a,b);
drawCurve(ctx, rect, 0.0f, 1.0f, 0.01f,
[](float t, void * model, void * context) {
std::complex<float> parameters = *(std::complex<float> *)model;
float th = *(float *)context;
float a = parameters.real();
float b = parameters.imag();
return Poincare::Coordinate2D<float>(a*std::cos(t*th), b*std::sin(t*th));
}, &parameters, &th, false, Palette::GreyDark, false);
// Draw dashed segment to indicate real and imaginary
drawSegment(ctx, rect, Axis::Vertical, real, 0.0f, imag, Palette::Red, 1, 3);
drawSegment(ctx, rect, Axis::Horizontal, imag, 0.0f, real, Palette::Red, 1, 3);
// Draw complex position on the plan
drawDot(ctx, rect, real, imag, Palette::Red, Size::Large);
// Draw labels
// 're(z)' label
drawLabel(ctx, rect, real, 0.0f, "re(z)", Palette::Red, CurveView::RelativePosition::None, imag >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
// 'im(z)' label
drawLabel(ctx, rect, 0.0f, imag, "im(θ)", Palette::Red, real >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
// '|z|' label, the relative horizontal position of this label depends on the quadrant
CurveView::RelativePosition verticalPosition = real*imag < 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After;
if (real == 0.0f) {
// Edge case: pure imaginary
verticalPosition = CurveView::RelativePosition::None;
}
drawLabel(ctx, rect, real/2.0f, imag/2.0f, "|z|", Palette::Red, CurveView::RelativePosition::None, verticalPosition);
// 'arg(z)' label, the absolute and relative horizontal/vertical positions of this label depends on the quadrant
CurveView::RelativePosition horizontalPosition = real >= 0.0f ? CurveView::RelativePosition::After : CurveView::RelativePosition::None;
verticalPosition = imag >= 0.0f ? CurveView::RelativePosition::After : CurveView::RelativePosition::Before;
/* anglePositionRatio is the ratio of the angle where we position the label
* For the right half plan, we position the label close to the abscissa axis
* and for the left half plan, we position the label at the half angle. The
* relative position is chosen accordingly. */
float anglePositionRatio = real >= 0.0f ? 0.0f : 0.5f;
drawLabel(ctx, rect, a*std::cos(anglePositionRatio*th), b*std::sin(anglePositionRatio*th), "arg(z)", Palette::Red, horizontalPosition, verticalPosition);
}
}

View File

@@ -0,0 +1,37 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H
#define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H
#include "../../shared/curve_view.h"
#include "complex_model.h"
#include "illustration_cell.h"
namespace Calculation {
class ComplexGraphView : public Shared::CurveView {
public:
ComplexGraphView(ComplexModel * complexModel);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override {
return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]);
}
// '-' + significant digits + ".E-" + 2 digits (the represented dot is a float, so it is bounded by 1E38 and 1E-38
size_t labelMaxGlyphLengthSize() const override { return 1 + Poincare::Preferences::VeryShortNumberOfSignificantDigits + 3 + 2; }
char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
ComplexModel * m_complex;
};
class ComplexGraphCell : public IllustrationCell {
public:
ComplexGraphCell(ComplexModel * complexModel) : m_view(complexModel) {}
void reload() { m_view.reload(); }
private:
View * view() override { return &m_view; }
ComplexGraphView m_view;
};
}
#endif

View File

@@ -0,0 +1,44 @@
#include "complex_list_controller.h"
#include "../app.h"
#include "../../shared/poincare_helpers.h"
#include <poincare/imaginary_part.h>
#include <poincare/real_part.h>
#include "complex_list_controller.h"
using namespace Poincare;
using namespace Shared;
namespace Calculation {
void ComplexListController::viewWillAppear() {
IllustratedListController::viewWillAppear();
m_complexGraphCell.reload(); // compute labels
}
void ComplexListController::setExpression(Poincare::Expression e) {
IllustratedListController::setExpression(e);
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
Poincare::Preferences::ComplexFormat currentComplexFormat = preferences->complexFormat();
if (currentComplexFormat == Poincare::Preferences::ComplexFormat::Real) {
// Temporary change complex format to avoid all additional expressions to be "unreal"
preferences->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
}
Poincare::Context * context = App::app()->localContext();
// Fill Calculation Store
m_calculationStore.push("im(z)", context);
m_calculationStore.push("re(z)", context);
m_calculationStore.push("arg(z)", context);
m_calculationStore.push("abs(z)", context);
// Set Complex illustration
// Compute a and b as in Expression::hasDefinedComplexApproximation to ensure the same defined result
float a = Shared::PoincareHelpers::ApproximateToScalar<float>(RealPart::Builder(e.clone()), context);
float b = Shared::PoincareHelpers::ApproximateToScalar<float>(ImaginaryPart::Builder(e.clone()), context);
m_model.setComplex(std::complex<float>(a,b));
// Reset complex format as before
preferences->setComplexFormat(currentComplexFormat);
}
}

View File

@@ -0,0 +1,30 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_LIST_CONTROLLER_H
#include "complex_graph_cell.h"
#include "complex_model.h"
#include "illustrated_list_controller.h"
namespace Calculation {
class ComplexListController : public IllustratedListController {
public:
ComplexListController(EditExpressionController * editExpressionController) :
IllustratedListController(nullptr, editExpressionController),
m_complexGraphCell(&m_model) {}
// ViewController
void viewWillAppear() override;
void setExpression(Poincare::Expression e) override;
private:
CodePoint expressionSymbol() const override { return 'z'; }
HighlightCell * illustrationCell() override { return &m_complexGraphCell; }
ComplexGraphCell m_complexGraphCell;
ComplexModel m_model;
};
}
#endif

View File

@@ -0,0 +1,43 @@
#include "complex_model.h"
namespace Calculation {
ComplexModel::ComplexModel(std::complex<float> c) :
Shared::CurveViewRange(),
std::complex<float>(c)
{
}
float ComplexModel::rangeBound(float direction, bool horizontal) const {
float minFactor = k_minVerticalMarginFactor;
float maxFactor = k_maxVerticalMarginFactor;
float value = imag();
if (horizontal) {
minFactor = k_minHorizontalMarginFactor;
maxFactor = k_maxHorizontalMarginFactor;
value = real();
}
if (std::isnan(value) || std::isinf(value) || value == 0.0f) {
return direction*maxFactor;
}
float factor = direction*value >= 0.0f ? maxFactor : minFactor;
return factor*value;
}
float ComplexModel::xMin() const {
return rangeBound(-1.0f, true);
}
float ComplexModel::xMax() const {
return rangeBound(1.0f, true);
}
float ComplexModel::yMin() const {
return rangeBound(-1.0f, false);
}
float ComplexModel::yMax() const {
return rangeBound(1.0f, false);
}
}

View File

@@ -0,0 +1,32 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_MODEL_H
#define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_MODEL_H
#include "../../shared/curve_view_range.h"
#include <complex>
namespace Calculation {
class ComplexModel : public Shared::CurveViewRange, public std::complex<float> {
public:
ComplexModel(std::complex<float> c = std::complex<float>(NAN, NAN));
// CurveViewRange
float xMin() const override;
float xMax() const override;
float yMin() const override;
float yMax() const override;
void setComplex(std::complex<float> c) { *this = ComplexModel(c); }
static constexpr float k_minVerticalMarginFactor = -0.5f;
static constexpr float k_maxVerticalMarginFactor = 1.2f;
static constexpr float k_minHorizontalMarginFactor = -1.0f;
static constexpr float k_maxHorizontalMarginFactor = 2.0f;
private:
float rangeBound(float direction, bool horizontal) const;
};
}
#endif

View File

@@ -0,0 +1,33 @@
#include "expression_with_equal_sign_view.h"
namespace Calculation {
KDSize ExpressionWithEqualSignView::minimalSizeForOptimalDisplay() const {
KDSize expressionSize = ExpressionView::minimalSizeForOptimalDisplay();
KDSize equalSize = m_equalSign.minimalSizeForOptimalDisplay();
return KDSize(expressionSize.width() + equalSize.width() + Metric::CommonLargeMargin, expressionSize.height());
}
void ExpressionWithEqualSignView::drawRect(KDContext * ctx, KDRect rect) const {
if (m_layout.isUninitialized()) {
return;
}
// Do not color the whole background to avoid coloring behind the equal symbol
KDSize expressionSize = ExpressionView::minimalSizeForOptimalDisplay();
ctx->fillRect(KDRect(0, 0, expressionSize), m_backgroundColor);
m_layout.draw(ctx, drawingOrigin(), m_textColor, m_backgroundColor, m_selectionStart, m_selectionEnd, Palette::Select);
}
View * ExpressionWithEqualSignView::subviewAtIndex(int index) {
assert(index == 0);
return &m_equalSign;
}
void ExpressionWithEqualSignView::layoutSubviews(bool force) {
KDSize expressionSize = ExpressionView::minimalSizeForOptimalDisplay();
KDSize equalSize = m_equalSign.minimalSizeForOptimalDisplay();
KDCoordinate expressionBaseline = layout().baseline();
m_equalSign.setFrame(KDRect(expressionSize.width() + Metric::CommonLargeMargin, expressionBaseline - equalSize.height()/2, equalSize), force);
}
}

View File

@@ -0,0 +1,26 @@
#ifndef CALCULATION_EXPRESSION_WITH_EQUAL_SIGN_VIEW_H
#define CALCULATION_EXPRESSION_WITH_EQUAL_SIGN_VIEW_H
#include <escher.h>
#include <apps/i18n.h>
#include <poincare/layout.h>
namespace Calculation {
class ExpressionWithEqualSignView : public ExpressionView {
public:
ExpressionWithEqualSignView() :
m_equalSign(KDFont::LargeFont, I18n::Message::Equal, 0.5f, 0.5f, KDColorBlack)
{}
KDSize minimalSizeForOptimalDisplay() const override;
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
int numberOfSubviews() const override { return 1; }
MessageTextView m_equalSign;
};
}
#endif

View File

@@ -0,0 +1,66 @@
#include "expressions_list_controller.h"
#include "../app.h"
using namespace Poincare;
namespace Calculation {
/* Expressions list controller */
ExpressionsListController::ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController) :
ListController(parentResponder, editExpressionController),
m_cells{}
{
for (int i = 0; i < k_maxNumberOfCells; i++) {
m_cells[i].setParentResponder(m_listController.selectableTableView());
}
}
void ExpressionsListController::didEnterResponderChain(Responder * previousFirstResponder) {
selectCellAtLocation(0, 0);
}
int ExpressionsListController::reusableCellCount(int type) {
return k_maxNumberOfCells;
}
HighlightCell * ExpressionsListController::reusableCell(int index, int type) {
return &m_cells[index];
}
KDCoordinate ExpressionsListController::rowHeight(int j) {
Layout l = layoutAtIndex(j);
if (l.isUninitialized()) {
return 0;
}
return l.layoutSize().height() + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness;
}
void ExpressionsListController::willDisplayCellForIndex(HighlightCell * cell, int index) {
ExpressionTableCellWithPointer * myCell = static_cast<ExpressionTableCellWithPointer *>(cell);
myCell->setLayout(layoutAtIndex(index));
myCell->setAccessoryMessage(messageAtIndex(index));
myCell->reloadScroll();
}
void ExpressionsListController::setExpression(Poincare::Expression e) {
// Reinitialize memoization
for (int i = 0; i < k_maxNumberOfCells; i++) {
m_layouts[i] = Layout();
}
m_expression = e;
}
Poincare::Layout ExpressionsListController::layoutAtIndex(int index) {
if (m_layouts[index].isUninitialized()) {
computeLayoutAtIndex(index);
}
assert(!m_layouts[index].isUninitialized());
return m_layouts[index];
}
int ExpressionsListController::textAtIndex(char * buffer, size_t bufferSize, int index) {
return m_layouts[index].serializeParsedExpression(buffer, bufferSize, App::app()->localContext());
}
}

View File

@@ -0,0 +1,45 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_EXPRESSIONS_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_EXPRESSIONS_LIST_CONTROLLER_H
#include <escher.h>
#include <poincare/expression.h>
#include <apps/i18n.h>
#include "list_controller.h"
namespace Calculation {
class ExpressionsListController : public ListController {
public:
ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController);
// Responder
void didEnterResponderChain(Responder * previousFirstResponder) override;
//ListViewDataSource
int reusableCellCount(int type) override;
HighlightCell * reusableCell(int index, int type) override;
KDCoordinate rowHeight(int j) override;
int typeAtLocation(int i, int j) override { return 0; }
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
// IllustratedListController
void setExpression(Poincare::Expression e) override;
protected:
constexpr static int k_maxNumberOfCells = 4;
virtual int textAtIndex(char * buffer, size_t bufferSize, int index) override;
Poincare::Expression m_expression;
// Memoization of layouts
mutable Poincare::Layout m_layouts[k_maxNumberOfCells];
private:
Poincare::Layout layoutAtIndex(int index);
virtual void computeLayoutAtIndex(int index) = 0;
virtual I18n::Message messageAtIndex(int index) = 0;
// Cells
ExpressionTableCellWithPointer m_cells[k_maxNumberOfCells];
};
}
#endif

View File

@@ -0,0 +1,128 @@
#include "illustrated_list_controller.h"
#include <poincare/symbol.h>
#include "../app.h"
using namespace Poincare;
namespace Calculation {
/* Illustrated list controller */
IllustratedListController::IllustratedListController(Responder * parentResponder, EditExpressionController * editExpressionController) :
ListController(parentResponder, editExpressionController, this),
m_additionalCalculationCells{}
{
for (int i = 0; i < k_maxNumberOfAdditionalCalculations; i++) {
m_additionalCalculationCells[i].setParentResponder(m_listController.selectableTableView());
}
}
void IllustratedListController::didEnterResponderChain(Responder * previousFirstResponder) {
// Select the left subview on all cells and reinitialize scroll
for (int i = 0; i < k_maxNumberOfAdditionalCalculations; i++) {
m_additionalCalculationCells[i].reinitSelection();
}
selectCellAtLocation(0, 1);
}
void IllustratedListController::viewDidDisappear() {
StackViewController::viewDidDisappear();
// Reset the context as it was before displaying the IllustratedListController
Poincare::Context * context = App::app()->localContext();
if (m_savedExpression.isUninitialized()) {
/* If no expression was stored in the symbol used by the
* IllustratedListController, we delete the record we stored */
char symbolName[3];
size_t length = UTF8Decoder::CodePointToChars(expressionSymbol(), symbolName, 3);
assert(length < 3);
symbolName[length] = 0;
const char * const extensions[2] = {"exp", "func"};
Ion::Storage::sharedStorage()->recordBaseNamedWithExtensions(symbolName, extensions, 2).destroy();
} else {
Poincare::Symbol s = Poincare::Symbol::Builder(expressionSymbol());
context->setExpressionForSymbolAbstract(m_savedExpression, s);
}
}
int IllustratedListController::numberOfRows() const {
return m_calculationStore.numberOfCalculations() + 1;
}
int IllustratedListController::reusableCellCount(int type) {
assert(type < 2);
if (type == 0) {
return 1;
}
return k_maxNumberOfAdditionalCalculations;
}
HighlightCell * IllustratedListController::reusableCell(int index, int type) {
assert(type < 2);
assert(index >= 0);
if (type == 0) {
return illustrationCell();
}
return &m_additionalCalculationCells[index];
}
KDCoordinate IllustratedListController::rowHeight(int j) {
if (j == 0) {
return k_illustrationHeight;
}
int calculationIndex = j-1;
if (calculationIndex >= m_calculationStore.numberOfCalculations()) {
return 0;
}
Shared::ExpiringPointer<Calculation> calculation = m_calculationStore.calculationAtIndex(calculationIndex);
return calculation->height(App::app()->localContext(), true, true) + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness;
}
int IllustratedListController::typeAtLocation(int i, int j) {
return j == 0 ? 0 : 1;
}
void IllustratedListController::willDisplayCellForIndex(HighlightCell * cell, int index) {
if (index == 0) {
// TODO ?
return;
}
Poincare::Context * context = App::app()->localContext();
ScrollableThreeExpressionsCell * myCell = (ScrollableThreeExpressionsCell *)cell;
Calculation * c = m_calculationStore.calculationAtIndex(index-1).pointer();
myCell->setCalculation(c);
myCell->setDisplayCenter(c->displayOutput(context) != Calculation::DisplayOutput::ApproximateOnly);
//myCell->setHighlighted(myCell->isHighlighted()); //TODO??
}
void IllustratedListController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
if (withinTemporarySelection) {
return;
}
// Forbid selecting Illustration cell
if (t->selectedRow() == 0) {
t->selectCellAtLocation(0, 1);
}
/* But scroll to the top when we select the first
* ScrollableThreeExpressionsCell in order display the
* illustration cell. */
if (t->selectedRow() == 1) {
t->scrollToCell(0, 0);
}
}
void IllustratedListController::setExpression(Poincare::Expression e) {
m_calculationStore.deleteAll();
Poincare::Context * context = App::app()->localContext();
Poincare::Symbol s = Poincare::Symbol::Builder(expressionSymbol());
m_savedExpression = context->expressionForSymbolAbstract(s, false);
context->setExpressionForSymbolAbstract(e, s);
}
int IllustratedListController::textAtIndex(char * buffer, size_t bufferSize, int index) {
ScrollableThreeExpressionsCell * myCell = static_cast<ScrollableThreeExpressionsCell *>(m_listController.selectableTableView()->selectedCell());
Shared::ExpiringPointer<Calculation> c = m_calculationStore.calculationAtIndex(index-1);
const char * text = myCell->selectedSubviewPosition() == ScrollableThreeExpressionsView::SubviewPosition::Right ? c->approximateOutputText(Calculation::NumberOfSignificantDigits::Maximal) : c->exactOutputText();
return strlcpy(buffer, text, bufferSize);
}
}

View File

@@ -0,0 +1,50 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_ILLUSTRATED_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_ILLUSTRATED_LIST_CONTROLLER_H
#include <escher.h>
#include "scrollable_three_expressions_cell.h"
#include "list_controller.h"
#include "../calculation_store.h"
#include <apps/i18n.h>
namespace Calculation {
class IllustratedListController : public ListController, public SelectableTableViewDelegate {
public:
IllustratedListController(Responder * parentResponder, EditExpressionController * editExpressionController);
// Responder
void viewDidDisappear() override;
void didEnterResponderChain(Responder * previousFirstResponder) override;
//ListViewDataSource
int numberOfRows() const override;
int reusableCellCount(int type) override;
HighlightCell * reusableCell(int index, int type) override;
KDCoordinate rowHeight(int j) override;
int typeAtLocation(int i, int j) override;
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
// SelectableTableViewDelegate
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
// IllustratedListController
void setExpression(Poincare::Expression e) override;
constexpr static KDCoordinate k_illustrationHeight = 100;
protected:
Poincare::Expression m_savedExpression;
CalculationStore m_calculationStore;
private:
int textAtIndex(char * buffer, size_t bufferSize, int index) override;
virtual CodePoint expressionSymbol() const = 0;
constexpr static int k_maxNumberOfAdditionalCalculations = 4;
// Cells
virtual HighlightCell * illustrationCell() = 0;
ScrollableThreeExpressionsCell m_additionalCalculationCells[k_maxNumberOfAdditionalCalculations];
};
}
#endif

View File

@@ -0,0 +1,16 @@
#include "illustration_cell.h"
using namespace Shared;
using namespace Poincare;
namespace Calculation {
void IllustrationCell::layoutSubviews(bool force) {
view()->setFrame(KDRect(Metric::CellSeparatorThickness, Metric::CellSeparatorThickness, bounds().width() - 2*Metric::CellSeparatorThickness, bounds().height() - 2*Metric::CellSeparatorThickness), force);
}
void IllustrationCell::drawRect(KDContext * ctx, KDRect rect) const {
drawBorderOfRect(ctx, bounds(), Palette::GreyBright);
}
}

View File

@@ -0,0 +1,23 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_ILLUSTRATION_CELL_H
#define CALCULATION_ADDITIONAL_OUTPUTS_ILLUSTRATION_CELL_H
#include <escher/bordered.h>
#include <escher/highlight_cell.h>
namespace Calculation {
class IllustrationCell : public Bordered, public HighlightCell {
public:
void setHighlighted(bool highlight) override { return; }
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
int numberOfSubviews() const override { return 1; }
View * subviewAtIndex(int index) override { return view(); }
void layoutSubviews(bool force = false) override;
virtual View * view() = 0;
};
}
#endif

View File

@@ -0,0 +1,70 @@
#include "integer_list_controller.h"
#include <poincare/based_integer.h>
#include <poincare/integer.h>
#include <poincare/empty_layout.h>
#include <poincare/factor.h>
#include "../app.h"
#include "../../shared/poincare_helpers.h"
using namespace Poincare;
using namespace Shared;
namespace Calculation {
int IntegerListController::numberOfRows() const {
return 3 + factorExpressionIsComputable();
}
Integer::Base baseAtIndex(int index) {
switch (index) {
case 0:
return Integer::Base::Decimal;
case 1:
return Integer::Base::Hexadecimal;
default:
assert(index == 2);
return Integer::Base::Binary;
}
}
void IntegerListController::computeLayoutAtIndex(int index) {
if (!m_layouts[index].isUninitialized()) {
return;
}
assert(m_expression.type() == ExpressionNode::Type::BasedInteger);
// For index = k_indexOfFactorExpression, the layout is assumed to be alreday memoized because it is needed to compute the numberOfRows
assert(index < k_indexOfFactorExpression);
Integer i = static_cast<BasedInteger &>(m_expression).integer();
m_layouts[index] = i.createLayout(baseAtIndex(index));
}
I18n::Message IntegerListController::messageAtIndex(int index) {
switch (index) {
case 0:
return I18n::Message::DecimalBase;
case 1:
return I18n::Message::HexadecimalBase;
case 2:
return I18n::Message::BinaryBase;
default:
return I18n::Message::PrimeFactors;
}
}
bool IntegerListController::factorExpressionIsComputable() const {
if (!m_layouts[k_indexOfFactorExpression].isUninitialized()) {
// The factor expression is already memoized
return !m_layouts[k_indexOfFactorExpression].isEmpty();
}
Poincare::Context * context = App::app()->localContext();
Expression factor = Factor::Builder(m_expression.clone());
PoincareHelpers::Simplify(&factor, context, ExpressionNode::ReductionTarget::User);
if (!factor.isUndefined()) {
m_layouts[k_indexOfFactorExpression] = PoincareHelpers::CreateLayout(factor);
return true;
}
m_layouts[k_indexOfFactorExpression] = EmptyLayout::Builder();
return false;
}
}

View File

@@ -0,0 +1,26 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_INTEGER_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_INTEGER_LIST_CONTROLLER_H
#include "expressions_list_controller.h"
namespace Calculation {
class IntegerListController : public ExpressionsListController {
public:
IntegerListController(EditExpressionController * editExpressionController) :
ExpressionsListController(nullptr, editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;
private:
static constexpr int k_indexOfFactorExpression = 3;
void computeLayoutAtIndex(int index) override;
I18n::Message messageAtIndex(int index) override;
bool factorExpressionIsComputable() const;
};
}
#endif

View File

@@ -0,0 +1,47 @@
#include "list_controller.h"
#include "../edit_expression_controller.h"
using namespace Poincare;
namespace Calculation {
/* Inner List Controller */
ListController::InnerListController::InnerListController(ListController * dataSource, SelectableTableViewDelegate * delegate) :
ViewController(dataSource),
m_selectableTableView(this, dataSource, dataSource, delegate)
{
m_selectableTableView.setMargins(0);
m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None);
}
void ListController::InnerListController::didBecomeFirstResponder() {
m_selectableTableView.reloadData();
}
/* List Controller */
ListController::ListController(Responder * parentResponder, EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate) :
StackViewController(parentResponder, &m_listController, KDColorWhite, Palette::PurpleBright, Palette::PurpleDark),
m_listController(this, delegate),
m_editExpressionController(editExpressionController)
{
}
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);
Container::activeApp()->dismissModalViewController();
Container::activeApp()->setFirstResponder(m_editExpressionController);
return true;
}
return false;
}
void ListController::didBecomeFirstResponder() {
Container::activeApp()->setFirstResponder(&m_listController);
}
}

View File

@@ -0,0 +1,41 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_LIST_CONTROLLER_H
#include <escher.h>
#include <apps/i18n.h>
namespace Calculation {
class EditExpressionController;
class ListController : public StackViewController, public ListViewDataSource, public SelectableTableViewDataSource {
public:
ListController(Responder * parentResponder, EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate = nullptr);
// Responder
bool handleEvent(Ion::Events::Event event) override;
void didBecomeFirstResponder() override;
// ListController
virtual void setExpression(Poincare::Expression e) = 0;
protected:
class InnerListController : public ViewController {
public:
InnerListController(ListController * dataSource, SelectableTableViewDelegate * delegate = nullptr);
const char * title() override { return I18n::translate(I18n::Message::AdditionalResults); }
View * view() override { return &m_selectableTableView; }
void didBecomeFirstResponder() override;
SelectableTableView * selectableTableView() { return &m_selectableTableView; }
private:
SelectableTableView m_selectableTableView;
};
virtual int textAtIndex(char * buffer, size_t bufferSize, int index) = 0;
InnerListController m_listController;
EditExpressionController * m_editExpressionController;
};
}
#endif

View File

@@ -0,0 +1,63 @@
#include "rational_list_controller.h"
#include "../app.h"
#include "../../shared/poincare_helpers.h"
#include <poincare_nodes.h>
#include <string.h>
using namespace Poincare;
using namespace Shared;
namespace Calculation {
int RationalListController::numberOfRows() const {
return 2;
}
Integer extractInteger(const Expression e) {
assert(e.type() == ExpressionNode::Type::BasedInteger);
return static_cast<const BasedInteger &>(e).integer();
}
void RationalListController::computeLayoutAtIndex(int index) {
bool negative = false;
Expression div = m_expression;
if (m_expression.type() == ExpressionNode::Type::Opposite) {
negative = true;
div = m_expression.childAtIndex(0);
}
assert(div.type() == ExpressionNode::Type::Division);
Integer numerator = extractInteger(div.childAtIndex(0));
numerator.setNegative(negative);
Integer denominator = extractInteger(div.childAtIndex(1));
Expression e;
if (index == 0) {
e = Integer::CreateMixedFraction(numerator, denominator);
} else {
assert(index == 1);
e = Integer::CreateEuclideanDivision(numerator, denominator);
}
m_layouts[index] = PoincareHelpers::CreateLayout(e);
}
I18n::Message RationalListController::messageAtIndex(int index) {
switch (index) {
case 0:
return I18n::Message::MixedFraction;
default:
return I18n::Message::EuclideanDivision;
}
}
int RationalListController::textAtIndex(char * buffer, size_t bufferSize, int index) {
int length = ExpressionsListController::textAtIndex(buffer, bufferSize, index);
if (index == 1) {
// Get rid of the left part of the equality
char * equalPosition = strchr(buffer, '=');
assert(equalPosition != nullptr);
strlcpy(buffer, equalPosition + 1, bufferSize);
return buffer + length - 1 - equalPosition;
}
return length;
}
}

View File

@@ -0,0 +1,25 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_RATIONAL_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_RATIONAL_LIST_CONTROLLER_H
#include "expressions_list_controller.h"
namespace Calculation {
class RationalListController : public ExpressionsListController {
public:
RationalListController(EditExpressionController * editExpressionController) :
ExpressionsListController(nullptr, editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;
private:
void computeLayoutAtIndex(int index) override;
I18n::Message messageAtIndex(int index) override;
int textAtIndex(char * buffer, size_t bufferSize, int index) override;
};
}
#endif

View File

@@ -0,0 +1,84 @@
#include "scrollable_three_expressions_cell.h"
#include <poincare/exception_checkpoint.h>
#include "../app.h"
namespace Calculation {
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());
// Create the input layout
Poincare::Layout inputLayout = calculation->createInputLayout();
// Create the exact output layout
Poincare::Layout exactOutputLayout = Poincare::Layout();
if (Calculation::DisplaysExact(calculation->displayOutput(context))) {
bool couldNotCreateExactLayout = false;
exactOutputLayout = calculation->createExactOutputLayout(&couldNotCreateExactLayout);
if (couldNotCreateExactLayout) {
if (calculation->displayOutput(context) == ::Calculation::Calculation::DisplayOutput::ExactOnly) {
Poincare::ExceptionCheckpoint::Raise();
} else {
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
}
}
}
Calculation::DisplayOutput displayOutput = calculation->displayOutput(context);
// Create the approximate output layout
Poincare::Layout approximateOutputLayout = Poincare::Layout();
if (displayOutput == Calculation::DisplayOutput::ExactOnly) {
approximateOutputLayout = exactOutputLayout;
} else {
bool couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
if (calculation->displayOutput(context) == ::Calculation::Calculation::DisplayOutput::ApproximateOnly) {
Poincare::ExceptionCheckpoint::Raise();
} else {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
exactOutputLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
Poincare::ExceptionCheckpoint::Raise();
}
}
}
}
setLayouts(inputLayout, exactOutputLayout, approximateOutputLayout);
I18n::Message equalMessage = calculation->exactAndApproximateDisplayedOutputsAreEqual(context) == Calculation::EqualSign::Equal ? I18n::Message::Equal : I18n::Message::AlmostEqual;
setEqualMessage(equalMessage);
/* The displayed input and outputs have changed. We need to re-layout the cell
* and re-initialize the scroll. */
layoutSubviews();
}
void ScrollableThreeExpressionsCell::didBecomeFirstResponder() {
reinitSelection();
Container::activeApp()->setFirstResponder(&m_view);
}
void ScrollableThreeExpressionsCell::reinitSelection() {
m_view.setSelectedSubviewPosition(ScrollableThreeExpressionsView::SubviewPosition::Left);
m_view.reloadScroll();
}
void ScrollableThreeExpressionsCell::setCalculation(Calculation * calculation) {
m_view.setCalculation(calculation);
layoutSubviews();
}
void ScrollableThreeExpressionsCell::setDisplayCenter(bool display) {
m_view.setDisplayCenter(display);
layoutSubviews();
}
}

View File

@@ -0,0 +1,67 @@
#ifndef CALCULATION_SCROLLABLE_THREE_EXPRESSIONS_CELL_H
#define CALCULATION_SCROLLABLE_THREE_EXPRESSIONS_CELL_H
#include <escher.h>
#include "../../shared/scrollable_multiple_expressions_view.h"
#include "../calculation.h"
#include "expression_with_equal_sign_view.h"
namespace Calculation {
class ScrollableThreeExpressionsView : public Shared::AbstractScrollableMultipleExpressionsView {
public:
ScrollableThreeExpressionsView(Responder * parentResponder) : Shared::AbstractScrollableMultipleExpressionsView(parentResponder, &m_contentCell), m_contentCell() {
setMargins(Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin); // Left Right margins are already added by TableCell
setBackgroundColor(KDColorWhite);
}
void setCalculation(Calculation * calculation);
private:
class ContentCell : public Shared::AbstractScrollableMultipleExpressionsView::ContentCell {
public:
ContentCell() : m_leftExpressionView() {}
KDColor backgroundColor() const override { return KDColorWhite; }
void setEven(bool even) override { return; }
ExpressionView * leftExpressionView() const override { return const_cast<ExpressionWithEqualSignView *>(&m_leftExpressionView); }
private:
ExpressionWithEqualSignView m_leftExpressionView;
};
ContentCell * contentCell() override { return &m_contentCell; };
const ContentCell * constContentCell() const override { return &m_contentCell; };
ContentCell m_contentCell;
};
class ScrollableThreeExpressionsCell : public TableCell, public Responder {
public:
ScrollableThreeExpressionsCell() :
Responder(nullptr),
m_view(this) {}
// Cell
Poincare::Layout layout() const override { return m_view.layout(); }
// Responder cell
Responder * responder() override {
return this;
}
void didBecomeFirstResponder() override;
// Table cell
View * labelView() const override { return (View *)&m_view; }
void setHighlighted(bool highlight) override { m_view.evenOddCell()->setHighlighted(highlight); }
void setCalculation(Calculation * calculation);
void setDisplayCenter(bool display);
ScrollableThreeExpressionsView::SubviewPosition selectedSubviewPosition() { return m_view.selectedSubviewPosition(); }
void setSelectedSubviewPosition(ScrollableThreeExpressionsView::SubviewPosition subviewPosition) { m_view.setSelectedSubviewPosition(subviewPosition); }
void reinitSelection();
private:
// Remove label margin added by TableCell because they're already handled by ScrollableThreeExpressionsView
KDCoordinate labelMargin() const override { return 0; }
ScrollableThreeExpressionsView m_view;
};
}
#endif

View File

@@ -0,0 +1,37 @@
#include "trigonometry_graph_cell.h"
using namespace Shared;
using namespace Poincare;
namespace Calculation {
TrigonometryGraphView::TrigonometryGraphView(TrigonometryModel * model) :
CurveView(model),
m_model(model)
{
}
void TrigonometryGraphView::drawRect(KDContext * ctx, KDRect rect) const {
float s = std::sin(m_model->angle());
float c = std::cos(m_model->angle());
ctx->fillRect(rect, KDColorWhite);
drawGrid(ctx, rect);
drawAxes(ctx, rect);
// Draw the circle
drawCurve(ctx, rect, 0.0f, 2.0f*M_PI, M_PI/180.0f, [](float t, void * model, void * context) {
return Poincare::Coordinate2D<float>(std::cos(t), std::sin(t));
}, nullptr, nullptr, true, Palette::GreyDark, false);
// Draw dashed segment to indicate sine and cosine
drawSegment(ctx, rect, Axis::Vertical, c, 0.0f, s, Palette::Red, 1, 3);
drawSegment(ctx, rect, Axis::Horizontal, s, 0.0f, c, Palette::Red, 1, 3);
// Draw angle position on the circle
drawDot(ctx, rect, c, s, Palette::Red, Size::Large);
// Draw graduations
drawLabelsAndGraduations(ctx, rect, Axis::Vertical, false, true);
drawLabelsAndGraduations(ctx, rect, Axis::Horizontal, false, true);
// Draw labels
drawLabel(ctx, rect, 0.0f, s, "sin(θ)", Palette::Red, c >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
drawLabel(ctx, rect, c, 0.0f, "cos(θ)", Palette::Red, CurveView::RelativePosition::None, s >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
}
}

View File

@@ -0,0 +1,30 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_TRIGONOMETRY_GRAPH_CELL_H
#define CALCULATION_ADDITIONAL_OUTPUTS_TRIGONOMETRY_GRAPH_CELL_H
#include "../../shared/curve_view.h"
#include "trigonometry_model.h"
#include "illustration_cell.h"
namespace Calculation {
class TrigonometryGraphView : public Shared::CurveView {
public:
TrigonometryGraphView(TrigonometryModel * model);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override { return nullptr; }
TrigonometryModel * m_model;
};
class TrigonometryGraphCell : public IllustrationCell {
public:
TrigonometryGraphCell(TrigonometryModel * model) : m_view(model) {}
private:
View * view() override { return &m_view; }
TrigonometryGraphView m_view;
};
}
#endif

View File

@@ -0,0 +1,23 @@
#include "trigonometry_list_controller.h"
#include "../app.h"
using namespace Poincare;
namespace Calculation {
void TrigonometryListController::setExpression(Poincare::Expression e) {
assert(e.type() == ExpressionNode::Type::Cosine || e.type() == ExpressionNode::Type::Sine);
IllustratedListController::setExpression(e.childAtIndex(0));
// Fill calculation store
Poincare::Context * context = App::app()->localContext();
m_calculationStore.push("sin(θ)", context);
m_calculationStore.push("cos(θ)", context);
m_calculationStore.push("θ", context);
// Set trigonometry illustration
float angle = Shared::PoincareHelpers::ApproximateToScalar<float>(m_calculationStore.calculationAtIndex(0)->approximateOutput(context, Calculation::NumberOfSignificantDigits::Maximal), context);
m_model.setAngle(angle);
}
}

View File

@@ -0,0 +1,25 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_TRIGONOMETRY_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_TRIGONOMETRY_LIST_CONTROLLER_H
#include "trigonometry_graph_cell.h"
#include "trigonometry_model.h"
#include "illustrated_list_controller.h"
namespace Calculation {
class TrigonometryListController : public IllustratedListController {
public:
TrigonometryListController(EditExpressionController * editExpressionController) :
IllustratedListController(nullptr, editExpressionController),
m_graphCell(&m_model) {}
void setExpression(Poincare::Expression e) override;
private:
CodePoint expressionSymbol() const override { return UCodePointGreekSmallLetterTheta; }
HighlightCell * illustrationCell() override { return &m_graphCell; }
TrigonometryGraphCell m_graphCell;
TrigonometryModel m_model;
};
}
#endif

View File

@@ -0,0 +1,11 @@
#include "trigonometry_model.h"
namespace Calculation {
TrigonometryModel::TrigonometryModel() :
Shared::CurveViewRange(),
m_angle(NAN)
{
}
}

View File

@@ -0,0 +1,40 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_TRIGONOMETRY_MODEL_H
#define CALCULATION_ADDITIONAL_OUTPUTS_TRIGONOMETRY_MODEL_H
#include "../../shared/curve_view_range.h"
#include "illustrated_list_controller.h"
#include <complex>
#include <poincare/trigonometry.h>
namespace Calculation {
class TrigonometryModel : public Shared::CurveViewRange {
public:
TrigonometryModel();
// CurveViewRange
float xMin() const override { return -k_xHalfRange; }
float xMax() const override { return k_xHalfRange; }
float yMin() const override { return yCenter() - yHalfRange(); }
float yMax() const override { return yCenter() + yHalfRange(); }
void setAngle(float f) { m_angle = f; }
float angle() const { return m_angle*M_PI/Poincare::Trigonometry::PiInAngleUnit(Poincare::Preferences::sharedPreferences()->angleUnit()); }
private:
constexpr static float k_xHalfRange = 2.1f;
// We center the yRange around the semi-circle where the angle is
float yCenter() const { return std::sin(angle()) >= 0.0f ? 0.5f : -0.5f; }
/* We want to normalize the displayed trigonometry circle:
* - On the X axis, we display 4.4 units on an available pixel width of
* (Ion::Display::Width - Metric::PopUpRightMargin - Metric::PopUpLeftMargin)
* - On the Y axis, the available pixel height is
* IllustratedListController::k_illustrationHeight
*/
float yHalfRange() const { return IllustratedListController::k_illustrationHeight*k_xHalfRange/(Ion::Display::Width - Metric::PopUpRightMargin - Metric::PopUpLeftMargin); }
float m_angle;
};
}
#endif

View File

@@ -66,7 +66,7 @@ bool App::layoutFieldDidReceiveEvent(::LayoutField * layoutField, Ion::Events::E
bool App::isAcceptableExpression(const Poincare::Expression expression) {
{
Expression ansExpression = static_cast<Snapshot *>(snapshot())->calculationStore()->ansExpression(localContext());
if (!TextFieldDelegateApp::ExpressionCanBeSerialized(expression, true, ansExpression)) {
if (!TextFieldDelegateApp::ExpressionCanBeSerialized(expression, true, ansExpression, localContext())) {
return false;
}
}

View File

@@ -34,7 +34,6 @@ public:
bool textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) override;
bool layoutFieldDidReceiveEvent(::LayoutField * layoutField, Ion::Events::Event event) override;
// TextFieldDelegateApp
bool isAcceptableExpression(const Poincare::Expression expression) override;
private:
App(Snapshot * snapshot);

View File

@@ -1,2 +1,9 @@
CalculApp = "Berechnung"
CalculAppCapital = "BERECHNUNG"
AdditionalResults = "????"
DecimalBase = "????"
HexadecimalBase = "????"
BinaryBase = "????"
PrimeFactors = "????"
MixedFraction = "????"
EuclideanDivision = "????"

View File

@@ -1,2 +1,9 @@
CalculApp = "Calculation"
CalculAppCapital = "CALCULATION"
AdditionalResults = "Additional results"
DecimalBase = "Decimal"
HexadecimalBase = "Hexadecimal"
BinaryBase = "Binary"
PrimeFactors = "Prime factors"
MixedFraction = "Mixed fraction"
EuclideanDivision = "Euclidean division"

View File

@@ -1,2 +1,9 @@
CalculApp = "Cálculo"
CalculAppCapital = "CÁLCULO"
AdditionalResults = "????"
DecimalBase = "????"
HexadecimalBase = "????"
BinaryBase = "????"
PrimeFactors = "????"
MixedFraction = "????"
EuclideanDivision = "????"

View File

@@ -1,2 +1,9 @@
CalculApp = "Calculs"
CalculAppCapital = "CALCULS"
AdditionalResults = "Résultats complémentaires"
DecimalBase = "Décimal"
HexadecimalBase = "Hexadécimal"
BinaryBase = "Binaire"
PrimeFactors = "Facteurs premiers"
MixedFraction = "Fraction mixte"
EuclideanDivision = "Division euclidienne"

View File

@@ -1,2 +1,9 @@
CalculApp = "Cálculo"
CalculAppCapital = "CÁLCULO"
AdditionalResults = "????"
DecimalBase = "????"
HexadecimalBase = "????"
BinaryBase = "????"
PrimeFactors = "????"
MixedFraction = "????"
EuclideanDivision = "????"

View File

@@ -1,6 +1,7 @@
#include "calculation.h"
#include "../shared/poincare_helpers.h"
#include "../global_preferences.h"
#include "../exam_mode_configuration.h"
#include <poincare/exception_checkpoint.h>
#include <poincare/undefined.h>
#include <poincare/unreal.h>
@@ -16,7 +17,8 @@ static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { retur
bool Calculation::operator==(const Calculation& c) {
return strcmp(inputText(), c.inputText()) == 0
&& strcmp(approximateOutputText(), c.approximateOutputText()) == 0
&& strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), c.approximateOutputText(NumberOfSignificantDigits::Maximal)) == 0
&& strcmp(approximateOutputText(NumberOfSignificantDigits::UserDefined), c.approximateOutputText(NumberOfSignificantDigits::UserDefined)) == 0
/* Some calculations can make appear trigonometric functions in their
* exact output. Their argument will be different with the angle unit
* preferences but both input and approximate output will be the same.
@@ -28,8 +30,8 @@ bool Calculation::operator==(const Calculation& c) {
Calculation * Calculation::next() const {
const char * result = reinterpret_cast<const char *>(this) + sizeof(Calculation);
for (int i = 0; i < 3; i++) {
result = result + strlen(result) + 1; // Pass inputText, exactOutputText, ApproximateOutputText
for (int i = 0; i < k_numberOfExpressions; i++) {
result = result + strlen(result) + 1; // Pass inputText, exactOutputText, ApproximateOutputText x2
}
return reinterpret_cast<Calculation *>(const_cast<char *>(result));
}
@@ -41,13 +43,17 @@ void Calculation::tidy() {
m_expandedHeight = -1;
}
const char * Calculation::approximateOutputText() const {
const char * Calculation::approximateOutputText(NumberOfSignificantDigits numberOfSignificantDigits) const {
const char * exactOutput = exactOutputText();
return exactOutput + strlen(exactOutput) + 1;
const char * approximateOutputTextWithMaxNumberOfDigits = exactOutput + strlen(exactOutput) + 1;
if (numberOfSignificantDigits == NumberOfSignificantDigits::Maximal) {
return approximateOutputTextWithMaxNumberOfDigits;
}
return approximateOutputTextWithMaxNumberOfDigits + strlen(approximateOutputTextWithMaxNumberOfDigits) + 1;
}
Expression Calculation::input() {
return Expression::Parse(m_inputText);
return Expression::Parse(m_inputText, nullptr);
}
Expression Calculation::exactOutput() {
@@ -55,64 +61,159 @@ Expression Calculation::exactOutput() {
* thereby avoid turning cos(Pi/4) into sqrt(2)/2 and displaying
* 'sqrt(2)/2 = 0.999906' (which is totally wrong) instead of
* 'cos(pi/4) = 0.999906' (which is true in degree). */
Expression exactOutput = Expression::Parse(exactOutputText());
Expression exactOutput = Expression::Parse(exactOutputText(), nullptr);
assert(!exactOutput.isUninitialized());
return exactOutput;
}
Expression Calculation::approximateOutput(Context * context) {
/* To ensure that the expression 'm_output' is a matrix or a complex, we
* call 'evaluate'. */
Expression exp = Expression::Parse(approximateOutputText());
Expression Calculation::approximateOutput(Context * context, NumberOfSignificantDigits numberOfSignificantDigits) {
Expression exp = Expression::Parse(approximateOutputText(numberOfSignificantDigits), nullptr);
assert(!exp.isUninitialized());
return PoincareHelpers::Approximate<double>(exp, context);
/* Warning:
* Since quite old versions of Epsilon, the Expression 'exp' was used to be
* approximated again to ensure its content was in the expected form - a
* linear combination of Decimal.
* However, since the approximate output may contain units and that a
* Poincare::Unit approximates to undef, thus it must not be approximated
* anymore.
* We have to keep two serializations of the approximation outputs:
* - one with the maximal significant digits, to be used by 'ans' or when
* handling 'OK' event on the approximation output.
* - one with the displayed number of significant digits that we parse to
* create the displayed layout. If we used the other serialization to
* create the layout, the result of the parsing could be an Integer which
* does not take the number of significant digits into account when creating
* its layout. This would lead to wrong number of significant digits in the
* layout.
* For instance:
* Number of asked significant digits: 7
* Input: "123456780", Approximate output: "1.234567E8"
*
* |--------------------------------------------------------------------------------------|
* | Number of significant digits | Approximate text | Parse expression | Layout |
* |------------------------------+------------------+---------------------+--------------|
* | Maximal | "123456780" | Integer(123456780) | "123456780" |
* |------------------------------+------------------+---------------------+--------------|
* | User defined | "1.234567E8" | Decimal(1.234567E8) | "1.234567E8" |
* |--------------------------------------------------------------------------------------|
*
*/
return exp;
}
Layout Calculation::createInputLayout() {
return input().createLayout(Preferences::PrintFloatMode::Decimal, PrintFloat::k_numberOfStoredSignificantDigits);
}
Layout Calculation::createExactOutputLayout() {
return PoincareHelpers::CreateLayout(exactOutput());
Layout Calculation::createExactOutputLayout(bool * couldNotCreateExactLayout) {
Poincare::ExceptionCheckpoint ecp;
if (ExceptionRun(ecp)) {
return PoincareHelpers::CreateLayout(exactOutput());
} else {
*couldNotCreateExactLayout = true;
return Layout();
}
}
Layout Calculation::createApproximateOutputLayout(Context * context) {
return PoincareHelpers::CreateLayout(approximateOutput(context));
Layout Calculation::createApproximateOutputLayout(Context * context, bool * couldNotCreateApproximateLayout) {
Poincare::ExceptionCheckpoint ecp;
if (ExceptionRun(ecp)) {
return PoincareHelpers::CreateLayout(approximateOutput(context, NumberOfSignificantDigits::UserDefined));
} else {
*couldNotCreateApproximateLayout = true;
return Layout();
}
}
KDCoordinate Calculation::height(Context * context, bool expanded) {
KDCoordinate result = expanded ? m_expandedHeight : m_height;
if (result < 0) {
DisplayOutput display = displayOutput(context);
Layout inputLayout = createInputLayout();
KDCoordinate inputHeight = inputLayout.layoutSize().height();
if (display == DisplayOutput::ExactOnly) {
KDCoordinate exactOutputHeight = createExactOutputLayout().layoutSize().height();
result = inputHeight+exactOutputHeight;
} else if (display == DisplayOutput::ApproximateOnly || (!expanded && display == DisplayOutput::ExactAndApproximateToggle)) {
KDCoordinate approximateOutputHeight = createApproximateOutputLayout(context).layoutSize().height();
result = inputHeight+approximateOutputHeight;
} else {
assert(display == DisplayOutput::ExactAndApproximate || (display == DisplayOutput::ExactAndApproximateToggle && expanded));
Layout approximateLayout = createApproximateOutputLayout(context);
Layout exactLayout = createExactOutputLayout();
KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height();
KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
KDCoordinate outputHeight = maxCoordinate(exactLayout.baseline(), approximateLayout.baseline()) + maxCoordinate(exactOutputHeight-exactLayout.baseline(), approximateOutputHeight-approximateLayout.baseline());
result = inputHeight + outputHeight;
KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpressionsInline) {
KDCoordinate result = expanded ? m_expandedHeight : m_height;
if (result >= 0) {
// Height already computed
return result;
}
// Get input height
Layout inputLayout = createInputLayout();
KDCoordinate inputHeight = inputLayout.layoutSize().height();
KDCoordinate inputBaseline = inputLayout.baseline();
// Get exact output height if needed
Poincare::Layout exactLayout;
bool couldNotCreateExactLayout = false;
if (DisplaysExact(displayOutput(context))) {
// Create the exact output layout
exactLayout = createExactOutputLayout(&couldNotCreateExactLayout);
if (couldNotCreateExactLayout) {
if (displayOutput(context) != DisplayOutput::ExactOnly) {
forceDisplayOutput(DisplayOutput::ApproximateOnly);
} else {
/* We should only display the exact result, but we cannot create it
* -> raise an exception. */
ExceptionCheckpoint::Raise();
}
}
/* For all display output except ExactAndApproximateToggle, the selected
* height and the usual height are identical. We update both heights in
* theses cases. */
if (display != DisplayOutput::ExactAndApproximateToggle) {
m_height = result;
}
if (displayOutput(context) == DisplayOutput::ExactOnly) {
KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
if (allExpressionsInline) {
KDCoordinate exactOutputBaseline = exactLayout.baseline();
result = maxCoordinate(inputBaseline, exactOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline);
} else {
result = inputHeight+exactOutputHeight;
}
} else {
bool couldNotCreateApproximateLayout = false;
Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
if (displayOutput(context) == DisplayOutput::ApproximateOnly) {
Poincare::ExceptionCheckpoint::Raise();
} else {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
forceDisplayOutput(DisplayOutput::ApproximateOnly);
exactLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
Poincare::ExceptionCheckpoint::Raise();
}
}
}
KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height();
if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) {
if (allExpressionsInline) {
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
result = maxCoordinate(inputBaseline, approximateOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline);
} else {
result = inputHeight+approximateOutputHeight;
}
} else {
assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded));
KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
KDCoordinate exactOutputBaseline = exactLayout.baseline();
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
if (allExpressionsInline) {
result = maxCoordinate(inputBaseline, maxCoordinate(exactOutputBaseline, approximateOutputBaseline)) + maxCoordinate(inputHeight - inputBaseline, maxCoordinate(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline));
} else {
KDCoordinate outputHeight = maxCoordinate(exactOutputBaseline, approximateOutputBaseline) + maxCoordinate(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline);
result = inputHeight + outputHeight;
}
}
}
/* 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 {
if (expanded) {
m_expandedHeight = result;
} else {
m_height = result;
}
m_height = result;
}
}
return result;
@@ -124,15 +225,31 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
}
if (shouldOnlyDisplayExactOutput()) {
m_displayOutput = DisplayOutput::ExactOnly;
// Force all results to be ApproximateOnly in Dutch exam mode
} else if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Dutch ||
} else if (
/* If the exact and approximate outputs are equal (with the
* UserDefined number of significant digits), do not display the exact
* output. Indeed, in this case, the layouts are identical. */
strcmp(exactOutputText(), approximateOutputText(NumberOfSignificantDigits::UserDefined)) == 0
||
// If the approximate output is 'unreal' or the exact result is 'undef'
strcmp(exactOutputText(), Undefined::Name()) == 0 ||
strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), Unreal::Name()) == 0
||
/* If the approximate output is 'undef' and the input and exactOutput are
* equal */
(strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), Undefined::Name()) == 0 &&
strcmp(inputText(), exactOutputText()) == 0)
||
// Force all outputs to be ApproximateOnly if required by the exam mode configuration
ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::sharedGlobalPreferences()->examMode())
||
/* If the input contains the following types, we only display the
* approximate output. */
input().recursivelyMatches(
[](const Expression e, Context * c) {
constexpr int approximateOnlyTypesCount = 9;
/* If the input contains the following types, we only display the
* approximate output. */
ExpressionNode::Type approximateOnlyTypes[approximateOnlyTypesCount] = {
ExpressionNode::Type approximateOnlyTypes[] = {
ExpressionNode::Type::Random,
ExpressionNode::Type::Unit,
ExpressionNode::Type::Round,
ExpressionNode::Type::FracPart,
ExpressionNode::Type::Integral,
@@ -142,27 +259,11 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
ExpressionNode::Type::ConfidenceInterval,
ExpressionNode::Type::PredictionInterval
};
return e.isOfType(approximateOnlyTypes, approximateOnlyTypesCount);
}, context, true))
return e.isOfType(approximateOnlyTypes, sizeof(approximateOnlyTypes)/sizeof(ExpressionNode::Type));
}, context, true)
)
{
m_displayOutput = DisplayOutput::ApproximateOnly;
} else if (strcmp(exactOutputText(), approximateOutputText()) == 0) {
/* If the exact and approximate results' texts are equal and their layouts
* too, do not display the exact result. If the two layouts are not equal
* because of the number of significant digits, we display both. */
m_displayOutput = exactAndApproximateDisplayedOutputsAreEqual(context) == Calculation::EqualSign::Equal ? DisplayOutput::ApproximateOnly : DisplayOutput::ExactAndApproximate;
} else if (strcmp(exactOutputText(), Undefined::Name()) == 0
|| strcmp(approximateOutputText(), Unreal::Name()) == 0
|| exactOutput().type() == ExpressionNode::Type::Undefined)
{
// If the approximate result is 'unreal' or the exact result is 'undef'
m_displayOutput = DisplayOutput::ApproximateOnly;
} else if (strcmp(approximateOutputText(), Undefined::Name()) == 0
&& strcmp(inputText(), exactOutputText()) == 0)
{
/* If the approximate result is 'undef' and the input and exactOutput are
* equal */
m_displayOutput = DisplayOutput::ApproximateOnly;
} else if (input().recursivelyMatches(Expression::IsApproximate, context)
|| exactOutput().recursivelyMatches(Expression::IsApproximate, context))
{
@@ -196,15 +297,9 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(
* Store in the exactOutput. */
Poincare::ExceptionCheckpoint ecp;
if (ExceptionRun(ecp)) {
constexpr int bufferSize = Constant::MaxSerializedExpressionSize + 30;
char buffer[bufferSize];
Preferences * preferences = Preferences::sharedPreferences();
Expression exactOutputExpression = PoincareHelpers::ParseAndSimplify(exactOutputText(), context, false);
if (exactOutputExpression.isUninitialized()) {
exactOutputExpression = Undefined::Builder();
}
Preferences::ComplexFormat complexFormat = Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), m_inputText);
m_equalSign = exactOutputExpression.isEqualToItsApproximationLayout(approximateOutput(context), buffer, bufferSize, complexFormat, preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), context) ? EqualSign::Equal : EqualSign::Approximation;
m_equalSign = Expression::ParsedExpressionsAreEqual(exactOutputText(), approximateOutputText(NumberOfSignificantDigits::UserDefined), context, complexFormat, preferences->angleUnit()) ? EqualSign::Equal : EqualSign::Approximation;
return m_equalSign;
} else {
/* Do not override m_equalSign in case there is enough room in the pool
@@ -213,4 +308,41 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(
}
}
Calculation::AdditionalInformationType Calculation::additionalInformationType(Context * context) {
Preferences * preferences = Preferences::sharedPreferences();
Preferences::ComplexFormat complexFormat = Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), m_inputText);
Expression i = input();
Expression o = exactOutput();
/* Special case for Equal and Store:
* Equal/Store nodes have to be at the root of the expression, which prevents
* from creating new expressions with equal/store node as a child. We don't
* return any additional outputs for them to avoid bothering with special
* cases. */
if (i.type() == ExpressionNode::Type::Equal || i.type() == ExpressionNode::Type::Store) {
return AdditionalInformationType::None;
}
/* Trigonometry additional results are displayed if either input or output is a sin or a cos. Indeed, we want to capture both cases:
* - > input: cos(60)
* > output: 1/2
* - > input: 2cos(2) - cos(2)
* > output: cos(2)
*/
if (input().isDefinedCosineOrSine(context, complexFormat, preferences->angleUnit()) || o.isDefinedCosineOrSine(context, complexFormat, preferences->angleUnit())) {
return AdditionalInformationType::Trigonometry;
}
// TODO: return AdditionalInformationType::Unit
if (o.isBasedIntegerCappedBy(k_maximalIntegerWithAdditionalInformation)) {
return AdditionalInformationType::Integer;
}
// Find forms like [12]/[23] or -[12]/[23]
if (o.isDivisionOfIntegers() || (o.type() == ExpressionNode::Type::Opposite && o.childAtIndex(0).isDivisionOfIntegers())) {
return AdditionalInformationType::Rational;
}
if (o.hasDefinedComplexApproximation(context, complexFormat, preferences->angleUnit())) {
return AdditionalInformationType::Complex;
}
return AdditionalInformationType::None;
}
}

View File

@@ -5,6 +5,7 @@
#include <escher.h>
#include <poincare/context.h>
#include <poincare/expression.h>
#include "../shared/poincare_helpers.h"
namespace Calculation {
@@ -18,6 +19,7 @@ class CalculationStore;
* */
class Calculation {
friend CalculationStore;
public:
enum class EqualSign : uint8_t {
Unknown,
@@ -32,6 +34,15 @@ public:
ExactAndApproximate,
ExactAndApproximateToggle
};
enum class AdditionalInformationType {
None = 0,
Integer,
Rational,
Trigonometry,
Unit,
Complex
};
static bool DisplaysExact(DisplayOutput d) { return d != DisplayOutput::ApproximateOnly; }
/* It is not really the minimal size, but it clears enough space for most
* calculations instead of clearing less space, then fail to serialize, clear
@@ -53,26 +64,40 @@ public:
void tidy();
// Texts
enum class NumberOfSignificantDigits {
Maximal,
UserDefined
};
const char * inputText() const { return m_inputText; }
const char * exactOutputText() const { return m_inputText + strlen(m_inputText) + 1; }
const char * approximateOutputText() const;
// See comment in approximateOutput implementation explaining the need of two approximateOutputTexts
const char * approximateOutputText(NumberOfSignificantDigits numberOfSignificantDigits) const;
// Expressions
Poincare::Expression input();
Poincare::Expression exactOutput();
Poincare::Expression approximateOutput(Poincare::Context * context);
Poincare::Expression approximateOutput(Poincare::Context * context, NumberOfSignificantDigits numberOfSignificantDigits);
// Layouts
Poincare::Layout createInputLayout();
Poincare::Layout createExactOutputLayout();
Poincare::Layout createApproximateOutputLayout(Poincare::Context * context);
Poincare::Layout createExactOutputLayout(bool * couldNotCreateExactLayout);
Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout);
KDCoordinate height(Poincare::Context * context, bool expanded = false);
// Memoization of height
KDCoordinate height(Poincare::Context * context, bool expanded = false, bool allExpressionsInline = false);
// Displayed output
DisplayOutput displayOutput(Poincare::Context * context);
void forceDisplayOutput(DisplayOutput d) { m_displayOutput = d; }
bool shouldOnlyDisplayExactOutput();
EqualSign exactAndApproximateDisplayedOutputsAreEqual(Poincare::Context * context);
// Additional Information
AdditionalInformationType additionalInformationType(Poincare::Context * context);
private:
static constexpr int k_numberOfExpressions = 4;
static constexpr KDCoordinate k_heightComputationFailureHeight = 50;
static constexpr const char * k_maximalIntegerWithAdditionalInformation = "10000000000000000";
/* Buffers holding text expressions have to be longer than the text written
* by user (of maximum length TextField::maxBufferSize()) because when we
* print an expression we add omitted signs (multiplications, parenthesis...) */

View File

@@ -79,8 +79,8 @@ ExpiringPointer<Calculation> CalculationStore::push(const char * text, Context *
* want to keep Ans symbol in the calculation store. */
const char * inputSerialization = nextSerializationLocation;
{
Expression input = Expression::Parse(text).replaceSymbolWithExpression(Symbol::Ans(), ans);
if (!serializeExpression(input, nextSerializationLocation, &newCalculationsLocation)) {
Expression input = Expression::Parse(text, context).replaceSymbolWithExpression(Symbol::Ans(), ans);
if (!pushSerializeExpression(input, nextSerializationLocation, &newCalculationsLocation)) {
/* If the input does not fit in the store (event if the current
* calculation is the only calculation), just replace the calculation with
* undef. */
@@ -90,16 +90,34 @@ ExpiringPointer<Calculation> CalculationStore::push(const char * text, Context *
}
// Compute and serialize the outputs
/* The serialized outputs are:
* - the exact ouput
* - the approximate output with the maximal number of significant digits
* - the approximate output with the displayed number of significant digits */
{
<<<<<<< HEAD
Expression outputs[] = {Expression(), Expression()};
PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, GlobalPreferences::sharedGlobalPreferences()->isInExamModeSymbolic()); // Symbolic computation
for (int i = 0; i < 2; i++) {
if (!serializeExpression(outputs[i], nextSerializationLocation, &newCalculationsLocation)) {
=======
// Outputs hold exact output, approximate output and its duplicate
constexpr static int numberOfOutputs = Calculation::k_numberOfExpressions - 1;
Expression outputs[numberOfOutputs] = {Expression(), Expression(), Expression()};
PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
outputs[2] = outputs[1];
int numberOfSignificantDigits = Poincare::PrintFloat::k_numberOfStoredSignificantDigits;
for (int i = 0; i < numberOfOutputs; i++) {
if (i == numberOfOutputs - 1) {
numberOfSignificantDigits = Poincare::Preferences::sharedPreferences()->numberOfSignificantDigits();
}
if (!pushSerializeExpression(outputs[i], nextSerializationLocation, &newCalculationsLocation, numberOfSignificantDigits)) {
>>>>>>> upstream/master
/* If the exat/approximate output does not fit in the store (event if the
* current calculation is the only calculation), replace the output with
* undef if it fits, else replace the whole calcualtion with undef. */
Expression undef = Undefined::Builder();
if (!serializeExpression(undef, nextSerializationLocation, &newCalculationsLocation)) {
if (!pushSerializeExpression(undef, nextSerializationLocation, &newCalculationsLocation)) {
return emptyStoreAndPushUndef(context);
}
}
@@ -163,11 +181,10 @@ Expression CalculationStore::ansExpression(Context * context) {
* To avoid turning 'ans->A' in '2->A->A' or '2=A->A' (which cannot be
* parsed), ans is replaced by the approximation output when any Store or
* Equal expression appears. */
bool exactOuptutInvolvesStoreEqual = mostRecentCalculation->exactOutput().recursivelyMatches([](const Expression e, Context * context) {
return e.type() == ExpressionNode::Type::Store || e.type() == ExpressionNode::Type::Equal;
}, context, false);
Expression e = mostRecentCalculation->exactOutput();
bool exactOuptutInvolvesStoreEqual = e.type() == ExpressionNode::Type::Store || e.type() == ExpressionNode::Type::Equal;
if (mostRecentCalculation->input().recursivelyMatches(Expression::IsApproximate, context) || exactOuptutInvolvesStoreEqual) {
return mostRecentCalculation->approximateOutput(context);
return mostRecentCalculation->approximateOutput(context, Calculation::NumberOfSignificantDigits::Maximal);
}
return mostRecentCalculation->exactOutput();
}
@@ -184,12 +201,20 @@ Calculation * CalculationStore::bufferCalculationAtIndex(int i) {
return nullptr;
}
bool CalculationStore::serializeExpression(Expression e, char * location, char * * newCalculationsLocation) {
bool CalculationStore::pushSerializeExpression(Expression e, char * location, char * * newCalculationsLocation, int numberOfSignificantDigits) {
assert(m_slidedBuffer);
return pushExpression(
[](char * location, size_t locationSize, void * e) {
return PoincareHelpers::Serialize(*(Expression *)e, location, locationSize) < (int)locationSize-1;
}, &e, location, newCalculationsLocation);
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
bool expressionIsPushed = false;
while (true) {
size_t locationSize = *newCalculationsLocation - location;
expressionIsPushed = (PoincareHelpers::Serialize(e, location, locationSize, numberOfSignificantDigits) < (int)locationSize-1);
if (expressionIsPushed || *newCalculationsLocation >= m_buffer + k_bufferSize) {
break;
}
*newCalculationsLocation = *newCalculationsLocation + deleteLastCalculation();
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
}
return expressionIsPushed;
}
char * CalculationStore::slideCalculationsToEndOfBuffer() {
@@ -232,20 +257,6 @@ const char * CalculationStore::lastCalculationPosition(const char * calculations
return reinterpret_cast<const char *>(c);
}
bool CalculationStore::pushExpression(ValueCreator valueCreator, Expression * expression, char * location, char * * newCalculationsLocation) {
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
bool expressionIsPushed = false;
while (true) {
expressionIsPushed = valueCreator(location, *newCalculationsLocation - location, expression);
if (expressionIsPushed || *newCalculationsLocation >= m_buffer + k_bufferSize) {
break;
}
*newCalculationsLocation = *newCalculationsLocation + deleteLastCalculation();
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
}
return expressionIsPushed;
}
Shared::ExpiringPointer<Calculation> CalculationStore::emptyStoreAndPushUndef(Context * context) {
/* We end up here as a result of a failed calculation push. The store
* attributes are not necessarily clean, so we need to reset them. */

View File

@@ -3,6 +3,7 @@
#include "calculation.h"
#include <apps/shared/expiring_pointer.h>
#include <poincare/print_float.h>
namespace Calculation {
@@ -35,7 +36,7 @@ public:
void tidy();
private:
static constexpr int k_maxNumberOfCalculations = 25;
static constexpr int k_bufferSize = 10 * 3 * Constant::MaxSerializedExpressionSize;
static constexpr int k_bufferSize = 10 * Calculation::k_numberOfExpressions * Constant::MaxSerializedExpressionSize;
class CalculationIterator {
public:
@@ -55,12 +56,10 @@ private:
Calculation * bufferCalculationAtIndex(int i);
int remainingBufferSize() const { assert(m_bufferEnd >= m_buffer); return k_bufferSize - (m_bufferEnd - m_buffer); }
bool serializeExpression(Poincare::Expression e, char * location, char * * newCalculationsLocation);
bool pushSerializeExpression(Poincare::Expression e, char * location, char * * newCalculationsLocation, int numberOfSignificantDigits = Poincare::PrintFloat::k_numberOfStoredSignificantDigits);
char * slideCalculationsToEndOfBuffer(); // returns the new position of the calculations
size_t deleteLastCalculation(const char * calculationsStart = nullptr);
const char * lastCalculationPosition(const char * calculationsStart) const;
typedef bool (*ValueCreator)(char * location, size_t locationSize, void * e);
bool pushExpression(ValueCreator valueCrator, Poincare::Expression * expression, char * location, char * * newCalculationsLocation);
Shared::ExpiringPointer<Calculation> emptyStoreAndPushUndef(Poincare::Context * context);
char m_buffer[k_bufferSize];

View File

@@ -25,12 +25,12 @@ View * EditExpressionController::ContentView::subviewAtIndex(int index) {
return &m_expressionField;
}
void EditExpressionController::ContentView::layoutSubviews() {
void EditExpressionController::ContentView::layoutSubviews(bool force) {
KDCoordinate inputViewFrameHeight = m_expressionField.minimalSizeForOptimalDisplay().height();
KDRect mainViewFrame(0, 0, bounds().width(), bounds().height() - inputViewFrameHeight);
m_mainView->setFrame(mainViewFrame);
m_mainView->setFrame(mainViewFrame, force);
KDRect inputViewFrame(0, bounds().height() - inputViewFrameHeight, bounds().width(), inputViewFrameHeight);
m_expressionField.setFrame(inputViewFrame);
m_expressionField.setFrame(inputViewFrame, force);
}
void EditExpressionController::ContentView::reload() {
@@ -42,25 +42,24 @@ EditExpressionController::EditExpressionController(Responder * parentResponder,
ViewController(parentResponder),
m_historyController(historyController),
m_calculationStore(calculationStore),
m_contentView(this, (TableView *)m_historyController->view(), inputEventHandlerDelegate, this, this),
m_inputViewHeightIsMaximal(false)
m_contentView(this, (TableView *)m_historyController->view(), inputEventHandlerDelegate, this, this)
{
m_cacheBuffer[0] = 0;
}
View * EditExpressionController::view() {
return &m_contentView;
}
void EditExpressionController::insertTextBody(const char * text) {
((ContentView *)view())->expressionField()->handleEventWithText(text, false, true);
m_contentView.expressionField()->handleEventWithText(text, false, true);
}
void EditExpressionController::didBecomeFirstResponder() {
int lastRow = m_calculationStore->numberOfCalculations() > 0 ? m_calculationStore->numberOfCalculations()-1 : 0;
m_historyController->scrollToCell(0, lastRow);
((ContentView *)view())->expressionField()->setEditing(true, false);
Container::activeApp()->setFirstResponder(((ContentView *)view())->expressionField());
m_contentView.expressionField()->setEditing(true, false);
Container::activeApp()->setFirstResponder(m_contentView.expressionField());
}
void EditExpressionController::viewWillAppear() {
m_historyController->viewWillAppear();
}
bool EditExpressionController::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) {
@@ -96,23 +95,23 @@ bool EditExpressionController::layoutFieldDidAbortEditing(::LayoutField * layout
}
void EditExpressionController::layoutFieldDidChangeSize(::LayoutField * layoutField) {
/* Reload the view only if the ExpressionField height actually changes, i.e.
* not if the height is already maximal and stays maximal. */
if (view()) {
bool newInputViewHeightIsMaximal = static_cast<ContentView *>(view())->expressionField()->heightIsMaximal();
if (!m_inputViewHeightIsMaximal || !newInputViewHeightIsMaximal) {
m_inputViewHeightIsMaximal = newInputViewHeightIsMaximal;
reloadView();
}
if (m_contentView.expressionField()->inputViewHeightDidChange()) {
/* Reload the whole view only if the ExpressionField's height did actually
* change. */
reloadView();
} else {
/* The input view is already at maximal size so we do not need to relayout
* the view underneath, but the view inside the input view might still need
* to be relayouted.
* We force the relayout because the frame stays the same but we need to
* propagate a relayout to the content of the field scroll view. */
m_contentView.expressionField()->layoutSubviews(true);
}
}
void EditExpressionController::reloadView() {
((ContentView *)view())->reload();
m_contentView.reload();
m_historyController->reload();
if (m_historyController->numberOfRows() > 0) {
((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1);
}
}
bool EditExpressionController::inputViewDidReceiveEvent(Ion::Events::Event event, bool shouldDuplicateLastCalculation) {
@@ -125,13 +124,12 @@ bool EditExpressionController::inputViewDidReceiveEvent(Ion::Events::Event event
}
m_calculationStore->push(m_cacheBuffer, myApp->localContext());
m_historyController->reload();
((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1);
return true;
}
if (event == Ion::Events::Up) {
if (m_calculationStore->numberOfCalculations() > 0) {
m_cacheBuffer[0] = 0;
((ContentView *)view())->expressionField()->setEditing(false, false);
m_contentView.expressionField()->setEditing(false, false);
Container::activeApp()->setFirstResponder(m_historyController);
}
return true;
@@ -141,29 +139,25 @@ bool EditExpressionController::inputViewDidReceiveEvent(Ion::Events::Event event
bool EditExpressionController::inputViewDidFinishEditing(const char * text, Layout layoutR) {
Context * context = textFieldDelegateApp()->localContext();
if (layoutR.isUninitialized()) {
assert(text);
strlcpy(m_cacheBuffer, text, k_cacheBufferSize);
} else {
layoutR.serializeParsedExpression(m_cacheBuffer, k_cacheBufferSize);
layoutR.serializeParsedExpression(m_cacheBuffer, k_cacheBufferSize, context);
}
m_calculationStore->push(m_cacheBuffer, textFieldDelegateApp()->localContext());
m_calculationStore->push(m_cacheBuffer, context);
m_historyController->reload();
((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1);
((ContentView *)view())->expressionField()->setEditing(true, true);
m_contentView.expressionField()->setEditing(true, true);
return true;
}
bool EditExpressionController::inputViewDidAbortEditing(const char * text) {
if (text != nullptr) {
((ContentView *)view())->expressionField()->setEditing(true, true);
((ContentView *)view())->expressionField()->setText(text);
m_contentView.expressionField()->setEditing(true, true);
m_contentView.expressionField()->setText(text);
}
return false;
}
void EditExpressionController::viewDidDisappear() {
m_historyController->viewDidDisappear();
}
}

View File

@@ -10,15 +10,14 @@
#include "calculation_store.h"
namespace Calculation {
class HistoryController;
/* TODO: implement a split view */
class EditExpressionController : public ViewController, public Shared::TextFieldDelegate, public Shared::LayoutFieldDelegate {
public:
EditExpressionController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, HistoryController * historyController, CalculationStore * calculationStore);
View * view() override;
View * view() override { return &m_contentView; }
void didBecomeFirstResponder() override;
void viewDidDisappear() override;
void viewWillAppear() override;
void insertTextBody(const char * text);
/* TextFieldDelegate */
@@ -39,11 +38,10 @@ private:
void reload();
TableView * mainView() { return m_mainView; }
ExpressionField * expressionField() { return &m_expressionField; }
/* View */
private:
int numberOfSubviews() const override { return 2; }
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
private:
void layoutSubviews(bool force = false) override;
TableView * m_mainView;
ExpressionField m_expressionField;
};
@@ -56,7 +54,6 @@ private:
HistoryController * m_historyController;
CalculationStore * m_calculationStore;
ContentView m_contentView;
bool m_inputViewHeightIsMaximal;
};
}

View File

@@ -3,14 +3,19 @@
#include <assert.h>
using namespace Shared;
using namespace Poincare;
namespace Calculation {
HistoryController::HistoryController(Responder * parentResponder, CalculationStore * calculationStore) :
ViewController(parentResponder),
HistoryController::HistoryController(EditExpressionController * editExpressionController, CalculationStore * calculationStore) :
ViewController(editExpressionController),
m_selectableTableView(this, this, this, this),
m_calculationHistory{},
m_calculationStore(calculationStore)
m_calculationStore(calculationStore),
m_complexController(editExpressionController),
m_integerController(editExpressionController),
m_rationalController(editExpressionController),
m_trigonometryController(editExpressionController)
{
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_calculationHistory[i].setParentResponder(&m_selectableTableView);
@@ -20,6 +25,19 @@ HistoryController::HistoryController(Responder * parentResponder, CalculationSto
void HistoryController::reload() {
m_selectableTableView.reloadData();
/* TODO
* Replace the following by selectCellAtLocation in order to avoid laying out
* the table view twice.
*/
if (numberOfRows() > 0) {
m_selectableTableView.scrollToCell(0, numberOfRows()-1);
// Force to reload last added cell (hide the burger and exact output if necessary)
tableViewDidChangeSelection(&m_selectableTableView, 0, numberOfRows()-1);
}
}
void HistoryController::viewWillAppear() {
reload();
}
void HistoryController::didBecomeFirstResponder() {
@@ -28,6 +46,9 @@ void HistoryController::didBecomeFirstResponder() {
}
void HistoryController::willExitResponderChain(Responder * nextFirstResponder) {
if (nextFirstResponder == nullptr) {
return;
}
if (nextFirstResponder == parentResponder()) {
m_selectableTableView.deselectTable();
}
@@ -47,20 +68,43 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
HistoryViewCell * selectedCell = (HistoryViewCell *)m_selectableTableView.selectedCell();
SubviewType subviewType = selectedSubviewType();
EditExpressionController * editController = (EditExpressionController *)parentResponder();
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(editController);
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(focusRow);
if (subviewType == SubviewType::Input) {
editController->insertTextBody(calculation->inputText());
} else {
ScrollableExactApproximateExpressionsView::SubviewPosition outputSubviewPosition = selectedCell->outputView()->selectedSubviewPosition();
if (outputSubviewPosition == ScrollableExactApproximateExpressionsView::SubviewPosition::Right
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(editController);
editController->insertTextBody(calculationAtIndex(focusRow)->inputText());
} else if (subviewType == SubviewType::Output) {
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(editController);
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(focusRow);
ScrollableTwoExpressionsView::SubviewPosition outputSubviewPosition = selectedCell->outputView()->selectedSubviewPosition();
if (outputSubviewPosition == ScrollableTwoExpressionsView::SubviewPosition::Right
&& !calculation->shouldOnlyDisplayExactOutput())
{
editController->insertTextBody(calculation->approximateOutputText());
editController->insertTextBody(calculation->approximateOutputText(Calculation::NumberOfSignificantDigits::Maximal));
} else {
editController->insertTextBody(calculation->exactOutputText());
}
} else {
assert(subviewType == SubviewType::Ellipsis);
Calculation::AdditionalInformationType additionalInfoType = selectedCell->additionalInformationType();
ListController * vc = nullptr;
Expression e = calculationAtIndex(focusRow)->exactOutput();
if (additionalInfoType == Calculation::AdditionalInformationType::Complex) {
vc = &m_complexController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Trigonometry) {
vc = &m_trigonometryController;
// Find which of the input or output is the cosine/sine
ExpressionNode::Type t = e.type();
e = t == ExpressionNode::Type::Cosine || t == ExpressionNode::Type::Sine ? e : calculationAtIndex(focusRow)->input();
} else if (additionalInfoType == Calculation::AdditionalInformationType::Integer) {
vc = &m_integerController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Rational) {
vc = &m_rationalController;
}
if (vc) {
vc->setExpression(e);
Container::activeApp()->displayModalViewController(vc, 0.f, 0.f, Metric::CommonTopMargin, Metric::PopUpLeftMargin, 0, Metric::PopUpRightMargin);
}
}
return true;
}
@@ -113,11 +157,13 @@ void HistoryController::tableViewDidChangeSelection(SelectableTableView * t, int
return;
}
if (previousSelectedCellY == -1) {
setSelectedSubviewType(SubviewType::Output);
setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
} else if (selectedRow() < previousSelectedCellY) {
setSelectedSubviewType(SubviewType::Output);
setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
} else if (selectedRow() > previousSelectedCellY) {
setSelectedSubviewType(SubviewType::Input);
setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
} else if (selectedRow() == -1) {
setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
}
HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell());
if (selectedCell == nullptr) {
@@ -146,7 +192,7 @@ void HistoryController::willDisplayCellForIndex(HighlightCell * cell, int index)
HistoryViewCell * myCell = (HistoryViewCell *)cell;
myCell->setCalculation(calculationAtIndex(index).pointer(), index == selectedRow() && selectedSubviewType() == SubviewType::Output);
myCell->setEven(index%2 == 0);
myCell->setHighlighted(myCell->isHighlighted());
myCell->reloadSubviewHighlight();
}
KDCoordinate HistoryController::rowHeight(int j) {
@@ -165,12 +211,27 @@ void HistoryController::scrollToCell(int i, int j) {
m_selectableTableView.scrollToCell(i, j);
}
HistoryViewCell * HistoryController::historyViewCellDidChangeSelection() {
/* Update the whole table as the height of the selected cell row might have
* changed. */
m_selectableTableView.reloadData();
// Return the selected cell if one
return static_cast<HistoryViewCell *>(m_selectableTableView.selectedCell());
bool HistoryController::calculationAtIndexToggles(int index) {
Context * context = App::app()->localContext();
return index >= 0 && index < m_calculationStore->numberOfCalculations() && calculationAtIndex(index)->displayOutput(context) == Calculation::DisplayOutput::ExactAndApproximateToggle;
}
void HistoryController::historyViewCellDidChangeSelection(HistoryViewCell ** cell, HistoryViewCell ** previousCell, int previousSelectedCellX, int previousSelectedCellY, SubviewType type, SubviewType previousType) {
/* If the selection change triggers the toggling of the outputs, we update
* the whole table as the height of the selected cell row might have changed. */
if ((type == SubviewType::Output || previousType == SubviewType::Output) && (calculationAtIndexToggles(selectedRow()) || calculationAtIndexToggles(previousSelectedCellY))) {
m_selectableTableView.reloadData();
}
// 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));
/* 'reloadData' calls 'willDisplayCellForIndex' for each cell while the table
* has been deselected. To reload the expanded cell, we call one more time
* 'willDisplayCellForIndex' but once the right cell has been selected. */
if (*cell) {
willDisplayCellForIndex(*cell, selectedRow());
}
}
}

View File

@@ -5,6 +5,10 @@
#include "history_view_cell.h"
#include "calculation_store.h"
#include "selectable_table_view.h"
#include "additional_outputs/complex_list_controller.h"
#include "additional_outputs/integer_list_controller.h"
#include "additional_outputs/rational_list_controller.h"
#include "additional_outputs/trigonometry_list_controller.h"
namespace Calculation {
@@ -12,9 +16,10 @@ class App;
class HistoryController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public HistoryViewCellDataSource {
public:
HistoryController(Responder * parentResponder, CalculationStore * calculationStore);
HistoryController(EditExpressionController * editExpressionController, CalculationStore * calculationStore);
View * view() override { return &m_selectableTableView; }
bool handleEvent(Ion::Events::Event event) override;
void viewWillAppear() override;
void didBecomeFirstResponder() override;
void willExitResponderChain(Responder * nextFirstResponder) override;
void reload();
@@ -30,11 +35,16 @@ private:
int storeIndex(int i) { return numberOfRows() - i - 1; }
Shared::ExpiringPointer<Calculation> calculationAtIndex(int i);
CalculationSelectableTableView * selectableTableView();
HistoryViewCell * historyViewCellDidChangeSelection() override;
bool calculationAtIndexToggles(int index);
void historyViewCellDidChangeSelection(HistoryViewCell ** cell, HistoryViewCell ** previousCell, int previousSelectedCellX, int previousSelectedCellY, SubviewType type, SubviewType previousType) override;
constexpr static int k_maxNumberOfDisplayedRows = 5;
CalculationSelectableTableView m_selectableTableView;
HistoryViewCell m_calculationHistory[k_maxNumberOfDisplayedRows];
CalculationStore * m_calculationStore;
ComplexListController m_complexController;
IntegerListController m_integerController;
RationalListController m_rationalController;
TrigonometryListController m_trigonometryController;
};
}

View File

@@ -2,6 +2,7 @@
#include "app.h"
#include "../constant.h"
#include "selectable_table_view.h"
#include <poincare/exception_checkpoint.h>
#include <assert.h>
#include <string.h>
@@ -15,12 +16,23 @@ static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { retur
HistoryViewCellDataSource::HistoryViewCellDataSource() :
m_selectedSubviewType(SubviewType::Output) {}
void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType) {
void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedCellX, int previousSelectedCellY) {
HistoryViewCell * selectedCell = nullptr;
HistoryViewCell * previouslySelectedCell = nullptr;
SubviewType previousSubviewType = m_selectedSubviewType;
m_selectedSubviewType = subviewType;
HistoryViewCell * cell = historyViewCellDidChangeSelection();
if (cell) {
cell->setHighlighted(cell->isHighlighted());
cell->cellDidSelectSubview(subviewType);
/* We need to notify the whole table that the selection changed if it
* involves the selection/deselection of an output. Indeed, only them can
* trigger change in the displayed expressions. */
historyViewCellDidChangeSelection(&selectedCell, &previouslySelectedCell, previousSelectedCellX, previousSelectedCellY, subviewType, previousSubviewType);
previousSubviewType = sameCell ? previousSubviewType : SubviewType::None;
if (selectedCell) {
selectedCell->reloadSubviewHighlight();
selectedCell->cellDidSelectSubview(subviewType, previousSubviewType);
}
if (previouslySelectedCell) {
previouslySelectedCell->cellDidSelectSubview(SubviewType::Input);
}
}
@@ -29,14 +41,15 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType)
HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
Responder(parentResponder),
m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
m_calculationExpanded(false),
m_inputView(this),
m_inputView(this, Metric::CommonLargeMargin, Metric::CommonSmallMargin),
m_scrollableOutputView(this)
{
m_calculationCRC32 = 0;
}
Shared::ScrollableExactApproximateExpressionsView * HistoryViewCell::outputView() {
Shared::ScrollableTwoExpressionsView * HistoryViewCell::outputView() {
return &m_scrollableOutputView;
}
@@ -45,18 +58,32 @@ void HistoryViewCell::setEven(bool even) {
m_inputView.setBackgroundColor(backgroundColor());
m_scrollableOutputView.setBackgroundColor(backgroundColor());
m_scrollableOutputView.evenOddCell()->setEven(even);
m_ellipsis.setEven(even);
}
void HistoryViewCell::setHighlighted(bool highlight) {
assert(m_dataSource);
if (m_highlighted == highlight) {
return;
}
m_highlighted = highlight;
reloadSubviewHighlight();
// Re-layout as the ellispsis subview might have appear/disappear
layoutSubviews();
}
void HistoryViewCell::reloadSubviewHighlight() {
assert(m_dataSource);
m_inputView.setExpressionBackgroundColor(backgroundColor());
m_scrollableOutputView.evenOddCell()->setHighlighted(false);
m_ellipsis.setHighlighted(false);
if (isHighlighted()) {
if (m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Input) {
m_inputView.setExpressionBackgroundColor(Palette::ListCellBackgroundSelected);
} else {
} else if (m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Output) {
m_scrollableOutputView.evenOddCell()->setHighlighted(true);
} else {
assert(m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Ellipsis);
m_ellipsis.setHighlighted(true);
}
}
}
@@ -75,30 +102,35 @@ void HistoryViewCell::reloadScroll() {
m_scrollableOutputView.reloadScroll();
}
void HistoryViewCell::reloadOutputSelection() {
void HistoryViewCell::reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType) {
/* Select the right output according to the calculation display output. This
* will reload the scroll to display the selected output. */
if (m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximate) {
m_scrollableOutputView.setSelectedSubviewPosition(Shared::ScrollableExactApproximateExpressionsView::SubviewPosition::Left);
m_scrollableOutputView.setSelectedSubviewPosition(
previousType == HistoryViewCellDataSource::SubviewType::Ellipsis ?
Shared::ScrollableTwoExpressionsView::SubviewPosition::Right :
Shared::ScrollableTwoExpressionsView::SubviewPosition::Center
);
} else {
assert((m_calculationDisplayOutput == Calculation::DisplayOutput::ApproximateOnly)
|| (m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximateToggle)
|| (m_calculationDisplayOutput == Calculation::DisplayOutput::ExactOnly));
m_scrollableOutputView.setSelectedSubviewPosition(Shared::ScrollableExactApproximateExpressionsView::SubviewPosition::Right);
m_scrollableOutputView.setSelectedSubviewPosition(Shared::ScrollableTwoExpressionsView::SubviewPosition::Right);
}
}
void HistoryViewCell::cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type) {
void HistoryViewCell::cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType) {
// Init output selection
if (type == HistoryViewCellDataSource::SubviewType::Output) {
reloadOutputSelection();
reloadOutputSelection(previousType);
}
// Update m_calculationExpanded
m_calculationExpanded = (type == HistoryViewCellDataSource::SubviewType::Output && m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximateToggle);
/* The selected subview has changed. The displayed outputs might have changed.
* For example, for the calculation 1.2+2 --> 3.2, selecting the output would
* display 1.2+2 --> 16/5 = 3.2. */
m_calculationExpanded = (type == HistoryViewCellDataSource::SubviewType::Output);
m_scrollableOutputView.setDisplayLeftLayout(displayLeftLayout());
m_scrollableOutputView.setDisplayCenter(m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximate || m_calculationExpanded);
/* The displayed outputs have changed. We need to re-layout the cell
* and re-initialize the scroll. */
@@ -112,56 +144,121 @@ KDColor HistoryViewCell::backgroundColor() const {
}
int HistoryViewCell::numberOfSubviews() const {
return 2;
return 2 + displayedEllipsis();
}
View * HistoryViewCell::subviewAtIndex(int index) {
View * views[2] = {&m_inputView, &m_scrollableOutputView};
/* The order of the subviews should not matter here as they don't overlap.
* However, the order determines the order of redrawing as well. For several
* reasons listed after, changing subview selection often redraws the entire
* m_scrollableOutputView even if it seems unecessary:
* - Before feeding new Layouts to ExpressionViews, we reset the hold layouts
* in order to empty the Poincare pool and have more space to compute new
* layouts.
* - Even if we did not do that, ExpressionView::setLayout doesn't avoid
* redrawing when the previous expression is identical (for reasons
* explained in expression_view.cpp)
* - Because of the toggling burger view, ExpressionViews often have the same
* absolute frame but a different relative frame which leads to redrawing
* them anyway.
* All these reasons cause a blinking which can be avoided if we redraw the
* output view before the input view (starting with redrawing the more
* complex view enables to redraw it before the vblank thereby preventing
* blinking).
* TODO: this is a dirty hack which should be fixed! */
View * views[3] = {&m_scrollableOutputView, &m_inputView, &m_ellipsis};
return views[index];
}
void HistoryViewCell::layoutSubviews() {
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
}
KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay();
m_inputView.setFrame(KDRect(
0,
0,
0, 0,
minCoordinate(maxFrameWidth, inputSize.width()),
inputSize.height()
));
inputSize.height()),
force);
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
m_scrollableOutputView.setFrame(KDRect(
maxCoordinate(0, maxFrameWidth - outputSize.width()),
inputSize.height(),
minCoordinate(maxFrameWidth, outputSize.width()),
outputSize.height()
));
outputSize.height()),
force);
}
void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded) {
uint32_t newCalculationCRC = Ion::crc32Byte((const uint8_t *)calculation, ((char *)calculation->next()) - ((char *) calculation));
if (m_calculationExpanded == expanded && newCalculationCRC == m_calculationCRC32) {
if (newCalculationCRC == m_calculationCRC32 && m_calculationExpanded == expanded) {
return;
}
Poincare::Context * context = App::app()->localContext();
// Clean the layouts to make room in the pool
// TODO: maybe do this only when the layout won't change to avoid blinking
m_inputView.setLayout(Poincare::Layout());
m_scrollableOutputView.setLayouts(Poincare::Layout(), Poincare::Layout());
m_scrollableOutputView.setLayouts(Poincare::Layout(), Poincare::Layout(), Poincare::Layout());
// Memoization
m_calculationCRC32 = newCalculationCRC;
m_calculationExpanded = expanded;
m_calculationDisplayOutput = calculation->displayOutput(context);
m_calculationExpanded = expanded && calculation->displayOutput(context) == ::Calculation::Calculation::DisplayOutput::ExactAndApproximateToggle;
m_calculationAdditionInformation = calculation->additionalInformationType(context);
m_inputView.setLayout(calculation->createInputLayout());
/* Both output expressions have to be updated at the same time. Otherwise,
/* All expressions have to be updated at the same time. Otherwise,
* when updating one layout, if the second one still points to a deleted
* layout, calling to layoutSubviews() would fail. */
Poincare::Layout leftOutputLayout = calculation->createExactOutputLayout();
Poincare::Layout rightOutputLayout = (m_calculationDisplayOutput == Calculation::DisplayOutput::ExactOnly) ? leftOutputLayout :
calculation->createApproximateOutputLayout(context);
m_scrollableOutputView.setDisplayLeftLayout(displayLeftLayout()); // Must be before the setLayouts fo the reload
m_scrollableOutputView.setLayouts(rightOutputLayout, leftOutputLayout);
// Create the exact output layout
Poincare::Layout exactOutputLayout = Poincare::Layout();
if (Calculation::DisplaysExact(calculation->displayOutput(context))) {
bool couldNotCreateExactLayout = false;
exactOutputLayout = calculation->createExactOutputLayout(&couldNotCreateExactLayout);
if (couldNotCreateExactLayout) {
if (calculation->displayOutput(context) != ::Calculation::Calculation::DisplayOutput::ExactOnly) {
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
} else {
/* We should only display the exact result, but we cannot create it
* -> raise an exception. */
Poincare::ExceptionCheckpoint::Raise();
}
}
}
// Create the approximate output layout
Poincare::Layout approximateOutputLayout;
if (calculation->displayOutput(context) == ::Calculation::Calculation::DisplayOutput::ExactOnly) {
approximateOutputLayout = exactOutputLayout;
} else {
bool couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
if (calculation->displayOutput(context) == ::Calculation::Calculation::DisplayOutput::ApproximateOnly) {
Poincare::ExceptionCheckpoint::Raise();
} else {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
exactOutputLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
Poincare::ExceptionCheckpoint::Raise();
}
}
}
}
m_calculationDisplayOutput = calculation->displayOutput(context);
// We must set which subviews are displayed before setLayouts to mark the right rectangle as dirty
m_scrollableOutputView.setDisplayCenter(m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximate || m_calculationExpanded);
m_scrollableOutputView.setLayouts(Poincare::Layout(), exactOutputLayout, approximateOutputLayout);
I18n::Message equalMessage = calculation->exactAndApproximateDisplayedOutputsAreEqual(context) == Calculation::EqualSign::Equal ? I18n::Message::Equal : I18n::Message::AlmostEqual;
m_scrollableOutputView.setEqualMessage(equalMessage);
@@ -175,17 +272,30 @@ void HistoryViewCell::didBecomeFirstResponder() {
assert(m_dataSource);
if (m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Input) {
Container::activeApp()->setFirstResponder(&m_inputView);
} else {
} else if (m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Output) {
Container::activeApp()->setFirstResponder(&m_scrollableOutputView);
}
}
bool HistoryViewCell::handleEvent(Ion::Events::Event event) {
assert(m_dataSource);
if ((event == Ion::Events::Down && m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Input) ||
(event == Ion::Events::Up && m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Output)) {
HistoryViewCellDataSource::SubviewType otherSubviewType = m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Input ? HistoryViewCellDataSource::SubviewType::Output : HistoryViewCellDataSource::SubviewType::Input;
m_dataSource->setSelectedSubviewType(otherSubviewType);
HistoryViewCellDataSource::SubviewType type = m_dataSource->selectedSubviewType();
if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input) ||
(event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) ||
(event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) ||
(event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis)) {
HistoryViewCellDataSource::SubviewType otherSubviewType;
if (event == Ion::Events::Down) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
} else if (event == Ion::Events::Up) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Input;
} else if (event == Ion::Events::Right) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis;
} else {
assert(event == Ion::Events::Left);
otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
}
m_dataSource->setSelectedSubviewType(otherSubviewType, true);
CalculationSelectableTableView * tableView = (CalculationSelectableTableView *)parentResponder();
tableView->scrollToSubviewOfTypeOfCellAtLocation(otherSubviewType, tableView->selectedColumn(), tableView->selectedRow());
Container::activeApp()->setFirstResponder(this);
@@ -194,9 +304,8 @@ bool HistoryViewCell::handleEvent(Ion::Events::Event event) {
return false;
}
bool HistoryViewCell::displayLeftLayout() const {
return (m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximate)
|| (m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximateToggle && m_calculationExpanded);
bool HistoryViewCell::displayedEllipsis() const {
return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None;
}
}

View File

@@ -3,8 +3,7 @@
#include <escher.h>
#include "calculation.h"
#include "scrollable_expression_view.h"
#include "../shared/scrollable_exact_approximate_expressions_view.h"
#include "../shared/scrollable_multiple_expressions_view.h"
namespace Calculation {
@@ -13,50 +12,56 @@ class HistoryViewCell;
class HistoryViewCellDataSource {
public:
enum class SubviewType {
None,
Input,
Output
Output,
Ellipsis
};
HistoryViewCellDataSource();
void setSelectedSubviewType(SubviewType subviewType);
void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1);
SubviewType selectedSubviewType() { return m_selectedSubviewType; }
private:
/* This method should belong to a delegate instead of a data source but as
* both the data source and the delegate will be the same controller, we
* avoid keeping 2 pointers in HistoryViewCell. */
// It returns the selected cell at the end of the method
virtual HistoryViewCell * historyViewCellDidChangeSelection() = 0;
virtual void historyViewCellDidChangeSelection(HistoryViewCell ** cell, HistoryViewCell ** previousCell, int previousSelectedCellX, int previousSelectedCellY, SubviewType type, SubviewType previousType) = 0;
SubviewType m_selectedSubviewType;
};
class HistoryViewCell : public ::EvenOddCell, public Responder {
public:
HistoryViewCell(Responder * parentResponder = nullptr);
void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type);
void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None);
void setEven(bool even) override;
void setHighlighted(bool highlight) override;
void reloadSubviewHighlight();
void setDataSource(HistoryViewCellDataSource * dataSource) { m_dataSource = dataSource; }
Responder * responder() override {
return this;
}
Poincare::Layout layout() const override;
KDColor backgroundColor() const override;
void setCalculation(Calculation * calculation, bool expanded = false);
void setCalculation(Calculation * calculation, bool expanded);
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
void layoutSubviews(bool force = false) override;
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
Shared::ScrollableExactApproximateExpressionsView * outputView();
Shared::ScrollableTwoExpressionsView * outputView();
Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; }
private:
constexpr static KDCoordinate k_resultWidth = 80;
void reloadScroll();
void reloadOutputSelection();
bool displayLeftLayout() const;
void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType);
bool displayedEllipsis() const;
uint32_t m_calculationCRC32;
Calculation::DisplayOutput m_calculationDisplayOutput;
Calculation::AdditionalInformationType m_calculationAdditionInformation;
bool m_calculationExpanded;
ScrollableExpressionView m_inputView;
Shared::ScrollableExactApproximateExpressionsView m_scrollableOutputView;
Shared::ScrollableTwoExpressionsView m_scrollableOutputView;
EvenOddCellWithEllipsis m_ellipsis;
HistoryViewCellDataSource * m_dataSource;
};

View File

@@ -1,21 +0,0 @@
#ifndef CALCULATION_SCROLLABLE_EXPRESSION_VIEW_H
#define CALCULATION_SCROLLABLE_EXPRESSION_VIEW_H
#include <escher.h>
namespace Calculation {
class ScrollableExpressionView : public ScrollableView, public ScrollViewDataSource {
public:
ScrollableExpressionView(Responder * parentResponder);
Poincare::Layout layout() const;
void setLayout(Poincare::Layout layout);
void setBackgroundColor(KDColor backgroundColor) override;
void setExpressionBackgroundColor(KDColor backgroundColor);
private:
ExpressionView m_expressionView;
};
}
#endif

View File

@@ -50,12 +50,12 @@ QUIZ_CASE(calculation_ans) {
store.push("ans+0.22", &globalContext);
lastCalculation = store.calculationAtIndex(0);
quiz_assert(lastCalculation->displayOutput(&globalContext) == ::Calculation::Calculation::DisplayOutput::ExactAndApproximateToggle);
quiz_assert(strcmp(lastCalculation->approximateOutputText(),"2.6366666666667") == 0);
quiz_assert(strcmp(lastCalculation->approximateOutputText(::Calculation::Calculation::NumberOfSignificantDigits::Maximal),"2.6366666666667") == 0);
store.deleteAll();
}
void assertCalculationDisplay(const char * input, ::Calculation::Calculation::DisplayOutput display, ::Calculation::Calculation::EqualSign sign, const char * exactOutput, const char * approximateOutput, Context * context, CalculationStore * store) {
void assertCalculationIs(const char * input, ::Calculation::Calculation::DisplayOutput display, ::Calculation::Calculation::EqualSign sign, const char * exactOutput, const char * displayedApproximateOutput, const char * storedApproximateOutput, Context * context, CalculationStore * store) {
store->push(input, context);
Shared::ExpiringPointer<::Calculation::Calculation> lastCalculation = store->calculationAtIndex(0);
quiz_assert(lastCalculation->displayOutput(context) == display);
@@ -65,37 +65,49 @@ void assertCalculationDisplay(const char * input, ::Calculation::Calculation::Di
if (exactOutput) {
quiz_assert_print_if_failure(strcmp(lastCalculation->exactOutputText(), exactOutput) == 0, input);
}
if (approximateOutput) {
quiz_assert_print_if_failure(strcmp(lastCalculation->approximateOutputText(), approximateOutput) == 0, input);
if (displayedApproximateOutput) {
quiz_assert_print_if_failure(strcmp(lastCalculation->approximateOutputText(::Calculation::Calculation::NumberOfSignificantDigits::UserDefined), displayedApproximateOutput) == 0, input);
}
if (storedApproximateOutput) {
quiz_assert_print_if_failure(strcmp(lastCalculation->approximateOutputText(::Calculation::Calculation::NumberOfSignificantDigits::Maximal), storedApproximateOutput) == 0, input);
}
store->deleteAll();
}
QUIZ_CASE(calculation_significant_digits) {
Shared::GlobalContext globalContext;
CalculationStore store;
assertCalculationIs("123456789", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "123456789", "1.234568ᴇ8", "123456789", &globalContext, &store);
assertCalculationIs("1234567", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "1234567", "1234567", "1234567", &globalContext, &store);
}
QUIZ_CASE(calculation_display_exact_approximate) {
Shared::GlobalContext globalContext;
CalculationStore store;
assertCalculationDisplay("1/2", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Equal, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("1/3", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("1/0", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", &globalContext, &store);
assertCalculationDisplay("2x-x", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", &globalContext, &store);
assertCalculationDisplay("[[1,2,3]]", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("[[1,x,3]]", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "undef", &globalContext, &store);
assertCalculationDisplay("28^7", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("3+√(2)→a", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "√(2)+3", nullptr, &globalContext, &store);
assertCalculationIs("1/2", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Equal, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("1/3", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("1/0", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", "undef", &globalContext, &store);
assertCalculationIs("2x-x", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", "undef", &globalContext, &store);
assertCalculationIs("[[1,2,3]]", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("[[1,x,3]]", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "undef", "undef", &globalContext, &store);
assertCalculationIs("28^7", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("3+√(2)→a", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "√(2)+3", nullptr, nullptr, &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
assertCalculationDisplay("3+2→a", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "5", "5", &globalContext, &store);
assertCalculationIs("3+2→a", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "5", "5", "5", &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
assertCalculationDisplay("3→a", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "3", "3", &globalContext, &store);
assertCalculationIs("3→a", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "3", "3", "3", &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
assertCalculationDisplay("3+x→f(x)", ::Calculation::Calculation::DisplayOutput::ExactOnly, ::Calculation::Calculation::EqualSign::Unknown, "x+3", nullptr, &globalContext, &store);
assertCalculationIs("3+x→f(x)", ::Calculation::Calculation::DisplayOutput::ExactOnly, ::Calculation::Calculation::EqualSign::Unknown, "x+3", nullptr, nullptr, &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
assertCalculationDisplay("1+1+random()", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("1+1+round(1.343,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "3.34", &globalContext, &store);
assertCalculationDisplay("randint(2,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "5", "5", &globalContext, &store);
assertCalculationDisplay("confidence(0.5,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("prediction(0.5,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("prediction95(0.5,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("1+1+random()", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("1+1+round(1.343,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "3.34", "3.34", &globalContext, &store);
assertCalculationIs("randint(2,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "5", "5", "5", &globalContext, &store);
assertCalculationIs("confidence(0.5,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("prediction(0.5,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("prediction95(0.5,2)+3", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store);
}
@@ -103,14 +115,14 @@ QUIZ_CASE(calculation_symbolic_computation) {
Shared::GlobalContext globalContext;
CalculationStore store;
assertCalculationDisplay("x+x+1+3+√(π)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", &globalContext, &store);
assertCalculationDisplay("f(x)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", &globalContext, &store);
assertCalculationDisplay("1+x→f(x)", ::Calculation::Calculation::DisplayOutput::ExactOnly, ::Calculation::Calculation::EqualSign::Unknown, "x+1", nullptr, &globalContext, &store);
assertCalculationDisplay("f(x)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", &globalContext, &store);
assertCalculationDisplay("f(2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "3", "3", &globalContext, &store);
assertCalculationDisplay("2→x", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "2", nullptr, &globalContext, &store);
assertCalculationDisplay("f(x)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "3", nullptr, &globalContext, &store);
assertCalculationDisplay("x+x+1+3+√(π)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "√(π)+8", nullptr, &globalContext, &store);
assertCalculationIs("x+x+1+3+√(π)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", "undef", &globalContext, &store);
assertCalculationIs("f(x)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", "undef", &globalContext, &store);
assertCalculationIs("1+x→f(x)", ::Calculation::Calculation::DisplayOutput::ExactOnly, ::Calculation::Calculation::EqualSign::Unknown, "x+1", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("f(x)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", "undef", &globalContext, &store);
assertCalculationIs("f(2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "3", "3", "3", &globalContext, &store);
assertCalculationIs("2→x", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "2", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("f(x)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Equal, "3", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("x+x+1+3+√(π)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "√(π)+8", nullptr, nullptr, &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
Ion::Storage::sharedStorage()->recordNamed("x.exp").destroy();
@@ -120,16 +132,16 @@ QUIZ_CASE(calculation_symbolic_computation_and_parametered_expressions) {
Shared::GlobalContext globalContext;
CalculationStore store;
assertCalculationDisplay("int((^(-x))-x^(0.5), x, 0, 3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store); // Tests a bug with symbolic computation
assertCalculationDisplay("int(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", &globalContext, &store);
assertCalculationDisplay("sum(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "3", &globalContext, &store);
assertCalculationDisplay("product(x,x,1,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", &globalContext, &store);
assertCalculationDisplay("diff(x^2,x,3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "6", &globalContext, &store);
assertCalculationDisplay("2→x", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("int(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", &globalContext, &store);
assertCalculationDisplay("sum(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "3", &globalContext, &store);
assertCalculationDisplay("product(x,x,1,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", &globalContext, &store);
assertCalculationDisplay("diff(x^2,x,3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "6", &globalContext, &store);
assertCalculationIs("int((^(-x))-x^(0.5), x, 0, 3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store); // Tests a bug with symbolic computation
assertCalculationIs("int(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", "2", &globalContext, &store);
assertCalculationIs("sum(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "3", "3", &globalContext, &store);
assertCalculationIs("product(x,x,1,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", "2", &globalContext, &store);
assertCalculationIs("diff(x^2,x,3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "6", "6", &globalContext, &store);
assertCalculationIs("2→x", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, nullptr, &globalContext, &store);
assertCalculationIs("int(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", "2", &globalContext, &store);
assertCalculationIs("sum(x,x,0,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "3", "3", &globalContext, &store);
assertCalculationIs("product(x,x,1,2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "2", "2", &globalContext, &store);
assertCalculationIs("diff(x^2,x,3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "6", "6", &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("x.exp").destroy();
}
@@ -140,31 +152,31 @@ QUIZ_CASE(calculation_complex_format) {
CalculationStore store;
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Real);
assertCalculationDisplay("1+𝐢", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+𝐢", &globalContext, &store);
assertCalculationDisplay("√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "unreal", nullptr, &globalContext, &store);
assertCalculationDisplay("ln(-2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store);
assertCalculationDisplay("√(-1)×√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store);
assertCalculationDisplay("(-8)^(1/3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-2", &globalContext, &store);
assertCalculationDisplay("(-8)^(2/3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "4", &globalContext, &store);
assertCalculationDisplay("(-2)^(1/4)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store);
assertCalculationIs("1+𝐢", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+𝐢", "1+𝐢", &globalContext, &store);
assertCalculationIs("√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, "unreal", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("ln(-2)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", "unreal", &globalContext, &store);
assertCalculationIs("√(-1)×√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", "unreal", &globalContext, &store);
assertCalculationIs("(-8)^(1/3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-2", "-2", &globalContext, &store);
assertCalculationIs("(-8)^(2/3)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "4", "4", &globalContext, &store);
assertCalculationIs("(-2)^(1/4)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", "unreal", &globalContext, &store);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
assertCalculationDisplay("1+𝐢", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+𝐢", &globalContext, &store);
assertCalculationDisplay("√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "𝐢", &globalContext, &store);
assertCalculationDisplay("ln(-2)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, &globalContext, &store);
assertCalculationDisplay("√(-1)×√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-1", &globalContext, &store);
assertCalculationDisplay("(-8)^(1/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "1+√(3)×𝐢", nullptr, &globalContext, &store);
assertCalculationDisplay("(-8)^(2/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "-2+2×√(3)×𝐢", nullptr, &globalContext, &store);
assertCalculationDisplay("(-2)^(1/4)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "root(8,4)/2+root(8,4)/2×𝐢", nullptr, &globalContext, &store);
assertCalculationIs("1+𝐢", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+𝐢", "1+𝐢", &globalContext, &store);
assertCalculationIs("√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "𝐢", "𝐢", &globalContext, &store);
assertCalculationIs("ln(-2)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("√(-1)×√(-1)", ::Calculation::Calculation::DisplayOutput::ApproximateOnly, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-1", "-1", &globalContext, &store);
assertCalculationIs("(-8)^(1/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "1+√(3)×𝐢", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("(-8)^(2/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "-2+2×√(3)×𝐢", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("(-2)^(1/4)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "root(8,4)/2+root(8,4)/2×𝐢", nullptr, nullptr, &globalContext, &store);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar);
assertCalculationDisplay("1+𝐢", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "√(2)×^\u0012π/4×𝐢\u0013", nullptr, &globalContext, &store);
assertCalculationDisplay("√(-1)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "^\u0012π/2×𝐢\u0013", nullptr, &globalContext, &store);
assertCalculationDisplay("ln(-2)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, &globalContext, &store);
assertCalculationDisplay("√(-1)×√(-1)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "^\u00123.1415926535898×𝐢\u0013", &globalContext, &store);
assertCalculationDisplay("(-8)^(1/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "2×^\u0012π/3×𝐢\u0013", nullptr, &globalContext, &store);
assertCalculationDisplay("(-8)^(2/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "4×^\u0012\u00122×π\u0013/3×𝐢\u0013", nullptr, &globalContext, &store);
assertCalculationDisplay("(-2)^(1/4)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "root(2,4)×^\u0012π/4×𝐢\u0013", nullptr, &globalContext, &store);
assertCalculationIs("1+𝐢", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "√(2)×^\u0012π/4×𝐢\u0013", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("√(-1)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "^\u0012π/2×𝐢\u0013", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("ln(-2)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("√(-1)×√(-1)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "^\u00123.141593×𝐢\u0013", "^\u00123.1415926535898×𝐢\u0013", &globalContext, &store);
assertCalculationIs("(-8)^(1/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "2×^\u0012π/3×𝐢\u0013", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("(-8)^(2/3)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "4×^\u0012\u00122×π\u0013/3×𝐢\u0013", nullptr, nullptr, &globalContext, &store);
assertCalculationIs("(-2)^(1/4)", ::Calculation::Calculation::DisplayOutput::ExactAndApproximate, ::Calculation::Calculation::EqualSign::Approximation, "root(2,4)×^\u0012π/4×𝐢\u0013", nullptr, nullptr, &globalContext, &store);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
}

View File

@@ -109,6 +109,10 @@ bool App::handleEvent(Ion::Events::Event event) {
return false;
}
void App::willExitResponderChain(Responder * nextFirstResponder) {
m_menuController.willExitApp();
}
Toolbox * App::toolboxForInputEventHandler(InputEventHandler * textInput) {
return &m_toolbox;
}

View File

@@ -53,6 +53,7 @@ public:
/* Responder */
bool handleEvent(Ion::Events::Event event) override;
void willExitResponderChain(Responder * nextFirstResponder) override;
/* InputEventHandlerDelegate */
Toolbox * toolboxForInputEventHandler(InputEventHandler * textInput) override;
@@ -69,13 +70,15 @@ public:
void deinitPython();
VariableBoxController * variableBoxController() { return &m_variableBoxController; }
static constexpr int k_pythonHeapSize = 16384;
private:
/* Python delegate:
* MicroPython requires a heap. To avoid dynamic allocation, we keep a working
* buffer here and we give to controllers that load Python environment. We
* also memoize the last Python user to avoid re-initiating MicroPython when
* unneeded. */
static constexpr int k_pythonHeapSize = 32768; // Default value: 16384
char m_pythonHeap[k_pythonHeapSize];
const void * m_pythonUser;

View File

@@ -12,6 +12,7 @@ PythonSingleQuote = "Einfaches Anführungszeichen"
PythonAbs = "Absolute/r Wert/Größe"
PythonAcos = "Arkuskosinus"
PythonAcosh = "Hyperbelkosinus"
PythonAppend = "Add x to the end of the list"
PythonAsin = "Arkussinus"
PythonAsinh = "Hyperbelsinus"
PythonAtan = "Arkustangens"
@@ -20,19 +21,21 @@ PythonAtanh = "Hyperbeltangens"
PythonBin = "Ganzzahl nach binär konvertieren"
PythonCeil = "Aufrundung"
PythonChoice = "Zufallszahl aus der Liste"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath-Modul-Funktionspräfix"
PythonColor = "Definiere eine RGB-Farbe"
PythonComplex = "a+ib zurückgeben"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Kosinus"
PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawString = "Display a text from pixel (x,y)"
PythonConstantE = "2.718281828459046"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
PythonEval = "Returns the evaluated expression"
PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
@@ -46,21 +49,68 @@ PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
PythonHex = "Convert integer to hexadecimal"
PythonImportCmath = "Import cmath module"
PythonImportIon = "Import ion module"
PythonImportKandinsky = "Import kandinsky module"
PythonImportRandom = "Import random module"
PythonImportMath = "Import math module"
PythonImportTime = "Import time module"
PythonImportTurtle = "Import turtle module"
PythonImportFromCmath = "Import cmath module"
PythonImportFromKandinsky = "Import kandinsky module"
PythonImportFromRandom = "Import random module"
PythonImportFromMath = "Import math module"
PythonImportFromTurtle = "Import turtle module"
PythonIndex = "Index of the first x occurrence"
PythonInput = "Prompt a value"
PythonInsert = "Insert x at index i in the list"
PythonInt = "Convert x to an integer"
PythonIonFunction = "ion module function prefix"
PythonIsFinite = "Check if x is finite"
PythonIsInfinite = "Check if x is infinity"
PythonIsNaN = "Check if x is a NaN"
PythonIsKeyDown = "Return True if the k key is down"
PythonKandinskyFunction = "kandinsky module function prefix"
PythonKeyLeft = "LEFT ARROW key"
PythonKeyUp = "UP ARROW key"
PythonKeyDown = "DOWN ARROW key"
PythonKeyRight = "RIGHT ARROW key"
PythonKeyOk = "OK key"
PythonKeyBack = "BACK key"
PythonKeyHome = "HOME key"
PythonKeyOnOff = "ON/OFF key"
PythonKeyShift = "SHIFT key"
PythonKeyAlpha = "ALPHA key"
PythonKeyXnt = "X,N,T key"
PythonKeyVar = "VAR key"
PythonKeyToolbox = "TOOLBOX key"
PythonKeyBackspace = "BACKSPACE key"
PythonKeyExp = "EXPONENTIAL key"
PythonKeyLn = "NATURAL LOGARITHM key"
PythonKeyLog = "DECIMAL LOGARITHM key"
PythonKeyImaginary = "IMAGINARY I key"
PythonKeyComma = "COMMA key"
PythonKeyPower = "POWER key"
PythonKeySine = "SINE key"
PythonKeyCosine = "COSINE key"
PythonKeyTangent = "TANGENT key"
PythonKeyPi = "PI key"
PythonKeySqrt = "SQUARE ROOT key"
PythonKeySquare = "SQUARE key"
PythonKeySeven = "7 key"
PythonKeyEight = "8 key"
PythonKeyNine = "9 key"
PythonKeyLeftParenthesis = "LEFT PARENTHESIS key"
PythonKeyRightParenthesis = "RIGHT PARENTHESIS key"
PythonKeyFour = "4 key"
PythonKeyFive = "5 key"
PythonKeySix = "6 key"
PythonKeyMultiplication = "MULTIPLICATION key"
PythonKeyDivision = "DIVISION key"
PythonKeyOne = "1 key"
PythonKeyTwo = "2 key"
PythonKeyThree = "3 key"
PythonKeyPlus = "PLUS key"
PythonKeyMinus = "MINUS key"
PythonKeyZero = "0 key"
PythonKeyDot = "DOT key"
PythonKeyEe = "10 POWER X key"
PythonKeyAns = "ANS key"
PythonKeyExe = "EXE key"
PythonLdexp = "Return x*(2**i), inverse of frexp"
PythonLength = "Length of an object"
PythonLgamma = "Log-gamma function"
@@ -71,10 +121,12 @@ PythonMathFunction = "math module function prefix"
PythonMax = "Maximum"
PythonMin = "Minimum"
PythonModf = "Fractional and integer parts of x"
PythonMonotonic = "Value of a monotonic clock"
PythonOct = "Convert integer to octal"
PythonPhase = "Phase of z"
PythonConstantPi = "3.141592653589794"
PythonPolar = "z in polar coordinates"
PythonPop = "Remove and return the last item"
PythonPower = "x raised to the power y"
PythonPrint = "Print object"
PythonRadians = "Convert x from degrees to radians"
@@ -85,16 +137,20 @@ PythonRandrange = "Random number in range(start, stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
PythonSeed = "Initialize random number generator"
PythonSetPixel = "Color pixel (x,y)"
PythonSin = "Sine"
PythonSinh = "Hyperbolic sine"
PythonSorted = "Sort a list"
PythonSleep = "Suspend the execution for t seconds"
PythonSort = "Sort the list"
PythonSqrt = "Square root"
PythonSum = "Sum the items of a list"
PythonTan = "Tangent"
PythonTanh = "Hyperbolic tangent"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"

View File

@@ -12,6 +12,7 @@ PythonSingleQuote = "Single quote"
PythonAbs = "Absolute value/Magnitude"
PythonAcos = "Arc cosine"
PythonAcosh = "Arc hyperbolic cosine"
PythonAppend = "Add x to the end of the list"
PythonAsin = "Arc sine"
PythonAsinh = "Arc hyperbolic sine"
PythonAtan = "Arc tangent"
@@ -20,19 +21,21 @@ PythonAtanh = "Arc hyperbolic tangent"
PythonBin = "Convert integer to binary"
PythonCeil = "Ceiling"
PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawString = "Display a text from pixel (x,y)"
PythonConstantE = "2.718281828459046"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
PythonEval = "Returns the evaluated expression"
PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
@@ -46,21 +49,68 @@ PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
PythonHex = "Convert integer to hexadecimal"
PythonImportCmath = "Import cmath module"
PythonImportIon = "Import ion module"
PythonImportKandinsky = "Import kandinsky module"
PythonImportRandom = "Import random module"
PythonImportMath = "Import math module"
PythonImportTime = "Import time module"
PythonImportTurtle = "Import turtle module"
PythonImportFromCmath = "Import cmath module"
PythonImportFromKandinsky = "Import kandinsky module"
PythonImportFromRandom = "Import random module"
PythonImportFromMath = "Import math module"
PythonImportFromTurtle = "Import turtle module"
PythonIndex = "Index of the first x occurrence"
PythonInput = "Prompt a value"
PythonInsert = "Insert x at index i in the list"
PythonInt = "Convert x to an integer"
PythonIonFunction = "ion module function prefix"
PythonIsFinite = "Check if x is finite"
PythonIsInfinite = "Check if x is infinity"
PythonIsKeyDown = "Return True if the k key is down"
PythonIsNaN = "Check if x is a NaN"
PythonKandinskyFunction = "kandinsky module function prefix"
PythonKeyLeft = "LEFT ARROW key"
PythonKeyUp = "UP ARROW key"
PythonKeyDown = "DOWN ARROW key"
PythonKeyRight = "RIGHT ARROW key"
PythonKeyOk = "OK key"
PythonKeyBack = "BACK key"
PythonKeyHome = "HOME key"
PythonKeyOnOff = "ON/OFF key"
PythonKeyShift = "SHIFT key"
PythonKeyAlpha = "ALPHA key"
PythonKeyXnt = "X,N,T key"
PythonKeyVar = "VAR key"
PythonKeyToolbox = "TOOLBOX key"
PythonKeyBackspace = "BACKSPACE key"
PythonKeyExp = "EXPONENTIAL key"
PythonKeyLn = "NATURAL LOGARITHM key"
PythonKeyLog = "DECIMAL LOGARITHM key"
PythonKeyImaginary = "IMAGINARY I key"
PythonKeyComma = "COMMA key"
PythonKeyPower = "POWER key"
PythonKeySine = "SINE key"
PythonKeyCosine = "COSINE key"
PythonKeyTangent = "TANGENT key"
PythonKeyPi = "PI key"
PythonKeySqrt = "SQUARE ROOT key"
PythonKeySquare = "SQUARE key"
PythonKeySeven = "7 key"
PythonKeyEight = "8 key"
PythonKeyNine = "9 key"
PythonKeyLeftParenthesis = "LEFT PARENTHESIS key"
PythonKeyRightParenthesis = "RIGHT PARENTHESIS key"
PythonKeyFour = "4 key"
PythonKeyFive = "5 key"
PythonKeySix = "6 key"
PythonKeyMultiplication = "MULTIPLICATION key"
PythonKeyDivision = "DIVISION key"
PythonKeyOne = "1 key"
PythonKeyTwo = "2 key"
PythonKeyThree = "3 key"
PythonKeyPlus = "PLUS key"
PythonKeyMinus = "MINUS key"
PythonKeyZero = "0 key"
PythonKeyDot = "DOT key"
PythonKeyEe = "10 POWER X key"
PythonKeyAns = "ANS key"
PythonKeyExe = "EXE key"
PythonLdexp = "Return x*(2**i), inverse of frexp"
PythonLength = "Length of an object"
PythonLgamma = "Log-gamma function"
@@ -71,10 +121,12 @@ PythonMathFunction = "math module function prefix"
PythonMax = "Maximum"
PythonMin = "Minimum"
PythonModf = "Fractional and integer parts of x"
PythonMonotonic = "Value of a monotonic clock"
PythonOct = "Convert integer to octal"
PythonPhase = "Phase of z"
PythonConstantPi = "3.141592653589794"
PythonPolar = "z in polar coordinates"
PythonPop = "Remove and return the last item"
PythonPower = "x raised to the power y"
PythonPrint = "Print object"
PythonRadians = "Convert x from degrees to radians"
@@ -85,16 +137,20 @@ PythonRandrange = "Random number in range(start, stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
PythonSeed = "Initialize random number generator"
PythonSetPixel = "Color pixel (x,y)"
PythonSin = "Sine"
PythonSinh = "Hyperbolic sine"
PythonSorted = "Sort a list"
PythonSleep = "Suspend the execution for t seconds"
PythonSort = "Sort the list"
PythonSqrt = "Square root"
PythonSum = "Sum the items of a list"
PythonTan = "Tangent"
PythonTanh = "Hyperbolic tangent"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"

View File

@@ -12,6 +12,7 @@ PythonSingleQuote = "Single quote"
PythonAbs = "Absolute value/Magnitude"
PythonAcos = "Arc cosine"
PythonAcosh = "Arc hyperbolic cosine"
PythonAppend = "Add x to the end of the list"
PythonAsin = "Arc sine"
PythonAsinh = "Arc hyperbolic sine"
PythonAtan = "Arc tangent"
@@ -20,19 +21,21 @@ PythonAtanh = "Arc hyperbolic tangent"
PythonBin = "Convert integer to binary"
PythonCeil = "Ceiling"
PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawString = "Display a text from pixel (x,y)"
PythonConstantE = "2.718281828459046"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
PythonEval = "Returns the evaluated expression"
PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
@@ -46,21 +49,68 @@ PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
PythonHex = "Convert integer to hexadecimal"
PythonImportCmath = "Import cmath module"
PythonImportIon = "Import ion module"
PythonImportKandinsky = "Import kandinsky module"
PythonImportRandom = "Import random module"
PythonImportMath = "Import math module"
PythonImportTime = "Import time module"
PythonImportTurtle = "Import turtle module"
PythonImportFromCmath = "Import cmath module"
PythonImportFromKandinsky = "Import kandinsky module"
PythonImportFromRandom = "Import random module"
PythonImportFromMath = "Import math module"
PythonImportFromTurtle = "Import turtle module"
PythonIndex = "Index of the first x occurrence"
PythonInput = "Prompt a value"
PythonInsert = "Insert x at index i in the list"
PythonInt = "Convert x to an integer"
PythonIonFunction = "ion module function prefix"
PythonIsFinite = "Check if x is finite"
PythonIsInfinite = "Check if x is infinity"
PythonIsKeyDown = "Return True if the k key is down"
PythonIsNaN = "Check if x is a NaN"
PythonKandinskyFunction = "kandinsky module function prefix"
PythonKeyLeft = "LEFT ARROW key"
PythonKeyUp = "UP ARROW key"
PythonKeyDown = "DOWN ARROW key"
PythonKeyRight = "RIGHT ARROW key"
PythonKeyOk = "OK key"
PythonKeyBack = "BACK key"
PythonKeyHome = "HOME key"
PythonKeyOnOff = "ON/OFF key"
PythonKeyShift = "SHIFT key"
PythonKeyAlpha = "ALPHA key"
PythonKeyXnt = "X,N,T key"
PythonKeyVar = "VAR key"
PythonKeyToolbox = "TOOLBOX key"
PythonKeyBackspace = "BACKSPACE key"
PythonKeyExp = "EXPONENTIAL key"
PythonKeyLn = "NATURAL LOGARITHM key"
PythonKeyLog = "DECIMAL LOGARITHM key"
PythonKeyImaginary = "IMAGINARY I key"
PythonKeyComma = "COMMA key"
PythonKeyPower = "POWER key"
PythonKeySine = "SINE key"
PythonKeyCosine = "COSINE key"
PythonKeyTangent = "TANGENT key"
PythonKeyPi = "PI key"
PythonKeySqrt = "SQUARE ROOT key"
PythonKeySquare = "SQUARE key"
PythonKeySeven = "7 key"
PythonKeyEight = "8 key"
PythonKeyNine = "9 key"
PythonKeyLeftParenthesis = "LEFT PARENTHESIS key"
PythonKeyRightParenthesis = "RIGHT PARENTHESIS key"
PythonKeyFour = "4 key"
PythonKeyFive = "5 key"
PythonKeySix = "6 key"
PythonKeyMultiplication = "MULTIPLICATION key"
PythonKeyDivision = "DIVISION key"
PythonKeyOne = "1 key"
PythonKeyTwo = "2 key"
PythonKeyThree = "3 key"
PythonKeyPlus = "PLUS key"
PythonKeyMinus = "MINUS key"
PythonKeyZero = "0 key"
PythonKeyDot = "DOT key"
PythonKeyEe = "10 POWER X key"
PythonKeyAns = "ANS key"
PythonKeyExe = "EXE key"
PythonLdexp = "Return x*(2**i), inverse of frexp"
PythonLength = "Length of an object"
PythonLgamma = "Log-gamma function"
@@ -71,10 +121,12 @@ PythonMathFunction = "math module function prefix"
PythonMax = "Maximum"
PythonMin = "Minimum"
PythonModf = "Fractional and integer parts of x"
PythonMonotonic = "Value of a monotonic clock"
PythonOct = "Convert integer to octal"
PythonPhase = "Phase of z"
PythonConstantPi = "3.141592653589794"
PythonPolar = "z in polar coordinates"
PythonPop = "Remove and return the last item"
PythonPower = "x raised to the power y"
PythonPrint = "Print object"
PythonRadians = "Convert x from degrees to radians"
@@ -85,16 +137,20 @@ PythonRandrange = "Random number in range(start, stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
PythonSeed = "Initialize random number generator"
PythonSetPixel = "Color pixel (x,y)"
PythonSin = "Sine"
PythonSinh = "Hyperbolic sine"
PythonSorted = "Sort a list"
PythonSleep = "Suspend the execution for t seconds"
PythonSort = "Sort the list"
PythonSqrt = "Square root"
PythonSum = "Sum the items of a list"
PythonTan = "Tangent"
PythonTanh = "Hyperbolic tangent"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"

View File

@@ -12,6 +12,7 @@ PythonSingleQuote = "Apostrophe"
PythonAbs = "Valeur absolue/Module"
PythonAcos = "Arc cosinus"
PythonAcosh = "Arc cosinus hyperbolique"
PythonAppend = "Insère x à la fin de la liste"
PythonAsin = "Arc sinus"
PythonAsinh = "Arc sinus hyperbolique"
PythonAtan = "Arc tangente"
@@ -20,12 +21,14 @@ PythonAtanh = "Arc tangente hyperbolique"
PythonBin = "Conversion d'un entier en binaire"
PythonCeil = "Plafond"
PythonChoice = "Nombre aléatoire dans la liste"
PythonClear = "Vide la liste"
PythonCmathFunction = "Préfixe fonction du module cmath"
PythonColor = "Définit une couleur rvb"
PythonComplex = "Renvoie a+ib"
PythonCopySign = "Renvoie x avec le signe de y"
PythonCos = "Cosinus"
PythonCosh = "Cosinus hyperbolique"
PythonCount = "Compte les occurrences de x"
PythonDegrees = "Conversion de radians en degrés"
PythonDivMod = "Quotient et reste"
PythonDrawString = "Affiche un texte au pixel (x,y)"
@@ -46,21 +49,68 @@ PythonGetPixel = "Renvoie la couleur du pixel (x,y)"
PythonGetrandbits = "Nombre aléatoire sur k bits"
PythonHex = "Conversion entier en hexadécimal"
PythonImportCmath = "Importation du module cmath"
PythonImportIon = "Importation du module ion"
PythonImportKandinsky = "Importation du module kandinsky"
PythonImportRandom = "Importation du module random"
PythonImportMath = "Importation du module math"
PythonImportTurtle = "Importation du module turtle"
PythonImportFromCmath = "Importation du module cmath"
PythonImportFromKandinsky = "Importation du module kandinsky"
PythonImportFromRandom = "Importation du module random"
PythonImportFromMath = "Importation du module math"
PythonImportFromTurtle = "Importation du module turtle"
PythonImportTime = "Importation du module time"
PythonIndex = "Indice première occurrence de x"
PythonInput = "Entrer une valeur"
PythonInsert = "Insère x en i-ème position"
PythonInt = "Conversion en entier"
PythonIonFunction = "Préfixe fonction module ion"
PythonIsFinite = "Teste si x est fini"
PythonIsInfinite = "Teste si x est infini"
PythonIsKeyDown = "Renvoie True si touche k enfoncée"
PythonIsNaN = "Teste si x est NaN"
PythonKandinskyFunction = "Préfixe fonction module kandinsky"
PythonKeyLeft = "Touche FLECHE GAUCHE"
PythonKeyUp = "Touche FLECHE HAUT"
PythonKeyDown = "Touche FLECHE BAS"
PythonKeyRight = "Touche FLECHE DROITE"
PythonKeyOk = "Touche OK"
PythonKeyBack = "Touche RETOUR"
PythonKeyHome = "Touche HOME"
PythonKeyOnOff = "Touche ON/OFF"
PythonKeyShift = "Touche SHIFT"
PythonKeyAlpha = "Touche ALPHA"
PythonKeyXnt = "Touche X,N,T"
PythonKeyVar = "Touche VAR"
PythonKeyToolbox = "Touche BOITE A OUTILS"
PythonKeyBackspace = "Touche EFFACER"
PythonKeyExp = "Touche EXPONENTIELLE"
PythonKeyLn = "Touche LOGARITHME NEPERIEN"
PythonKeyLog = "Touche LOGARITHME DECIMAL"
PythonKeyImaginary = "Touche I IMAGINAIRE"
PythonKeyComma = "Touche VIRGULE"
PythonKeyPower = "Touche PUISSANCE"
PythonKeySine = "Touche SINUS"
PythonKeyCosine = "Touche COSINUS"
PythonKeyTangent = "Touche TANGENTE"
PythonKeyPi = "Touche PI"
PythonKeySqrt = "Touche RACINE CARREE"
PythonKeySquare = "Touche CARRE"
PythonKeySeven = "Touche 7"
PythonKeyEight = "Touche 8"
PythonKeyNine = "Touche 9"
PythonKeyLeftParenthesis = "Touche PARENTHESE GAUCHE"
PythonKeyRightParenthesis = "Touche PARENTHESE DROITE"
PythonKeyFour = "Touche 4"
PythonKeyFive = "Touche 5"
PythonKeySix = "Touche 6"
PythonKeyMultiplication = "Touche MULTIPLICATION"
PythonKeyDivision = "Touche DIVISION"
PythonKeyOne = "Touche 1"
PythonKeyTwo = "Touche 2"
PythonKeyThree = "Touche 3"
PythonKeyPlus = "Touche PLUS"
PythonKeyMinus = "Touche MOINS"
PythonKeyZero = "Touche 0"
PythonKeyDot = "Touche POINT"
PythonKeyEe = "Touche 10 PUISSANCE X"
PythonKeyAns = "Touche ANS"
PythonKeyExe = "Touche EXE"
PythonLdexp = "Inverse de frexp : x*(2**i)"
PythonLength = "Longueur d'un objet"
PythonLgamma = "Logarithme de la fonction gamma"
@@ -71,10 +121,12 @@ PythonMathFunction = "Préfixe fonction du module math"
PythonMax = "Maximum"
PythonMin = "Minimum"
PythonModf = "Parties fractionnaire et entière"
PythonMonotonic = "Renvoie la valeur de l'horloge"
PythonOct = "Conversion en octal"
PythonPhase = "Argument de z"
PythonConstantPi = "3.141592653589793"
PythonPolar = "Conversion en polaire"
PythonPop = "Supprime le dernier élément"
PythonPower = "x à la puissance y"
PythonPrint = "Affiche l'objet"
PythonRadians = "Conversion de degrés en radians"
@@ -85,16 +137,20 @@ PythonRandrange = "Nombre dans range(start, stop)"
PythonRangeStartStop = "Liste de start à stop-1"
PythonRangeStop = "Liste de 0 à stop-1"
PythonRect = "Conversion en algébrique"
PythonRemove = "Supprime le premier x de la liste"
PythonReverse = "Inverse les éléments de la liste"
PythonRound = "Arrondi à n décimales"
PythonSeed = "Initialiser générateur aléatoire"
PythonSetPixel = "Colore le pixel (x,y)"
PythonSin = "Sinus"
PythonSinh = "Sinus hyperbolique"
PythonSorted = "Tri d'une liste"
PythonSleep = "Suspend l'exécution t secondes"
PythonSort = "Trie la liste"
PythonSqrt = "Racine carrée"
PythonSum = "Somme des éléments d'une liste"
PythonSum = "Somme des éléments de la liste"
PythonTan = "Tangente"
PythonTanh = "Tangente hyperbolique"
PythonTimeFunction = "Préfixe fonction module time"
PythonTrunc = "Troncature entière"
PythonTurtleBackward = "Recule de x pixels"
PythonTurtleBlack = "Couleur noire"
@@ -116,7 +172,7 @@ PythonTurtlePendown = "Abaisse le crayon"
PythonTurtlePensize = "Taille du tracé en pixels"
PythonTurtlePenup = "Relève le crayon"
PythonTurtlePink = "Couleur rose"
PythonTurtlePosition = "Renvoie la position (x,y) actuelle"
PythonTurtlePosition = "Renvoie la position (x,y)"
PythonTurtlePurple = "Couleur violette"
PythonTurtleRed = "Couleur rouge"
PythonTurtleReset = "Réinitialise le dessin"

View File

@@ -12,6 +12,7 @@ PythonReal = "Real part of z"
PythonAbs = "Absolute value/Magnitude"
PythonAcos = "Arc cosine"
PythonAcosh = "Arc hyperbolic cosine"
PythonAppend = "Add x to the end of the list"
PythonAsin = "Arc sine"
PythonAsinh = "Arc hyperbolic sine"
PythonAtan = "Arc tangent"
@@ -20,19 +21,21 @@ PythonAtanh = "Arc hyperbolic tangent"
PythonBin = "Convert integer to binary"
PythonCeil = "Ceiling"
PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawString = "Display a text from pixel (x,y)"
PythonConstantE = "2.718281828459046"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
PythonEval = "Returns the evaluated expression"
PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
@@ -46,21 +49,68 @@ PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
PythonHex = "Convert integer to hexadecimal"
PythonImportCmath = "Import cmath module"
PythonImportIon = "Import ion module"
PythonImportKandinsky = "Import kandinsky module"
PythonImportRandom = "Import random module"
PythonImportMath = "Import math module"
PythonImportTime = "Import time module"
PythonImportTurtle = "Import turtle module"
PythonImportFromCmath = "Import cmath module"
PythonImportFromKandinsky = "Import kandinsky module"
PythonImportFromRandom = "Import random module"
PythonImportFromMath = "Import math module"
PythonImportFromTurtle = "Import turtle module"
PythonIndex = "Index of the first x occurrence"
PythonInput = "Prompt a value"
PythonInsert = "Insert x at index i in the list"
PythonInt = "Convert x to an integer"
PythonIonFunction = "ion module function prefix"
PythonIsFinite = "Check if x is finite"
PythonIsInfinite = "Check if x is infinity"
PythonIsKeyDown = "Return True if the k key is down"
PythonIsNaN = "Check if x is a NaN"
PythonKandinskyFunction = "kandinsky module function prefix"
PythonKeyLeft = "LEFT ARROW key"
PythonKeyUp = "UP ARROW key"
PythonKeyDown = "DOWN ARROW key"
PythonKeyRight = "RIGHT ARROW key"
PythonKeyOk = "OK key"
PythonKeyBack = "BACK key"
PythonKeyHome = "HOME key"
PythonKeyOnOff = "ON/OFF key"
PythonKeyShift = "SHIFT key"
PythonKeyAlpha = "ALPHA key"
PythonKeyXnt = "X,N,T key"
PythonKeyVar = "VAR key"
PythonKeyToolbox = "TOOLBOX key"
PythonKeyBackspace = "BACKSPACE key"
PythonKeyExp = "EXPONENTIAL key"
PythonKeyLn = "NATURAL LOGARITHM key"
PythonKeyLog = "DECIMAL LOGARITHM key"
PythonKeyImaginary = "IMAGINARY I key"
PythonKeyComma = "COMMA key"
PythonKeyPower = "POWER key"
PythonKeySine = "SINE key"
PythonKeyCosine = "COSINE key"
PythonKeyTangent = "TANGENT key"
PythonKeyPi = "PI key"
PythonKeySqrt = "SQUARE ROOT key"
PythonKeySquare = "SQUARE key"
PythonKeySeven = "7 key"
PythonKeyEight = "8 key"
PythonKeyNine = "9 key"
PythonKeyLeftParenthesis = "LEFT PARENTHESIS key"
PythonKeyRightParenthesis = "RIGHT PARENTHESIS key"
PythonKeyFour = "4 key"
PythonKeyFive = "5 key"
PythonKeySix = "6 key"
PythonKeyMultiplication = "MULTIPLICATION key"
PythonKeyDivision = "DIVISION key"
PythonKeyOne = "1 key"
PythonKeyTwo = "2 key"
PythonKeyThree = "3 key"
PythonKeyPlus = "PLUS key"
PythonKeyMinus = "MINUS key"
PythonKeyZero = "0 key"
PythonKeyDot = "DOT key"
PythonKeyEe = "10 POWER X key"
PythonKeyAns = "ANS key"
PythonKeyExe = "EXE key"
PythonLdexp = "Return x*(2**i), inverse of frexp"
PythonLength = "Length of an object"
PythonLgamma = "Log-gamma function"
@@ -71,10 +121,12 @@ PythonMathFunction = "math module function prefix"
PythonMax = "Maximum"
PythonMin = "Minimum"
PythonModf = "Fractional and integer parts of x"
PythonMonotonic = "Value of a monotonic clock"
PythonOct = "Convert integer to octal"
PythonPhase = "Phase of z"
PythonConstantPi = "3.141592653589794"
PythonPolar = "z in polar coordinates"
PythonPop = "Remove and return the last item"
PythonPower = "x raised to the power y"
PythonPrint = "Print object"
PythonRadians = "Convert x from degrees to radians"
@@ -85,16 +137,20 @@ PythonRandrange = "Random number in range(start, stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
PythonSeed = "Initialize random number generator"
PythonSetPixel = "Color pixel (x,y)"
PythonSin = "Sine"
PythonSinh = "Hyperbolic sine"
PythonSorted = "Sort a list"
PythonSleep = "Suspend the execution for t seconds"
PythonSort = "Sort the list"
PythonSqrt = "Square root"
PythonSum = "Sum the items of a list"
PythonTan = "Tangent"
PythonTanh = "Hyperbolic tangent"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"

View File

@@ -10,6 +10,8 @@ PythonCommand1J = "1j"
PythonCommandAbs = "abs(x)"
PythonCommandAcos = "acos(x)"
PythonCommandAcosh = "acosh(x)"
PythonCommandAppend = "list.append(x)"
PythonCommandAppendWithoutArg = ".append(\x11)"
PythonCommandAsin = "asin(x)"
PythonCommandAsinh = "asinh(x)"
PythonCommandAtan = "atan(x)"
@@ -18,6 +20,8 @@ PythonCommandAtanh = "atanh(x)"
PythonCommandBin = "bin(x)"
PythonCommandCeil = "ceil(x)"
PythonCommandChoice = "choice(list)"
PythonCommandClear = "list.clear()"
PythonCommandClearWithoutArg = ".clear()"
PythonCommandCmathFunction = "cmath.function"
PythonCommandCmathFunctionWithoutArg = "cmath.\x11"
PythonCommandColor = "color(r,g,b)"
@@ -27,6 +31,8 @@ PythonCommandCopySign = "copysign(x,y)"
PythonCommandCos = "cos(x)"
PythonCommandCosComplex = "cos(z)"
PythonCommandCosh = "cosh(x)"
PythonCommandCount = "list.count(x)"
PythonCommandCountWithoutArg = ".count(\x11)"
PythonCommandDegrees = "degrees(x)"
PythonCommandDivMod = "divmod(a,b)"
PythonCommandDrawString = "draw_string(\"text\",x,y)"
@@ -48,24 +54,81 @@ PythonCommandGetPixel = "get_pixel(x,y)"
PythonCommandGetrandbits = "getrandbits(k)"
PythonCommandHex = "hex(x)"
PythonCommandImag = "z.imag"
PythonCommandImagWithoutArg = "\x11.imag"
PythonCommandImagWithoutArg = ".imag"
PythonCommandImportFromCmath = "from cmath import *"
PythonCommandImportFromMath = "from math import *"
PythonCommandImportFromIon = "from ion import *"
PythonCommandImportFromKandinsky = "from kandinsky import *"
PythonCommandImportFromMath = "from math import *"
PythonCommandImportFromRandom = "from random import *"
PythonCommandImportFromTime = "from time import *"
PythonCommandImportFromTurtle = "from turtle import *"
PythonCommandImportCmath = "import cmath"
PythonCommandImportIon = "import ion"
PythonCommandImportKandinsky = "import kandinsky"
PythonCommandImportRandom = "import random"
PythonCommandImportMath = "import math"
PythonCommandImportRandom = "import random"
PythonCommandImportTime = "import time"
PythonCommandImportTurtle = "import turtle"
PythonCommandIndex = "list.index(x)"
PythonCommandIndexWithoutArg = ".index(\x11)"
PythonCommandInput = "input(\"text\")"
PythonCommandInsert = "list.insert(i,x)"
PythonCommandInsertWithoutArg = ".insert(\x11,)"
PythonCommandInt = "int(x)"
PythonCommandIonFunction = "ion.function"
PythonCommandIonFunctionWithoutArg = "ion.\x11"
PythonCommandIsFinite = "isfinite(x)"
PythonCommandIsInfinite = "isinf(x)"
PythonCommandIsNaN = "isnan(x)"
PythonCommandKandinskyFunction = "kandinsky.function"
PythonCommandKandinskyFunctionWithoutArg = "kandinsky.\x11"
PythonCommandKeyLeft = "KEY_LEFT"
PythonCommandKeyUp = "KEY_UP"
PythonCommandKeyDown = "KEY_DOWN"
PythonCommandKeyRight = "KEY_RIGHT"
PythonCommandKeyOk = "KEY_OK"
PythonCommandKeyBack = "KEY_BACK"
PythonCommandKeyHome = "KEY_HOME"
PythonCommandKeyOnOff = "KEY_ONOFF"
PythonCommandKeyShift = "KEY_SHIFT"
PythonCommandKeyAlpha = "KEY_ALPHA"
PythonCommandKeyXnt = "KEY_XNT"
PythonCommandKeyVar = "KEY_VAR"
PythonCommandKeyToolbox = "KEY_TOOLBOX"
PythonCommandKeyBackspace = "KEY_BACKSPACE"
PythonCommandKeyExp = "KEY_EXP"
PythonCommandKeyLn = "KEY_LN"
PythonCommandKeyLog = "KEY_LOG"
PythonCommandKeyImaginary = "KEY_IMAGINARY"
PythonCommandKeyComma = "KEY_COMMA"
PythonCommandKeyPower = "KEY_POWER"
PythonCommandKeySine = "KEY_SINE"
PythonCommandKeyCosine = "KEY_COSINE"
PythonCommandKeyTangent = "KEY_TANGENT"
PythonCommandKeyPi = "KEY_PI"
PythonCommandKeySqrt = "KEY_SQRT"
PythonCommandKeySquare = "KEY_SQUARE"
PythonCommandKeySeven = "KEY_SEVEN"
PythonCommandKeyEight = "KEY_EIGHT"
PythonCommandKeyNine = "KEY_NINE"
PythonCommandKeyLeftParenthesis = "KEY_LEFTPARENTHESIS"
PythonCommandKeyRightParenthesis = "KEY_RIGHTPARENTHESIS"
PythonCommandKeyFour = "KEY_FOUR"
PythonCommandKeyFive = "KEY_FIVE"
PythonCommandKeySix = "KEY_SIX"
PythonCommandKeyMultiplication = "KEY_MULTIPLICATION"
PythonCommandKeyDivision = "KEY_DIVISION"
PythonCommandKeyOne = "KEY_ONE"
PythonCommandKeyTwo = "KEY_TWO"
PythonCommandKeyThree = "KEY_THREE"
PythonCommandKeyPlus = "KEY_PLUS"
PythonCommandKeyMinus = "KEY_MINUS"
PythonCommandKeyZero = "KEY_ZERO"
PythonCommandKeyDot = "KEY_DOT"
PythonCommandKeyEe = "KEY_EE"
PythonCommandKeyAns = "KEY_ANS"
PythonCommandKeyExe = "KEY_EXE"
PythonCommandIsKeyDown = "keydown(k)"
PythonCommandLdexp = "ldexp(x,i)"
PythonCommandLength = "len(object)"
PythonCommandLgamma = "lgamma(x)"
@@ -78,9 +141,12 @@ PythonCommandMathFunctionWithoutArg = "math.\x11"
PythonCommandMax = "max(list)"
PythonCommandMin = "min(list)"
PythonCommandModf = "modf(x)"
PythonCommandMonotonic = "monotonic()"
PythonCommandOct = "oct(x)"
PythonCommandPhase = "phase(z)"
PythonCommandPolar = "polar(z)"
PythonCommandPop = "list.pop()"
PythonCommandPopWithoutArg = ".pop()"
PythonCommandPower = "pow(x,y)"
PythonCommandPrint = "print(object)"
PythonCommandRadians = "radians(x)"
@@ -92,20 +158,29 @@ PythonCommandRandrange = "randrange(start, stop)"
PythonCommandRangeStartStop = "range(start, stop)"
PythonCommandRangeStop = "range(stop)"
PythonCommandReal = "z.real"
PythonCommandRealWithoutArg = "\x11.real"
PythonCommandRealWithoutArg = ".real"
PythonCommandRect = "rect(r, arg)"
PythonCommandRemove = "list.remove(x)"
PythonCommandRemoveWithoutArg = ".remove(\x11)"
PythonCommandReverse = "list.reverse()"
PythonCommandReverseWithoutArg = ".reverse()"
PythonCommandRound = "round(x, n)"
PythonCommandSeed = "seed(x)"
PythonCommandSetPixel = "set_pixel(x,y,color)"
PythonCommandSin = "sin(x)"
PythonCommandSinComplex = "sin(z)"
PythonCommandSinh = "sinh(x)"
PythonCommandSleep = "sleep(t)"
PythonCommandSort = "list.sort()"
PythonCommandSortWithoutArg = ".sort()"
PythonCommandSorted = "sorted(list)"
PythonCommandSqrt = "sqrt(x)"
PythonCommandSqrtComplex = "sqrt(z)"
PythonCommandSum = "sum(list)"
PythonCommandTan = "tan(x)"
PythonCommandTanh = "tanh(x)"
PythonCommandTimeFunction = "time.function"
PythonCommandTimeFunctionWithoutArg = "time.\x11"
PythonCommandTrunc = "trunc(x)"
PythonCommandTurtleFunction = "turtle.function"
PythonCommandTurtleFunctionWithoutArg = "turtle.\x11"

View File

@@ -6,7 +6,9 @@
#include <assert.h>
#include <escher/metric.h>
#include <poincare/preferences.h>
#include "../apps_container.h"
#include <apps/global_preferences.h>
#include <apps/apps_container.h>
#include <python/port/helpers.h>
extern "C" {
#include <stdlib.h>
@@ -28,13 +30,13 @@ ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDe
TextFieldDelegate(),
MicroPython::ExecutionEnvironment(),
m_pythonDelegate(pythonDelegate),
m_rowHeight(Poincare::Preferences::sharedPreferences()->KDPythonFont()->glyphSize().height()),
m_importScriptsWhenViewAppears(false),
m_selectableTableView(this, this, this, this),
m_editCell(this, pythonDelegate, this),
m_scriptStore(scriptStore),
m_sandboxController(this, this),
m_inputRunLoopActive(false)
m_inputRunLoopActive(false),
m_preventEdition(false)
#if EPSILON_GETOPT
, m_locked(lockOnConsole)
#endif
@@ -76,10 +78,21 @@ void ConsoleController::autoImport() {
}
void ConsoleController::runAndPrintForCommand(const char * command) {
m_consoleStore.pushCommand(command, strlen(command));
const char * storedCommand = m_consoleStore.pushCommand(command);
assert(m_outputAccumulationBuffer[0] == '\0');
runCode(command);
// Draw the console before running the code
m_preventEdition = true;
m_editCell.setText("");
m_editCell.setPrompt("");
refreshPrintOutput();
runCode(storedCommand);
m_preventEdition = false;
m_editCell.setPrompt(sStandardPromptText);
m_editCell.setEditing(true);
flushOutputAccumulationBufferToStore();
m_consoleStore.deleteLastLineIfEmpty();
}
@@ -119,6 +132,7 @@ const char * ConsoleController::inputText(const char * prompt) {
}
}
const char * previousPrompt = m_editCell.promptText();
m_editCell.setPrompt(promptText);
m_editCell.setText("");
@@ -141,7 +155,9 @@ const char * ConsoleController::inputText(const char * prompt) {
printText(text, strlen(text));
flushOutputAccumulationBufferToStore();
m_editCell.setPrompt(sStandardPromptText);
m_editCell.setPrompt(previousPrompt);
m_editCell.setText("");
refreshPrintOutput();
return text;
}
@@ -202,7 +218,7 @@ int ConsoleController::numberOfRows() const {
}
KDCoordinate ConsoleController::rowHeight(int j) {
return m_rowHeight;
return GlobalPreferences::sharedGlobalPreferences()->font()->glyphSize().height();
}
KDCoordinate ConsoleController::cumulatedHeightFromIndex(int j) {
@@ -359,6 +375,15 @@ void ConsoleController::resetSandbox() {
m_sandboxController.reset();
}
void ConsoleController::refreshPrintOutput() {
m_selectableTableView.reloadData();
m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines());
if (m_preventEdition) {
m_editCell.setEditing(false);
}
AppsContainer::sharedAppsContainer()->redrawWindow();
}
/* printText is called by the Python machine.
* The text argument is not always null-terminated. */
void ConsoleController::printText(const char * text, size_t length) {
@@ -381,6 +406,7 @@ void ConsoleController::printText(const char * text, size_t length) {
assert(textCutIndex == length - 1);
appendTextToOutputAccumulationBuffer(text, length-1);
flushOutputAccumulationBufferToStore();
micropython_port_vm_hook_refresh_print();
}
void ConsoleController::autoImportScript(Script script, bool force) {
@@ -424,7 +450,7 @@ void ConsoleController::autoImportScript(Script script, bool force) {
}
void ConsoleController::flushOutputAccumulationBufferToStore() {
m_consoleStore.pushResult(m_outputAccumulationBuffer, strlen(m_outputAccumulationBuffer));
m_consoleStore.pushResult(m_outputAccumulationBuffer);
emptyOutputAccumulationBuffer();
}

View File

@@ -17,7 +17,6 @@ class App;
class ConsoleController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate, public MicroPython::ExecutionEnvironment {
public:
ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore
#if EPSILON_GETOPT
, bool m_lockOnConsole
@@ -64,6 +63,7 @@ public:
void displaySandbox() override;
void hideSandbox() override;
void resetSandbox() override;
void refreshPrintOutput() override;
void printText(const char * text, size_t length) override;
const char * inputText(const char * prompt) override;
@@ -78,7 +78,8 @@ private:
static constexpr size_t k_maxImportCommandSize = 5 + 9 + TextField::maxBufferSize(); // strlen(k_importCommand1) + strlen(k_importCommand2) + TextField::maxBufferSize()
static constexpr int LineCellType = 0;
static constexpr int EditCellType = 1;
static constexpr int k_numberOfLineCells = 20; // May change depending on the screen height
static constexpr int k_numberOfLineCells = (Ion::Display::Height - Metric::TitleBarHeight) / 14 + 2; // 14 = KDFont::SmallFont->glyphSize().height()
// k_numberOfLineCells = (240 - 18)/14 ~ 15.9. The 0.1 cell can be above and below the 15 other cells so we add +2 cells.
static constexpr int k_outputAccumulationBufferSize = 100;
void flushOutputAccumulationBufferToStore();
void appendTextToOutputAccumulationBuffer(const char * text, size_t length);
@@ -86,7 +87,6 @@ private:
size_t firstNewLineCharIndex(const char * text, size_t length);
StackViewController * stackViewController();
App * m_pythonDelegate;
int m_rowHeight;
bool m_importScriptsWhenViewAppears;
ConsoleStore m_consoleStore;
SelectableTableView m_selectableTableView;
@@ -102,6 +102,7 @@ private:
SandboxController m_sandboxController;
bool m_inputRunLoopActive;
bool m_autoImportScripts;
bool m_preventEdition;
#if EPSILON_GETOPT
bool m_locked;
#endif

View File

@@ -2,6 +2,7 @@
#include "console_controller.h"
#include <escher/app.h>
#include <apps/i18n.h>
#include <apps/global_preferences.h>
#include <assert.h>
namespace Code {
@@ -9,8 +10,8 @@ namespace Code {
ConsoleEditCell::ConsoleEditCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * delegate) :
HighlightCell(),
Responder(parentResponder),
m_promptView(Poincare::Preferences::sharedPreferences()->KDPythonFont(), nullptr, 0, 0.5),
m_textField(this, nullptr, TextField::maxBufferSize(), TextField::maxBufferSize(), inputEventHandlerDelegate, delegate, Poincare::Preferences::sharedPreferences()->KDPythonFont())
m_promptView(GlobalPreferences::sharedGlobalPreferences()->font(), nullptr, 0, 0.5),
m_textField(this, nullptr, TextField::maxBufferSize(), TextField::maxBufferSize(), inputEventHandlerDelegate, delegate, GlobalPreferences::sharedGlobalPreferences()->font())
{
}
@@ -27,10 +28,10 @@ View * ConsoleEditCell::subviewAtIndex(int index) {
}
}
void ConsoleEditCell::layoutSubviews() {
void ConsoleEditCell::layoutSubviews(bool force) {
KDSize promptSize = m_promptView.minimalSizeForOptimalDisplay();
m_promptView.setFrame(KDRect(KDPointZero, promptSize.width(), bounds().height()));
m_textField.setFrame(KDRect(KDPoint(promptSize.width(), KDCoordinate(0)), bounds().width() - promptSize.width(), bounds().height()));
m_promptView.setFrame(KDRect(KDPointZero, promptSize.width(), bounds().height()), force);
m_textField.setFrame(KDRect(KDPoint(promptSize.width(), KDCoordinate(0)), bounds().width() - promptSize.width(), bounds().height()), force);
}
void ConsoleEditCell::didBecomeFirstResponder() {

View File

@@ -17,7 +17,7 @@ public:
// View
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
void layoutSubviews(bool force = false) override;
// Responder
void didBecomeFirstResponder() override;
@@ -33,6 +33,7 @@ public:
void setText(const char * text);
bool insertText(const char * text);
void setPrompt(const char * prompt);
const char * promptText() const { return m_promptView.text(); }
private:
PointerTextView m_promptView;
TextField m_textField;

View File

@@ -3,7 +3,7 @@
#include <kandinsky/point.h>
#include <kandinsky/coordinate.h>
#include <apps/i18n.h>
#include <poincare/preferences.h>
#include <apps/global_preferences.h>
namespace Code {
@@ -19,11 +19,11 @@ void ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::setLine(Consol
void ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(bounds(), Palette::CodeBackground);
ctx->drawString(m_line->text(), KDPointZero, Poincare::Preferences::sharedPreferences()->KDPythonFont(), textColor(m_line), isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
ctx->drawString(m_line->text(), KDPointZero, GlobalPreferences::sharedGlobalPreferences()->font(), textColor(m_line), isHighlighted()? Palette::Select : KDColorWhite);
}
KDSize ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::minimalSizeForOptimalDisplay() const {
return Poincare::Preferences::sharedPreferences()->KDPythonFont()->stringSize(m_line->text());
return GlobalPreferences::sharedGlobalPreferences()->font()->stringSize(m_line->text());
}
ConsoleLineCell::ScrollableConsoleLineView::ScrollableConsoleLineView(Responder * parentResponder) :
@@ -35,7 +35,7 @@ ConsoleLineCell::ScrollableConsoleLineView::ScrollableConsoleLineView(Responder
ConsoleLineCell::ConsoleLineCell(Responder * parentResponder) :
HighlightCell(),
Responder(parentResponder),
m_promptView(Poincare::Preferences::sharedPreferences()->KDPythonFont(), I18n::Message::ConsolePrompt, 0, 0.5),
m_promptView(GlobalPreferences::sharedGlobalPreferences()->font(), I18n::Message::ConsolePrompt, 0, 0.5),
m_scrollableView(this),
m_line()
{
@@ -78,16 +78,16 @@ View * ConsoleLineCell::subviewAtIndex(int index) {
return &m_scrollableView;
}
void ConsoleLineCell::layoutSubviews() {
void ConsoleLineCell::layoutSubviews(bool force) {
if (m_line.isCommand()) {
KDSize promptSize = Poincare::Preferences::sharedPreferences()->KDPythonFont()->stringSize(I18n::translate(I18n::Message::ConsolePrompt));
m_promptView.setFrame(KDRect(KDPointZero, promptSize.width(), bounds().height()));
m_scrollableView.setFrame(KDRect(KDPoint(promptSize.width(), 0), bounds().width() - promptSize.width(), bounds().height()));
KDSize promptSize = GlobalPreferences::sharedGlobalPreferences()->font()->stringSize(I18n::translate(I18n::Message::ConsolePrompt));
m_promptView.setFrame(KDRect(KDPointZero, promptSize.width(), bounds().height()), force);
m_scrollableView.setFrame(KDRect(KDPoint(promptSize.width(), 0), bounds().width() - promptSize.width(), bounds().height()), force);
return;
}
assert(m_line.isResult());
m_promptView.setFrame(KDRectZero);
m_scrollableView.setFrame(bounds());
m_promptView.setFrame(KDRectZero, force);
m_scrollableView.setFrame(bounds(), force);
}
void ConsoleLineCell::didBecomeFirstResponder() {

View File

@@ -30,7 +30,7 @@ public:
/* View */
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
void layoutSubviews(bool force = false) override;
/* Responder */
void didBecomeFirstResponder() override;

View File

@@ -55,12 +55,12 @@ int ConsoleStore::numberOfLines() const {
return 0;
}
void ConsoleStore::pushCommand(const char * text, size_t length) {
push(CurrentSessionCommandMarker, text, length);
const char * ConsoleStore::pushCommand(const char * text) {
return push(CurrentSessionCommandMarker, text);
}
void ConsoleStore::pushResult(const char * text, size_t length) {
push(CurrentSessionResultMarker, text, length);
void ConsoleStore::pushResult(const char * text) {
push(CurrentSessionResultMarker, text);
}
void ConsoleStore::deleteLastLineIfEmpty() {
@@ -91,9 +91,9 @@ int ConsoleStore::deleteCommandAndResultsAtIndex(int index) {
return indexOfLineToDelete;
}
void ConsoleStore::push(const char marker, const char * text, size_t length) {
size_t textLength = length;
if (ConsoleLine::sizeOfConsoleLine(length) > k_historySize - 1) {
const char * ConsoleStore::push(const char marker, const char * text) {
size_t textLength = strlen(text);
if (ConsoleLine::sizeOfConsoleLine(textLength) > k_historySize - 1) {
textLength = k_historySize - 1 - 1 - 1; // Marker, null termination and null marker.
}
int i = indexOfNullMarker();
@@ -105,6 +105,7 @@ void ConsoleStore::push(const char marker, const char * text, size_t length) {
m_history[i] = marker;
strlcpy(&m_history[i+1], text, minInt(k_historySize-(i+1),textLength+1));
m_history[i+1+textLength+1] = 0;
return &m_history[i+1];
}
ConsoleLine::Type ConsoleStore::lineTypeForMarker(char marker) const {

View File

@@ -14,8 +14,8 @@ public:
void startNewSession();
ConsoleLine lineAtIndex(int i) const;
int numberOfLines() const;
void pushCommand(const char * text, size_t length);
void pushResult(const char * text, size_t length);
const char * pushCommand(const char * text);
void pushResult(const char * text);
void deleteLastLineIfEmpty();
int deleteCommandAndResultsAtIndex(int index);
private:
@@ -30,7 +30,7 @@ private:
}
return marker;
}
void push(const char marker, const char * text, size_t length);
const char * push(const char marker, const char * text);
ConsoleLine::Type lineTypeForMarker(char marker) const;
int indexOfNullMarker() const;
void deleteLineAtIndex(int index);

View File

@@ -20,20 +20,36 @@ EditorController::EditorController(MenuController * menuController, App * python
void EditorController::setScript(Script script) {
m_script = script;
Script::Data scriptData = m_script.value();
size_t availableScriptSize = scriptData.size + Ion::Storage::sharedStorage()->availableSize();
assert(sizeof(m_areaBuffer) >= availableScriptSize);
// We cannot use strlcpy as the first char reprensenting the importation status can be 0.
memcpy(m_areaBuffer, (const char *)scriptData.buffer, scriptData.size);
m_editorView.setText(m_areaBuffer+1, availableScriptSize-1); // 1 char is taken by the importation status flag
/* We edit the script direclty in the storage buffer. We thus put all the
* storage available space at the end of the current edited script and we set
* its size.
*
* |****|****|m_script|****|**********|¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨|
* available space
* is transformed to:
*
* |****|****|m_script|¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨|****|**********|
* available space
*
* */
size_t newScriptSize = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(m_script);
m_editorView.setText(const_cast<char *>(m_script.scriptContent()), newScriptSize - Script::k_importationStatusSize);
}
void EditorController::willExitApp() {
cleanStorageEmptySpace();
}
// TODO: this should be done in textAreaDidFinishEditing maybe??
bool EditorController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::Back || event == Ion::Events::Home) {
saveScript();
if (event == Ion::Events::OK || event == Ion::Events::Back || event == Ion::Events::Home || event == Ion::Events::USBEnumeration) {
/* Exit the edition on USB enumeration, because the storage needs to be in a
* "clean" state (with all records packed at the beginning of the storage) */
cleanStorageEmptySpace();
stackController()->pop();
return event != Ion::Events::Home;
return event != Ion::Events::Home && event != Ion::Events::USBEnumeration;
}
return false;
}
@@ -48,16 +64,11 @@ void EditorController::viewWillAppear() {
}
void EditorController::viewDidDisappear() {
m_editorView.resetSelection();
m_menuController->scriptContentEditionDidFinish();
}
bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) {
if (event == Ion::Events::Var) {
/* We save the script before displaying the Variable box to add new
* functions or variables. */
saveScript();
return false;
}
if (App::app()->textInputDidReceiveEvent(textArea, event)) {
return true;
}
@@ -66,7 +77,7 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events:
return true;
}
if (event == Ion::Events::Backspace) {
if (event == Ion::Events::Backspace && textArea->selectionIsEmpty()) {
/* If the cursor is on the left of the text of a line, backspace one
* indentation space at a time. */
const char * text = textArea->text();
@@ -118,11 +129,15 @@ StackViewController * EditorController::stackController() {
return static_cast<StackViewController *>(parentResponder());
}
void EditorController::saveScript() {
size_t sizeOfValue = strlen(m_areaBuffer+1)+1+1; // size of scriptContent + size of importation status
Script::ErrorStatus err = m_script.setValue({.buffer=m_areaBuffer, .size=sizeOfValue});
assert(err != Script::ErrorStatus::NotEnoughSpaceAvailable && err != Script::ErrorStatus::RecordDoesNotExist); // This should not happen as we set the text area according to the available space in the Kallax
(void) err;
void EditorController::cleanStorageEmptySpace() {
if (m_script.isNull() || !Ion::Storage::sharedStorage()->hasRecord(m_script)) {
return;
}
Ion::Storage::Record::Data scriptValue = m_script.value();
Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(
m_script,
scriptValue.size - Script::k_importationStatusSize - (strlen(m_script.scriptContent()) + 1)); // TODO optimize number of script fetches
}
}

View File

@@ -17,6 +17,7 @@ class EditorController : public ViewController, public TextAreaDelegate, public
public:
EditorController(MenuController * menuController, App * pythonDelegate);
void setScript(Script script);
void willExitApp();
/* ViewController */
View * view() override { return &m_editorView; }
@@ -33,13 +34,9 @@ public:
VariableBoxController * variableBoxForInputEventHandler(InputEventHandler * textInput) override;
private:
void cleanStorageEmptySpace();
StackViewController * stackController();
void saveScript();
EditorView m_editorView;
/* m_areaBuffer first character is dedicated to the importation status.
* Thereby, we avoid wasteful copy while adding the Script to the storage
* (in order to add the importation status char before the areaBuffer). */
char m_areaBuffer[Ion::Storage::k_storageSize]; // this could be slightly optimize
Script m_script;
MenuController * m_menuController;
};

View File

@@ -1,4 +1,5 @@
#include "editor_view.h"
#include <apps/global_preferences.h>
#include <poincare/integer.h>
#include <escher/app.h>
#include <poincare/preferences.h>
@@ -10,50 +11,47 @@ namespace Code {
EditorView::EditorView(Responder * parentResponder, App * pythonDelegate) :
Responder(parentResponder),
View(),
m_textArea(parentResponder, pythonDelegate, Poincare::Preferences::sharedPreferences()->KDPythonFont()),
m_gutterView(Poincare::Preferences::sharedPreferences()->KDPythonFont())
m_textArea(parentResponder, pythonDelegate, GlobalPreferences::sharedGlobalPreferences()->font()),
m_gutterView(GlobalPreferences::sharedGlobalPreferences()->font())
{
m_textArea.setScrollViewDelegate(this);
}
void EditorView::resetSelection() {
m_textArea.resetSelection();
}
void EditorView::scrollViewDidChangeOffset(ScrollViewDataSource * scrollViewDataSource) {
m_gutterView.setOffset(scrollViewDataSource->offset().y());
}
int EditorView::numberOfSubviews() const {
return 2;
}
View * EditorView::subviewAtIndex(int index) {
View * subviews[] = {&m_textArea, &m_gutterView};
return subviews[index];
if (index == 0) {
return &m_textArea;
}
assert(index == 1);
return &m_gutterView;
}
void EditorView::didBecomeFirstResponder() {
Container::activeApp()->setFirstResponder(&m_textArea);
}
void EditorView::layoutSubviews() {
void EditorView::layoutSubviews(bool force) {
m_gutterView.setOffset(0);
KDCoordinate gutterWidth = m_gutterView.minimalSizeForOptimalDisplay().width();
m_gutterView.setFrame(KDRect(0, 0, gutterWidth, bounds().height()));
m_gutterView.setFrame(KDRect(0, 0, gutterWidth, bounds().height()), force);
m_textArea.setFrame(KDRect(
gutterWidth,
0,
bounds().width()-gutterWidth,
bounds().height()
));
gutterWidth,
0,
bounds().width()-gutterWidth,
bounds().height()),
force);
}
/* EditorView::GutterView */
EditorView::GutterView::GutterView(const KDFont * font) :
View(),
m_offset(0)
{
}
void EditorView::GutterView::drawRect(KDContext * ctx, KDRect rect) const {
KDColor textColor = KDColor::RGB24(0x919EA4);
KDColor backgroundColor = KDColor::RGB24(0xE4E6E7);

View File

@@ -9,6 +9,7 @@ namespace Code {
class EditorView : public Responder, public View, public ScrollViewDelegate {
public:
EditorView(Responder * parentResponder, App * pythonDelegate);
void resetSelection();
void setTextAreaDelegates(InputEventHandlerDelegate * inputEventHandlerDelegate, TextAreaDelegate * delegate) {
m_textArea.setDelegates(inputEventHandlerDelegate, delegate);
}
@@ -24,13 +25,13 @@ public:
void scrollViewDidChangeOffset(ScrollViewDataSource * scrollViewDataSource) override;
void didBecomeFirstResponder() override;
private:
int numberOfSubviews() const override;
int numberOfSubviews() const override { return 2; }
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
void layoutSubviews(bool force = false) override;
class GutterView : public View {
public:
GutterView(const KDFont * font);
GutterView(const KDFont * font) : View(), m_font(font), m_offset(0) {}
void drawRect(KDContext * ctx, KDRect rect) const override;
void setOffset(KDCoordinate offset);
KDSize minimalSizeForOptimalDisplay() const override;

View File

@@ -169,6 +169,10 @@ void MenuController::scriptContentEditionDidFinish() {
reloadConsole();
}
void MenuController::willExitApp() {
m_editorController.willExitApp();
}
int MenuController::numberOfRows() const {
return m_scriptStore->numberOfScripts() + m_shouldDisplayAddScriptRow;
}
@@ -299,24 +303,6 @@ bool MenuController::textFieldShouldFinishEditing(TextField * textField, Ion::Ev
|| event == Ion::Events::Down || event == Ion::Events::Up;
}
bool MenuController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
if (event == Ion::Events::Right
&& textField->isEditing()
&& textField->cursorLocation() == textField->text() + textField->draftTextLength()) {
return true;
}
if (event == Ion::Events::Clear && textField->isEditing()) {
constexpr size_t k_bufferSize = 4;
char buffer[k_bufferSize] = {'.', 0, 0, 0};
assert(k_bufferSize >= 1 + strlen(ScriptStore::k_scriptExtension) + 1);
strlcpy(&buffer[1], ScriptStore::k_scriptExtension, strlen(ScriptStore::k_scriptExtension) + 1);
textField->setText(buffer);
textField->setCursorLocation(textField->text());
return true;
}
return false;
}
bool MenuController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
const char * newName;
static constexpr int bufferSize = Script::k_defaultScriptNameMaxSize + 1 + ScriptStore::k_scriptExtensionLength; //"script99" + "." + "py"

View File

@@ -24,6 +24,7 @@ public:
void reloadConsole();
void openConsoleWithScript(Script script);
void scriptContentEditionDidFinish();
void willExitApp();
/* ViewController */
View * view() override { return &m_selectableTableView; }
@@ -51,7 +52,7 @@ public:
/* TextFieldDelegate */
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override { return false; }
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
bool textFieldDidAbortEditing(TextField * textField) override {
return privateTextFieldDidAbortEditing(textField, true);
@@ -67,7 +68,7 @@ public:
private:
static constexpr int k_maxNumberOfDisplayableScriptCells = 5; // = 240/50
static constexpr int k_parametersColumnWidth = 37;
static constexpr int k_parametersColumnWidth = Metric::EllipsisCellWidth;
static constexpr int AddScriptCellType = 0;
static constexpr int ScriptCellType = 1;
static constexpr int ScriptParameterCellType = 2;

View File

@@ -1,11 +1,13 @@
#include "python_text_area.h"
#include "app.h"
#include <escher/palette.h>
#include <ion/unicode/utf8_helper.h>
#include <python/port/port.h>
extern "C" {
#include "py/nlr.h"
#include "py/lexer.h"
}
#include <python/port/port.h>
#include <stdlib.h>
namespace Code {
@@ -17,6 +19,7 @@ constexpr KDColor KeywordColor = Palette::CodeKeyword;
constexpr KDColor OperatorColor = Palette::CodeOperator;
constexpr KDColor StringColor = Palette::CodeString;
constexpr KDColor BackgroundColor = Palette::CodeBackground;
constexpr KDColor HighlightColor = Palette::CodeBackgroundSelected;
static inline const char * minPointer(const char * x, const char * y) { return x < y ? x : y; }
@@ -39,40 +42,15 @@ static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
return Palette::CodeText;
}
static inline size_t TokenLength(mp_lexer_t * lex) {
if (lex->tok_kind == MP_TOKEN_STRING) {
return lex->vstr.len + 2;
}
if (lex->vstr.len > 0) {
return lex->vstr.len;
}
switch (lex->tok_kind) {
case MP_TOKEN_OP_DBL_STAR:
case MP_TOKEN_OP_DBL_SLASH:
case MP_TOKEN_OP_DBL_LESS:
case MP_TOKEN_OP_DBL_MORE:
case MP_TOKEN_OP_LESS_EQUAL:
case MP_TOKEN_OP_MORE_EQUAL:
case MP_TOKEN_OP_DBL_EQUAL:
case MP_TOKEN_OP_NOT_EQUAL:
case MP_TOKEN_DEL_PLUS_EQUAL:
case MP_TOKEN_DEL_MINUS_EQUAL:
case MP_TOKEN_DEL_STAR_EQUAL:
case MP_TOKEN_DEL_SLASH_EQUAL:
case MP_TOKEN_DEL_PERCENT_EQUAL:
case MP_TOKEN_DEL_AMPERSAND_EQUAL:
case MP_TOKEN_DEL_PIPE_EQUAL:
case MP_TOKEN_DEL_CARET_EQUAL:
case MP_TOKEN_DEL_MINUS_MORE:
return 2;
case MP_TOKEN_DEL_DBL_SLASH_EQUAL:
case MP_TOKEN_DEL_DBL_MORE_EQUAL:
case MP_TOKEN_DEL_DBL_LESS_EQUAL:
case MP_TOKEN_DEL_DBL_STAR_EQUAL:
return 3;
default:
return 1;
static inline size_t TokenLength(mp_lexer_t * lex, const char * tokenPosition) {
/* The lexer stores the beginning of the current token and of the next token,
* so we just use that. */
if (lex->line > 1) {
/* The next token is on the next line, so we cannot just make the difference
* of the columns. */
return UTF8Helper::CodePointSearch(tokenPosition, '\n') - tokenPosition;
}
return lex->column - lex->tok_column;
}
void PythonTextArea::ContentView::loadSyntaxHighlighter() {
@@ -95,7 +73,7 @@ void PythonTextArea::ContentView::clearRect(KDContext * ctx, KDRect rect) const
#define LOG_DRAW(...)
#endif
void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t byteLength, int fromColumn, int toColumn) const {
void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t byteLength, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const {
LOG_DRAW("Drawing \"%.*s\"\n", byteLength, text);
if (!m_pythonDelegate->isPythonUser(this)) {
@@ -108,46 +86,81 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
lineStart,
minPointer(text + byteLength, lineEnd) - lineStart,
StringColor,
BackgroundColor
BackgroundColor,
selectionStart,
selectionEnd,
HighlightColor
);
return;
}
/* We're using the MicroPython lexer to do syntax highlighting on a per-line
* basis. This can work, however the MicroPython lexer won't accept a line
* starting with a whitespace. So we're discarding leading whitespaces
* beforehand. */
const char * firstNonSpace = UTF8Helper::NotCodePointSearch(text, ' ');
if (firstNonSpace != text) {
// Color the discarded leading whitespaces
const char * spacesStart = UTF8Helper::CodePointAtGlyphOffset(text, fromColumn);
drawStringAt(
ctx,
line,
fromColumn,
spacesStart,
minPointer(text + byteLength, firstNonSpace) - spacesStart,
StringColor,
BackgroundColor,
selectionStart,
selectionEnd,
HighlightColor);
}
if (UTF8Helper::CodePointIs(firstNonSpace, UCodePointNull)) {
return;
}
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
/* We're using the MicroPython lexer to do syntax highlighting on a per-line
* basis. This can work, however the MicroPython lexer won't accept a line
* starting with a whitespace. So we're discarding leading whitespaces
* beforehand. */
const char * firstNonSpace = UTF8Helper::NotCodePointSearch(text, ' ');
if (UTF8Helper::CodePointIs(firstNonSpace, UCodePointNull)) {
nlr_pop();
return;
}
mp_lexer_t * lex = mp_lexer_new_from_str_len(0, firstNonSpace, byteLength - (firstNonSpace - text), 0);
LOG_DRAW("Pop token %d\n", lex->tok_kind);
const char * tokenFrom = firstNonSpace;
size_t tokenLength = 0;
const char * tokenEnd = firstNonSpace;
while (lex->tok_kind != MP_TOKEN_NEWLINE && lex->tok_kind != MP_TOKEN_END) {
tokenFrom = firstNonSpace + lex->tok_column - 1;
tokenLength = TokenLength(lex);
if (tokenFrom != tokenEnd) {
// We passed over white spaces, we need to color them
drawStringAt(
ctx,
line,
UTF8Helper::GlyphOffsetAtCodePoint(text, tokenEnd),
tokenEnd,
minPointer(text + byteLength, tokenFrom) - tokenEnd,
StringColor,
BackgroundColor,
selectionStart,
selectionEnd,
HighlightColor);
}
tokenLength = TokenLength(lex, tokenFrom);
tokenEnd = tokenFrom + tokenLength;
LOG_DRAW("Draw \"%.*s\" for token %d\n", tokenLength, tokenFrom, lex->tok_kind);
drawStringAt(ctx, line,
UTF8Helper::GlyphOffsetAtCodePoint(text, tokenFrom),
tokenFrom,
tokenLength,
TokenColor(lex->tok_kind),
BackgroundColor
);
BackgroundColor,
selectionStart,
selectionEnd,
HighlightColor);
mp_lexer_to_next(lex);
LOG_DRAW("Pop token %d\n", lex->tok_kind);
}
tokenFrom += tokenLength;
if (tokenFrom < text + byteLength) {
LOG_DRAW("Draw comment \"%.*s\" from %d\n", byteLength - (tokenFrom - text), firstNonSpace, tokenFrom);
drawStringAt(ctx, line,
@@ -155,7 +168,10 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
tokenFrom,
text + byteLength - tokenFrom,
CommentColor,
BackgroundColor);
BackgroundColor,
selectionStart,
selectionEnd,
HighlightColor);
}
mp_lexer_free(lex);
@@ -163,13 +179,13 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
}
}
KDRect PythonTextArea::ContentView::dirtyRectFromPosition(const char * position, bool lineBreak) const {
KDRect PythonTextArea::ContentView::dirtyRectFromPosition(const char * position, bool includeFollowingLines) const {
/* Mark the whole line as dirty.
* TextArea has a very conservative approach and only dirties the surroundings
* of the current character. That works for plain text, but when doing syntax
* highlighting, you may want to redraw the surroundings as well. For example,
* if editing "def foo" into "df foo", you'll want to redraw "df". */
KDRect baseDirtyRect = TextArea::ContentView::dirtyRectFromPosition(position, lineBreak);
KDRect baseDirtyRect = TextArea::ContentView::dirtyRectFromPosition(position, includeFollowingLines);
return KDRect(
bounds().x(),
baseDirtyRect.y(),

View File

@@ -27,8 +27,8 @@ protected:
void loadSyntaxHighlighter();
void unloadSyntaxHighlighter();
void clearRect(KDContext * ctx, KDRect rect) const override;
void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn) const override;
KDRect dirtyRectFromPosition(const char * position, bool lineBreak) const override;
void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const override;
KDRect dirtyRectFromPosition(const char * position, bool includeFollowingLines) const override;
private:
App * m_pythonDelegate;
};

View File

@@ -48,7 +48,7 @@ const ToolboxMessageTree loopsAndTestsChildren[] = {
const ToolboxMessageTree MathModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportMath, I18n::Message::PythonImportMath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromMath, I18n::Message::PythonImportFromMath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromMath, I18n::Message::PythonImportMath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandMathFunction, I18n::Message::PythonMathFunction, false, I18n::Message::PythonCommandMathFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantE, I18n::Message::PythonConstantE, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantPi, I18n::Message::PythonConstantPi, false),
@@ -94,7 +94,7 @@ const ToolboxMessageTree MathModuleChildren[] = {
const ToolboxMessageTree KandinskyModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportKandinsky, I18n::Message::PythonImportKandinsky, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromKandinsky, I18n::Message::PythonImportFromKandinsky, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromKandinsky, I18n::Message::PythonImportKandinsky, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKandinskyFunction, I18n::Message::PythonKandinskyFunction, false, I18n::Message::PythonCommandKandinskyFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetPixel, I18n::Message::PythonGetPixel),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSetPixel, I18n::Message::PythonSetPixel),
@@ -105,7 +105,7 @@ const ToolboxMessageTree KandinskyModuleChildren[] = {
const ToolboxMessageTree RandomModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportRandom, I18n::Message::PythonImportRandom, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromRandom, I18n::Message::PythonImportFromRandom, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromRandom, I18n::Message::PythonImportRandom, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRandomFunction, I18n::Message::PythonRandomFunction, false, I18n::Message::PythonCommandRandomFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetrandbits, I18n::Message::PythonGetrandbits),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSeed, I18n::Message::PythonSeed),
@@ -118,7 +118,7 @@ const ToolboxMessageTree RandomModuleChildren[] = {
const ToolboxMessageTree CMathModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportCmath, I18n::Message::PythonImportCmath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromCmath, I18n::Message::PythonImportFromCmath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromCmath, I18n::Message::PythonImportCmath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCmathFunction, I18n::Message::PythonCmathFunction, false, I18n::Message::PythonCommandCmathFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantE, I18n::Message::PythonConstantE, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantPi, I18n::Message::PythonConstantPi, false),
@@ -134,7 +134,7 @@ const ToolboxMessageTree CMathModuleChildren[] = {
const ToolboxMessageTree TurtleModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportTurtle, I18n::Message::PythonImportTurtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromTurtle, I18n::Message::PythonImportFromTurtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromTurtle, I18n::Message::PythonImportTurtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTurtleFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandTurtleFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandForward, I18n::Message::PythonTurtleForward),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBackward, I18n::Message::PythonTurtleBackward),
@@ -175,13 +175,76 @@ const ToolboxMessageTree timeModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonTimeCommandMonotonic, I18n::Message::PythonTimeMonotonic, false)
};
const ToolboxMessageTree IonModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportIon, I18n::Message::PythonImportIon, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromIon, I18n::Message::PythonImportIon, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIonFunction, I18n::Message::PythonIonFunction, false, I18n::Message::PythonCommandIonFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIsKeyDown, I18n::Message::PythonIsKeyDown),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyLeft, I18n::Message::PythonKeyLeft, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyUp, I18n::Message::PythonKeyUp, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyDown, I18n::Message::PythonKeyDown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyRight, I18n::Message::PythonKeyRight, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyOk, I18n::Message::PythonKeyOk, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyBack, I18n::Message::PythonKeyBack, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyHome, I18n::Message::PythonKeyHome, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyOnOff, I18n::Message::PythonKeyOnOff, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyShift, I18n::Message::PythonKeyShift, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyAlpha, I18n::Message::PythonKeyAlpha, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyXnt, I18n::Message::PythonKeyXnt, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyVar, I18n::Message::PythonKeyVar, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyToolbox, I18n::Message::PythonKeyToolbox, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyBackspace, I18n::Message::PythonKeyBackspace, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyExp, I18n::Message::PythonKeyExp, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyLn, I18n::Message::PythonKeyLn, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyLog, I18n::Message::PythonKeyLog, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyImaginary, I18n::Message::PythonKeyImaginary, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyComma, I18n::Message::PythonKeyComma, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyPower, I18n::Message::PythonKeyPower, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeySine, I18n::Message::PythonKeySine, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyCosine, I18n::Message::PythonKeyCosine, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyTangent, I18n::Message::PythonKeyTangent, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyPi, I18n::Message::PythonKeyPi, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeySqrt, I18n::Message::PythonKeySqrt, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeySquare, I18n::Message::PythonKeySquare, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeySeven, I18n::Message::PythonKeySeven, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyEight, I18n::Message::PythonKeyEight, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyNine, I18n::Message::PythonKeyNine, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyLeftParenthesis, I18n::Message::PythonKeyLeftParenthesis, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyRightParenthesis, I18n::Message::PythonKeyRightParenthesis, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyFour, I18n::Message::PythonKeyFour, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyFive, I18n::Message::PythonKeyFive, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeySix, I18n::Message::PythonKeySix, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyMultiplication, I18n::Message::PythonKeyMultiplication, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyDivision, I18n::Message::PythonKeyDivision, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyOne, I18n::Message::PythonKeyOne, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyTwo, I18n::Message::PythonKeyTwo, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyThree, I18n::Message::PythonKeyThree, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyPlus, I18n::Message::PythonKeyPlus, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyMinus, I18n::Message::PythonKeyMinus, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyZero, I18n::Message::PythonKeyZero, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyDot, I18n::Message::PythonKeyDot, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyEe, I18n::Message::PythonKeyEe, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyAns, I18n::Message::PythonKeyAns, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKeyExe, I18n::Message::PythonKeyExe, false)
};
const ToolboxMessageTree TimeModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportTime, I18n::Message::PythonImportTime, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromTime, I18n::Message::PythonImportTime, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTimeFunction, I18n::Message::PythonTimeFunction, false, I18n::Message::PythonCommandTimeFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandMonotonic, I18n::Message::PythonMonotonic, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSleep, I18n::Message::PythonSleep)
};
const ToolboxMessageTree modulesChildren[] = {
ToolboxMessageTree::Node(I18n::Message::MathModule, MathModuleChildren),
ToolboxMessageTree::Node(I18n::Message::CmathModule, CMathModuleChildren),
ToolboxMessageTree::Node(I18n::Message::RandomModule, RandomModuleChildren),
ToolboxMessageTree::Node(I18n::Message::TurtleModule, TurtleModuleChildren),
ToolboxMessageTree::Node(I18n::Message::KandinskyModule, KandinskyModuleChildren),
ToolboxMessageTree::Node(I18n::Message::PythonTimeModule, timeModuleChildren)
ToolboxMessageTree::Node(I18n::Message::PythonTimeModule, timeModuleChildren),
ToolboxMessageTree::Node(I18n::Message::IonModule, IonModuleChildren),
ToolboxMessageTree::Node(I18n::Message::TimeModule, TimeModuleChildren)
};
const ToolboxMessageTree catalogChildren[] = {
@@ -197,6 +260,7 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandAbs, I18n::Message::PythonAbs),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandAcos, I18n::Message::PythonAcos),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandAcosh, I18n::Message::PythonAcosh),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandAppend, I18n::Message::PythonAppend, false, I18n::Message::PythonCommandAppendWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandAsin, I18n::Message::PythonAsin),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandAsinh, I18n::Message::PythonAsinh),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandAtan, I18n::Message::PythonAtan),
@@ -210,12 +274,14 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCeil, I18n::Message::PythonCeil),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandChoice, I18n::Message::PythonChoice),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandCircle, I18n::Message::PythonTurtleCircle),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandClear, I18n::Message::PythonClear, false, I18n::Message::PythonCommandClearWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCmathFunction, I18n::Message::PythonCmathFunction, false, I18n::Message::PythonCommandCmathFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandComplex, I18n::Message::PythonComplex),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCopySign, I18n::Message::PythonCopySign),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCos, I18n::Message::PythonCos),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCosh, I18n::Message::PythonCosh),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCount, I18n::Message::PythonCount, false, I18n::Message::PythonCommandCountWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDegrees, I18n::Message::PythonDegrees),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDivMod, I18n::Message::PythonDivMod),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawString, I18n::Message::PythonDrawString),
@@ -232,11 +298,13 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandForward, I18n::Message::PythonTurtleForward),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFmod, I18n::Message::PythonFmod),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFrExp, I18n::Message::PythonFrExp),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromCmath, I18n::Message::PythonImportFromCmath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromKandinsky, I18n::Message::PythonImportFromKandinsky, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromMath, I18n::Message::PythonImportFromMath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromRandom, I18n::Message::PythonImportFromRandom, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromTurtle, I18n::Message::PythonImportFromTurtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromCmath, I18n::Message::PythonImportCmath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromIon, I18n::Message::PythonImportIon, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromKandinsky, I18n::Message::PythonImportKandinsky, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromMath, I18n::Message::PythonImportMath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromRandom, I18n::Message::PythonImportRandom, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromTurtle, I18n::Message::PythonImportTurtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromTime, I18n::Message::PythonImportTime, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGamma, I18n::Message::PythonGamma),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetPixel, I18n::Message::PythonGetPixel),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetrandbits, I18n::Message::PythonGetrandbits),
@@ -247,17 +315,23 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandHex, I18n::Message::PythonHex),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHideturtle, I18n::Message::PythonTurtleHideturtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportCmath, I18n::Message::PythonImportCmath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportIon, I18n::Message::PythonImportIon, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportKandinsky, I18n::Message::PythonImportKandinsky, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportMath, I18n::Message::PythonImportMath, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportRandom, I18n::Message::PythonImportRandom, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportTurtle, I18n::Message::PythonImportTurtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportTime, I18n::Message::PythonImportTime, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIndex, I18n::Message::PythonIndex, false, I18n::Message::PythonCommandIndexWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandInput, I18n::Message::PythonInput),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandInsert, I18n::Message::PythonInsert, false, I18n::Message::PythonCommandInsertWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandInt, I18n::Message::PythonInt),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIonFunction, I18n::Message::PythonIonFunction, false, I18n::Message::PythonCommandIonFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandIsdown, I18n::Message::PythonTurtleIsdown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIsFinite, I18n::Message::PythonIsFinite),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIsInfinite, I18n::Message::PythonIsInfinite),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIsNaN, I18n::Message::PythonIsNaN),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKandinskyFunction, I18n::Message::PythonKandinskyFunction, false, I18n::Message::PythonCommandKandinskyFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandIsKeyDown, I18n::Message::PythonIsKeyDown),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandLdexp, I18n::Message::PythonLdexp),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandLeft, I18n::Message::PythonTurtleLeft),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandLength, I18n::Message::PythonLength),
@@ -269,6 +343,7 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandMax, I18n::Message::PythonMax),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandMin, I18n::Message::PythonMin),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandModf, I18n::Message::PythonModf),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandMonotonic, I18n::Message::PythonMonotonic, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandOct, I18n::Message::PythonOct),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandOrange, I18n::Message::PythonTurtleOrange, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPendown, I18n::Message::PythonTurtlePendown, false),
@@ -278,6 +353,7 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantPi, I18n::Message::PythonConstantPi, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPink, I18n::Message::PythonTurtlePink, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPolar, I18n::Message::PythonPolar),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPop, I18n::Message::PythonPop, false, I18n::Message::PythonCommandPopWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPosition, I18n::Message::PythonTurtlePosition, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPower, I18n::Message::PythonPower),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPrint, I18n::Message::PythonPrint),
@@ -291,7 +367,9 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRangeStop, I18n::Message::PythonRangeStop),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRect, I18n::Message::PythonRect),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRemove, I18n::Message::PythonRemove, false, I18n::Message::PythonCommandRemoveWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandReset, I18n::Message::PythonTurtleReset, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandReverse, I18n::Message::PythonReverse, false, I18n::Message::PythonCommandReverseWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRight, I18n::Message::PythonTurtleRight),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRound, I18n::Message::PythonRound),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandSetheading, I18n::Message::PythonTurtleSetheading),
@@ -300,12 +378,15 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandShowturtle, I18n::Message::PythonTurtleShowturtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSin, I18n::Message::PythonSin),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSinh, I18n::Message::PythonSinh),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSorted, I18n::Message::PythonSorted),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSleep, I18n::Message::PythonSleep),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSort, I18n::Message::PythonSort, false, I18n::Message::PythonCommandSortWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSorted, I18n::Message::PythonSort),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandSpeed, I18n::Message::PythonTurtleSpeed),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSqrt, I18n::Message::PythonSqrt),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSum, I18n::Message::PythonSum),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTan, I18n::Message::PythonTan),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTanh, I18n::Message::PythonTanh),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTimeFunction, I18n::Message::PythonTimeFunction, false, I18n::Message::PythonCommandTimeFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTrunc, I18n::Message::PythonTrunc),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTurtleFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandTurtleFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandUniform, I18n::Message::PythonUniform),
@@ -341,7 +422,7 @@ bool PythonToolbox::handleEvent(Ion::Events::Event event) {
}
if (event.hasText() && strlen(event.text()) == 1 ) {
char c = event.text()[0];
if (UTF8Helper::CodePointIsLetter(c)) {
if (CodePoint(c).isLatinLetter()) {
scrollToLetter(c);
return true;
}
@@ -361,7 +442,7 @@ KDCoordinate PythonToolbox::rowHeight(int j) {
* children of the toolbox, which is the only menu that has special height
* rows. */
const ToolboxMessageTree * messageTree = static_cast<const ToolboxMessageTree *>(m_messageTreeModel->children(j));
return k_font->stringSize(I18n::translate(messageTree->label())).height() + 2*Metric::TableCellLabelTopMargin + (messageTree->text() == I18n::Message::Default ? 0 : Toolbox::rowHeight(j));
return k_font->stringSize(I18n::translate(messageTree->label())).height() + 2*Metric::TableCellVerticalMargin + (messageTree->text() == I18n::Message::Default ? 0 : Toolbox::rowHeight(j));
}
return Toolbox::rowHeight(j);
}
@@ -402,7 +483,7 @@ int PythonToolbox::maxNumberOfDisplayedRows() {
}
void PythonToolbox::scrollToLetter(char letter) {
assert(UTF8Helper::CodePointIsLetter(letter));
assert(CodePoint(letter).isLatinLetter());
/* We look for a child MessageTree that starts with the wanted letter. If we
* do not find one, we scroll to the first child MessageTree that starts with
* a letter higher than the wanted letter. */
@@ -414,7 +495,7 @@ void PythonToolbox::scrollToLetter(char letter) {
index = i;
break;
}
if (index < 0 && l >= lowerLetter && UTF8Helper::CodePointIsLowerCaseLetter(l)) {
if (index < 0 && l >= lowerLetter && CodePoint(l).isLatinSmallLetter()) {
index = i;
}
}

View File

@@ -48,7 +48,7 @@ bool Script::nameCompliant(const char * name) {
* problems with case sensitivity. */
UTF8Decoder decoder(name);
CodePoint c = decoder.nextCodePoint();
if (c == UCodePointNull || !(UTF8Helper::CodePointIsLowerCaseLetter(c) || c == '_' || c == '.')) {
if (c == UCodePointNull || !(c.isLatinSmallLetter() || c == '_' || c == '.')) {
/* The name cannot be empty. Its first letter must be in [a-z_] or the
* extension dot. */
return false;
@@ -57,7 +57,7 @@ bool Script::nameCompliant(const char * name) {
if (c == '.' && strcmp(decoder.stringPosition(), ScriptStore::k_scriptExtension) == 0) {
return true;
}
if (!(UTF8Helper::CodePointIsLowerCaseLetter(c) || c == '_' || UTF8Helper::CodePointIsNumber(c))) {
if (!(c.isLatinSmallLetter() || c == '_' || c.isDecimalDigit())) {
return false;
}
c = decoder.nextCodePoint();
@@ -77,7 +77,7 @@ void Script::toggleImportationStatus() {
setValue(d);
}
const char * Script::readContent() const {
const char * Script::scriptContent() const {
assert(!isNull());
Data d = value();
return (const char *)d.buffer + k_importationStatusSize;

View File

@@ -26,7 +26,7 @@ public:
Script(Ion::Storage::Record r) : Record(r) {}
bool importationStatus() const;
void toggleImportationStatus();
const char * readContent() const;
const char * scriptContent() const;
};
}

View File

@@ -29,12 +29,13 @@ void ScriptNameCell::didBecomeFirstResponder() {
Container::activeApp()->setFirstResponder(&m_textField);
}
void ScriptNameCell::layoutSubviews() {
void ScriptNameCell::layoutSubviews(bool force) {
KDRect cellBounds = bounds();
m_textField.setFrame(KDRect(cellBounds.x() + k_leftMargin,
cellBounds.y(),
cellBounds.width() - k_leftMargin,
cellBounds.height()));
cellBounds.height()),
force);
}
}

View File

@@ -48,7 +48,7 @@ private:
assert(index == 0);
return &m_textField;
}
void layoutSubviews() override;
void layoutSubviews(bool force = false) override;
Shared::TextFieldWithExtension m_textField;
char m_textBody[TextField::maxBufferSize()];

View File

@@ -23,12 +23,12 @@ void ScriptNodeCell::ScriptNodeView::setScriptStore(ScriptStore * scriptStore) {
}
void ScriptNodeCell::ScriptNodeView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->drawString(m_scriptNode->name(), KDPoint(0, Metric::TableCellLabelTopMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
ctx->drawString(m_scriptNode->name(), KDPoint(0, Metric::TableCellVerticalMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
KDSize nameSize = k_font->stringSize(m_scriptNode->name());
if (m_scriptNode->type() == ScriptNode::Type::Function) {
ctx->drawString(ScriptNodeCell::k_parentheses, KDPoint(nameSize.width(), Metric::TableCellLabelTopMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
ctx->drawString(ScriptNodeCell::k_parentheses, KDPoint(nameSize.width(), Metric::TableCellVerticalMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
}
ctx->drawString(m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName(), KDPoint(0, Metric::TableCellLabelTopMargin + nameSize.height() + k_verticalMargin), k_font, Palette::SecondaryText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
ctx->drawString(m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName(), KDPoint(0, Metric::TableCellVerticalMargin + nameSize.height() + k_verticalMargin), k_font, Palette::SecondaryText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
}
KDSize ScriptNodeCell::ScriptNodeView::minimalSizeForOptimalDisplay() const {
@@ -41,7 +41,7 @@ KDSize ScriptNodeCell::ScriptNodeView::minimalSizeForOptimalDisplay() const {
if (m_scriptNode->type() == ScriptNode::Type::Function) {
size3 = k_font->stringSize(ScriptNodeCell::k_parentheses);
}
return KDSize(size1.width() + size3.width() > size2.width() ? size1.width() + size3.width() : size2.width(), Metric::TableCellLabelTopMargin + size1.width() + k_verticalMargin + size2.width());
return KDSize(size1.width() + size3.width() > size2.width() ? size1.width() + size3.width() : size2.width(), Metric::TableCellVerticalMargin + size1.width() + k_verticalMargin + size2.width());
}
ScriptNodeCell::ScriptNodeCell() :

View File

@@ -38,7 +38,7 @@ void ScriptStore::scanScriptsForFunctionsAndVariables(void * context, ScanCallba
// Handle lexer or parser errors with nlr.
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
const char * scriptContent = scriptAtIndex(scriptIndex).readContent();
const char * scriptContent = scriptAtIndex(scriptIndex).scriptContent();
if (scriptContent == nullptr) {
continue;
}
@@ -120,7 +120,7 @@ const char * ScriptStore::contentOfScript(const char * name) {
if (script.isNull()) {
return nullptr;
}
return script.readContent();
return script.scriptContent();
}
Script::ErrorStatus ScriptStore::addScriptFromTemplate(const ScriptTemplate * scriptTemplate) {

View File

@@ -1,6 +1,8 @@
MathModule = "math"
CmathModule = "cmath"
IonModule = "ion"
KandinskyModule = "kandinsky"
MathModule = "math"
TimeModule = "time"
TurtleModule = "turtle"
ForLoopMenu = "For"
IfStatementMenu = "If"

View File

@@ -0,0 +1,27 @@
#ifndef APPS_EXAM_MODE_CONFIGURATION_H
#define APPS_EXAM_MODE_CONFIGURATION_H
#include "global_preferences.h"
#include "settings/settings_message_tree.h"
#include <apps/i18n.h>
namespace ExamModeConfiguration {
// Settings menu
extern const Settings::SettingsMessageTree s_modelExamChildren[2];
int numberOfAvailableExamMode();
GlobalPreferences::ExamMode examModeAtIndex(int index);
I18n::Message examModeActivationMessage(int index);
// Settings pop-up
I18n::Message examModeActivationWarningMessage(GlobalPreferences::ExamMode mode, int line);
// Exam mode behaviour
KDColor examModeColor(GlobalPreferences::ExamMode mode);
bool appIsForbiddenInExamMode(I18n::Message appName, GlobalPreferences::ExamMode mode);
bool exactExpressionsAreForbidden(GlobalPreferences::ExamMode mode);
}
#endif

View File

@@ -0,0 +1,38 @@
#include "exam_mode_configuration.h"
constexpr Settings::SettingsMessageTree ExamModeConfiguration::s_modelExamChildren[] = {Settings::SettingsMessageTree(I18n::Message::ActivateExamMode), Settings::SettingsMessageTree(I18n::Message::Default)};
int ExamModeConfiguration::numberOfAvailableExamMode() {
return 1;
}
GlobalPreferences::ExamMode ExamModeConfiguration::examModeAtIndex(int index) {
return GlobalPreferences::ExamMode::Standard;
}
I18n::Message ExamModeConfiguration::examModeActivationMessage(int index) {
return I18n::Message::ActivateExamMode;
}
I18n::Message ExamModeConfiguration::examModeActivationWarningMessage(GlobalPreferences::ExamMode mode, int line) {
if (mode == GlobalPreferences::ExamMode::Off) {
I18n::Message warnings[] = {I18n::Message::ExitExamMode1, I18n::Message::ExitExamMode2, I18n::Message::Default};
return warnings[line];
}
assert(mode == GlobalPreferences::ExamMode::Standard);
I18n::Message warnings[] = {I18n::Message::ActiveExamModeMessage1, I18n::Message::ActiveExamModeMessage2, I18n::Message::ActiveExamModeMessage3};
return warnings[line];
}
KDColor ExamModeConfiguration::examModeColor(GlobalPreferences::ExamMode mode) {
assert(mode == GlobalPreferences::ExamMode::Standard);
return KDColorRed;
}
bool ExamModeConfiguration::appIsForbiddenInExamMode(I18n::Message appName, GlobalPreferences::ExamMode mode) {
return false;
}
bool ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::ExamMode mode) {
return false;
}

View File

@@ -0,0 +1,53 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
// Caution: Dutch exam mode is subject to a compliance certification by a government agency. Distribution of a non-certified Dutch exam mode is illegal.
#include "exam_mode_configuration.h"
constexpr Settings::SettingsMessageTree ExamModeConfiguration::s_modelExamChildren[2] = {Settings::SettingsMessageTree(I18n::Message::ActivateExamMode), Settings::SettingsMessageTree(I18n::Message::ActivateDutchExamMode)};
int ExamModeConfiguration::numberOfAvailableExamMode() {
if (GlobalPreferences::sharedGlobalPreferences()->language() != I18n::Language::EN || GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) {
return 1;
}
return 2;
}
GlobalPreferences::ExamMode ExamModeConfiguration::examModeAtIndex(int index) {
return index == 0 ? GlobalPreferences::ExamMode::Standard : GlobalPreferences::ExamMode::Dutch;
}
I18n::Message ExamModeConfiguration::examModeActivationMessage(int index) {
return index == 0 ? I18n::Message::ActivateExamMode : I18n::Message::ActivateDutchExamMode;
}
I18n::Message ExamModeConfiguration::examModeActivationWarningMessage(GlobalPreferences::ExamMode mode, int line) {
if (mode == GlobalPreferences::ExamMode::Off) {
I18n::Message warnings[] = {I18n::Message::ExitExamMode1, I18n::Message::ExitExamMode2, I18n::Message::Default};
return warnings[line];
} else if (mode == GlobalPreferences::ExamMode::Standard || mode == GlobalPreferences::ExamMode::NoSym) {
I18n::Message warnings[] = {I18n::Message::ActiveExamModeMessage1, I18n::Message::ActiveExamModeMessage2, I18n::Message::ActiveExamModeMessage3};
return warnings[line];
}
assert(mode == GlobalPreferences::ExamMode::Dutch);
I18n::Message warnings[] = {I18n::Message::ActiveDutchExamModeMessage1, I18n::Message::ActiveDutchExamModeMessage2, I18n::Message::ActiveDutchExamModeMessage3};
return warnings[line];
}
KDColor ExamModeConfiguration::examModeColor(GlobalPreferences::ExamMode mode) {
/* The Dutch exam mode LED is supposed to be orange but we can only make
* blink "pure" colors: with RGB leds on or off (as the PWM is used for
* blinking). The closest "pure" color is Yellow. Moreover, Orange LED is
* already used when the battery is charging. Using yellow, we can assert
* that the yellow LED only means that Dutch exam mode is on and avoid
* confusing states when the battery is charging and states when the Dutch
* exam mode is on. */
return mode == GlobalPreferences::ExamMode::Dutch ? KDColorYellow : KDColorRed;
}
bool ExamModeConfiguration::appIsForbiddenInExamMode(I18n::Message appName, GlobalPreferences::ExamMode mode) {
return appName == I18n::Message::CodeApp && mode == GlobalPreferences::ExamMode::Dutch;
}
bool ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::ExamMode mode) {
return mode == GlobalPreferences::ExamMode::Dutch;
}

View File

@@ -1,5 +1,6 @@
#include "exam_pop_up_controller.h"
#include "apps_container.h"
#include "exam_mode_configuration.h"
#include <apps/i18n.h>
#include "global_preferences.h"
#include <assert.h>
@@ -68,10 +69,14 @@ ExamPopUpController::ContentView::ContentView(Responder * parentResponder) :
return true;
}, parentResponder), KDFont::SmallFont),
m_warningTextView(KDFont::SmallFont, I18n::Message::Warning, 0.5, 0.5, KDColorWhite, KDColorBlack),
m_messageTextView1(KDFont::SmallFont, I18n::Message::Default, 0.5, 0.5, KDColorWhite, KDColorBlack),
m_messageTextView2(KDFont::SmallFont, I18n::Message::Default, 0.5, 0.5, KDColorWhite, KDColorBlack),
m_messageTextView3(KDFont::SmallFont, I18n::Message::Default, 0.5, 0.5, KDColorWhite, KDColorBlack)
m_messageTextViews{}
{
for (int i = 0; i < k_maxNumberOfLines; i++) {
m_messageTextViews[i].setFont(KDFont::SmallFont);
m_messageTextViews[i].setAlignment(0.5f, 0.5f);
m_messageTextViews[i].setBackgroundColor(KDColorBlack);
m_messageTextViews[i].setTextColor(KDColorWhite);
}
}
void ExamPopUpController::ContentView::drawRect(KDContext * ctx, KDRect rect) const {
@@ -92,19 +97,8 @@ int ExamPopUpController::ContentView::selectedButton() {
}
void ExamPopUpController::ContentView::setMessagesForExamMode(GlobalPreferences::ExamMode mode) {
if (mode == GlobalPreferences::ExamMode::Off) {
m_messageTextView1.setMessage(I18n::Message::ExitExamMode1);
m_messageTextView2.setMessage(I18n::Message::ExitExamMode2);
m_messageTextView3.setMessage(I18n::Message::Default);
} else if (mode == GlobalPreferences::ExamMode::Standard || mode == GlobalPreferences::ExamMode::NoSym) {
m_messageTextView1.setMessage(I18n::Message::ActiveExamModeMessage1);
m_messageTextView2.setMessage(I18n::Message::ActiveExamModeMessage2);
m_messageTextView3.setMessage(I18n::Message::ActiveExamModeMessage3);
} else {
assert(mode == GlobalPreferences::ExamMode::Dutch);
m_messageTextView1.setMessage(I18n::Message::ActiveDutchExamModeMessage1);
m_messageTextView2.setMessage(I18n::Message::ActiveDutchExamModeMessage2);
m_messageTextView3.setMessage(I18n::Message::ActiveDutchExamModeMessage3);
for (int i = 0; i < k_maxNumberOfLines; i++) {
m_messageTextViews[i].setMessage(ExamModeConfiguration::examModeActivationWarningMessage(mode, i));
}
}
@@ -116,30 +110,23 @@ View * ExamPopUpController::ContentView::subviewAtIndex(int index) {
switch (index) {
case 0:
return &m_warningTextView;
case 1:
return &m_messageTextView1;
case 2:
return &m_messageTextView2;
case 3:
return &m_messageTextView3;
case 4:
return &m_cancelButton;
case 5:
return &m_okButton;
default:
assert(false);
return nullptr;
return &m_messageTextViews[index-1];
}
}
void ExamPopUpController::ContentView::layoutSubviews() {
void ExamPopUpController::ContentView::layoutSubviews(bool force) {
KDCoordinate height = bounds().height();
KDCoordinate width = bounds().width();
KDCoordinate textHeight = KDFont::SmallFont->glyphSize().height();
m_warningTextView.setFrame(KDRect(0, k_topMargin, width, textHeight));
m_messageTextView1.setFrame(KDRect(0, k_topMargin+k_paragraphHeight+textHeight, width, textHeight));
m_messageTextView2.setFrame(KDRect(0, k_topMargin+k_paragraphHeight+2*textHeight, width, textHeight));
m_messageTextView3.setFrame(KDRect(0, k_topMargin+k_paragraphHeight+3*textHeight, width, textHeight));
m_cancelButton.setFrame(KDRect(k_buttonMargin, height-k_buttonMargin-k_buttonHeight, (width-3*k_buttonMargin)/2, k_buttonHeight));
m_okButton.setFrame(KDRect(2*k_buttonMargin+(width-3*k_buttonMargin)/2, height-k_buttonMargin-k_buttonHeight, (width-3*k_buttonMargin)/2, k_buttonHeight));
m_warningTextView.setFrame(KDRect(0, k_topMargin, width, textHeight), force);
for (int i = 0; i < k_maxNumberOfLines; i++) {
m_messageTextViews[i].setFrame(KDRect(0, k_topMargin+k_paragraphHeight+(i+1)*textHeight, width, textHeight), force);
}
m_cancelButton.setFrame(KDRect(k_buttonMargin, height-k_buttonMargin-k_buttonHeight, (width-3*k_buttonMargin)/2, k_buttonHeight), force);
m_okButton.setFrame(KDRect(2*k_buttonMargin+(width-3*k_buttonMargin)/2, height-k_buttonMargin-k_buttonHeight, (width-3*k_buttonMargin)/2, k_buttonHeight), force);
}

View File

@@ -37,13 +37,12 @@ private:
constexpr static KDCoordinate k_paragraphHeight = 20;
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
void layoutSubviews(bool force = false) override;
HighContrastButton m_cancelButton;
HighContrastButton m_okButton;
MessageTextView m_warningTextView;
MessageTextView m_messageTextView1;
MessageTextView m_messageTextView2;
MessageTextView m_messageTextView3;
constexpr static int k_maxNumberOfLines = 3;
MessageTextView m_messageTextViews[k_maxNumberOfLines];
};
ContentView m_contentView;
GlobalPreferences::ExamMode m_targetExamMode;

View File

@@ -25,6 +25,8 @@ public:
void setShowPopUp(bool showPopUp) { m_showPopUp = showPopUp; }
int brightnessLevel() const { return m_brightnessLevel; }
void setBrightnessLevel(int brightnessLevel);
const KDFont * font() const { return m_font; }
void setFont(const KDFont * font) { m_font = font; }
constexpr static int NumberOfBrightnessStates = 15;
private:
GlobalPreferences() :
@@ -32,7 +34,8 @@ private:
m_examMode(ExamMode::Unknown),
m_tempExamMode(ExamMode::Standard),
m_showPopUp(true),
m_brightnessLevel(Ion::Backlight::MaxBrightness) {}
m_brightnessLevel(Ion::Backlight::MaxBrightness),
m_font(KDFont::LargeFont) {}
I18n::Language m_language;
static_assert((int8_t)GlobalPreferences::ExamMode::Off == 0, "GlobalPreferences::isInExamMode() is not right");
static_assert((int8_t)GlobalPreferences::ExamMode::Unknown < 0, "GlobalPreferences::isInExamMode() is not right");
@@ -40,6 +43,7 @@ private:
mutable ExamMode m_tempExamMode;
bool m_showPopUp;
int m_brightnessLevel;
const KDFont * m_font;
};
#endif

View File

@@ -13,7 +13,7 @@ IntervalX = "Intervalo x"
FunctionDomain = "Intervalo do gráfico"
FunctionColor = "Cor da função"
NoFunction = "Nenhuma função"
NoActivatedFunction = "Sem função ativada"
NoActivatedFunction = "Sem função activada"
PlotOptions = "Opções da curva"
Compute = "Calcular"
Zeros = "Raízes"

View File

@@ -57,20 +57,16 @@ ContinuousFunctionStore * CalculationGraphController::functionStore() const {
return App::app()->functionStore();
}
bool CalculationGraphController::handleLeftRightEvent(Ion::Events::Event event) {
if (!m_isActive) {
return false;
}
return SimpleInteractiveCurveViewController::handleLeftRightEvent(event);
}
bool CalculationGraphController::handleEnter() {
StackViewController * stack = static_cast<StackViewController *>(parentResponder());
stack->pop();
return true;
}
bool CalculationGraphController::moveCursorHorizontally(int direction) {
bool CalculationGraphController::moveCursorHorizontally(int direction, bool fast) {
if (!m_isActive) {
return false;
}
Coordinate2D<double> newPointOfInterest = computeNewPointOfInterestFromAbscissa(m_cursor->x(), direction);
if (std::isnan(newPointOfInterest.x1())) {
return false;

View File

@@ -31,9 +31,8 @@ protected:
bool m_isActive;
private:
bool handleZoom(Ion::Events::Event event) override { return false; }
bool handleLeftRightEvent(Ion::Events::Event event) override;
bool handleEnter() override;
bool moveCursorHorizontally(int direction) override;
bool moveCursorHorizontally(int direction, bool fast = false) override;
Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override { return m_graphRange; }
Shared::CurveView * curveView() override { return m_graphView; }
};

View File

@@ -93,7 +93,6 @@ void GraphController::interestingRanges(float * xm, float * xM, float * ym, floa
/* In practice, a step smaller than a pixel's width is needed for sampling
* the values of a function. Otherwise some relevant extremal values may be
* missed. */
const float step = const_cast<GraphController *>(this)->curveView()->pixelWidth() / 2;
for (int i = 0; i < functionsCount; i++) {
ExpiringPointer<ContinuousFunction> f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
if (f->plotType() != ContinuousFunction::PlotType::Cartesian) {
@@ -103,8 +102,9 @@ void GraphController::interestingRanges(float * xm, float * xM, float * ym, floa
* y-range for even functions (y = 1/x). */
assert(!std::isnan(f->tMin()));
assert(!std::isnan(f->tMax()));
double tMin = maxFloat(f->tMin(), resultxMin);
double tMax = minFloat(f->tMax(), resultxMax);
const double tMin = maxFloat(f->tMin(), resultxMin);
const double tMax = minFloat(f->tMax(), resultxMax);
const double step = (tMax - tMin) / (2.0 * (m_view.bounds().width() - 1.0));
interestingFunctionRange(f, tMin, tMax, step, &resultxMin, &resultxMax, &resultyMin, &resultyMax);
}
if (resultyMin > resultyMax) {
@@ -169,9 +169,9 @@ void GraphController::reloadBannerView() {
reloadDerivativeInBannerViewForCursorOnFunction(m_cursor, record);
}
bool GraphController::moveCursorHorizontally(int direction) {
bool GraphController::moveCursorHorizontally(int direction, bool fast) {
Ion::Storage::Record record = functionStore()->activeRecordAtIndex(indexFunctionSelectedByCursor());
return privateMoveCursorHorizontally(m_cursor, direction, m_graphRange, k_numberOfCursorStepsInGradUnit, record);
return privateMoveCursorHorizontally(m_cursor, direction, m_graphRange, k_numberOfCursorStepsInGradUnit, record, fast);
}
int GraphController::nextCurveIndexVertically(bool goingUp, int currentSelectedCurve, Poincare::Context * context) const {

View File

@@ -27,7 +27,7 @@ private:
void selectFunctionWithCursor(int functionIndex) override;
BannerView * bannerView() override { return &m_bannerView; }
void reloadBannerView() override;
bool moveCursorHorizontally(int direction) override;
bool moveCursorHorizontally(int direction, bool fast = false) override;
int nextCurveIndexVertically(bool goingUp, int currentSelectedCurve, Poincare::Context * context) const override;
double defaultCursorT(Ion::Storage::Record record) override;
Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override { return m_graphRange; }

View File

@@ -12,7 +12,7 @@ namespace Graph {
static inline double minDouble(double x, double y) { return x < y ? x : y; }
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCursor * cursor, int direction, Shared::InteractiveCurveViewRange * range, int numberOfStepsInGradUnit, Ion::Storage::Record record) {
bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCursor * cursor, int direction, Shared::InteractiveCurveViewRange * range, int numberOfStepsInGradUnit, Ion::Storage::Record record, bool fast) {
ExpiringPointer<ContinuousFunction> function = App::app()->functionStore()->modelForRecord(record);
double tCursorPosition = cursor->t();
double t = tCursorPosition;
@@ -28,13 +28,12 @@ bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCurso
}
function = App::app()->functionStore()->modelForRecord(record); // Reload the expiring pointer
double dir = (direction > 0 ? 1.0 : -1.0);
ContinuousFunction::PlotType type = function->plotType();
if (type == ContinuousFunction::PlotType::Cartesian) {
t += dir * range->xGridUnit()/numberOfStepsInGradUnit;
} else {
assert(type == ContinuousFunction::PlotType::Polar || type == ContinuousFunction::PlotType::Parametric);
t += dir * (tMax-tMin)/k_definitionDomainDivisor;
double step = function->plotType() == ContinuousFunction::PlotType::Cartesian ? range->xGridUnit()/numberOfStepsInGradUnit : (tMax-tMin)/k_definitionDomainDivisor;
if (fast) {
// TODO Navigate quicker the longer the user presses? (slow start)
step *= 5.0;
}
t += dir * step;
t = maxDouble(tMin, minDouble(tMax, t));
Coordinate2D<double> xy = function->evaluateXYAtParameter(t, App::app()->localContext());
cursor->moveTo(t, xy.x1(), xy.x2());

View File

@@ -11,7 +11,7 @@ class App;
class GraphControllerHelper {
protected:
bool privateMoveCursorHorizontally(Shared::CurveViewCursor * cursor, int direction, Shared::InteractiveCurveViewRange * range, int numberOfStepsInGradUnit, Ion::Storage::Record record);
bool privateMoveCursorHorizontally(Shared::CurveViewCursor * cursor, int direction, Shared::InteractiveCurveViewRange * range, int numberOfStepsInGradUnit, Ion::Storage::Record record, bool fast = false);
void reloadDerivativeInBannerViewForCursorOnFunction(Shared::CurveViewCursor * cursor, Ion::Storage::Record record);
virtual BannerView * bannerView() = 0;
private:

View File

@@ -29,6 +29,13 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const {
Ion::Storage::Record record = functionStore->activeRecordAtIndex(i);
ExpiringPointer<ContinuousFunction> f = functionStore->modelForRecord(record);;
Shared::ContinuousFunction::PlotType type = f->plotType();
Poincare::Expression e = f->expressionReduced(context());
if (e.isUndefined() || (
type == Shared::ContinuousFunction::PlotType::Parametric &&
e.childAtIndex(0).isUndefined() &&
e.childAtIndex(1).isUndefined())) {
continue;
}
float tmin = f->tMin();
float tmax = f->tMax();
/* The step is a fraction of tmax-tmin. We will evaluate the function at
@@ -50,7 +57,7 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const {
ContinuousFunction * f = (ContinuousFunction *)model;
Poincare::Context * c = (Poincare::Context *)context;
return f->evaluateXYAtParameter(t, c);
}, f.operator->(), context(), f->color(), record == m_selectedRecord, m_highlightedStart, m_highlightedEnd);
}, f.operator->(), context(), f->color(), true, record == m_selectedRecord, m_highlightedStart, m_highlightedEnd);
/* Draw tangent */
if (m_tangent && record == m_selectedRecord) {
float tangentParameter[2];

View File

@@ -89,7 +89,7 @@ void TangentGraphController::reloadBannerView() {
m_bannerView->reload();
}
bool TangentGraphController::moveCursorHorizontally(int direction) {
bool TangentGraphController::moveCursorHorizontally(int direction, bool fast) {
return privateMoveCursorHorizontally(m_cursor, direction, m_graphRange, k_numberOfCursorStepsInGradUnit, m_record);
}

Some files were not shown because too many files have changed in this diff Show More