mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
Fixed some conflicts
This commit is contained in:
4
Makefile
4
Makefile
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 \
|
||||
)
|
||||
|
||||
|
||||
98
apps/calculation/additional_outputs/complex_graph_cell.cpp
Normal file
98
apps/calculation/additional_outputs/complex_graph_cell.cpp
Normal 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));
|
||||
}, ¶meters, &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);
|
||||
}
|
||||
|
||||
}
|
||||
37
apps/calculation/additional_outputs/complex_graph_cell.h
Normal file
37
apps/calculation/additional_outputs/complex_graph_cell.h
Normal 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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
43
apps/calculation/additional_outputs/complex_model.cpp
Normal file
43
apps/calculation/additional_outputs/complex_model.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
32
apps/calculation/additional_outputs/complex_model.h
Normal file
32
apps/calculation/additional_outputs/complex_model.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
16
apps/calculation/additional_outputs/illustration_cell.cpp
Normal file
16
apps/calculation/additional_outputs/illustration_cell.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
23
apps/calculation/additional_outputs/illustration_cell.h
Normal file
23
apps/calculation/additional_outputs/illustration_cell.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
47
apps/calculation/additional_outputs/list_controller.cpp
Normal file
47
apps/calculation/additional_outputs/list_controller.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
41
apps/calculation/additional_outputs/list_controller.h
Normal file
41
apps/calculation/additional_outputs/list_controller.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
11
apps/calculation/additional_outputs/trigonometry_model.cpp
Normal file
11
apps/calculation/additional_outputs/trigonometry_model.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "trigonometry_model.h"
|
||||
|
||||
namespace Calculation {
|
||||
|
||||
TrigonometryModel::TrigonometryModel() :
|
||||
Shared::CurveViewRange(),
|
||||
m_angle(NAN)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
40
apps/calculation/additional_outputs/trigonometry_model.h
Normal file
40
apps/calculation/additional_outputs/trigonometry_model.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
CalculApp = "Berechnung"
|
||||
CalculAppCapital = "BERECHNUNG"
|
||||
AdditionalResults = "????"
|
||||
DecimalBase = "????"
|
||||
HexadecimalBase = "????"
|
||||
BinaryBase = "????"
|
||||
PrimeFactors = "????"
|
||||
MixedFraction = "????"
|
||||
EuclideanDivision = "????"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
CalculApp = "Cálculo"
|
||||
CalculAppCapital = "CÁLCULO"
|
||||
AdditionalResults = "????"
|
||||
DecimalBase = "????"
|
||||
HexadecimalBase = "????"
|
||||
BinaryBase = "????"
|
||||
PrimeFactors = "????"
|
||||
MixedFraction = "????"
|
||||
EuclideanDivision = "????"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
CalculApp = "Cálculo"
|
||||
CalculAppCapital = "CÁLCULO"
|
||||
AdditionalResults = "????"
|
||||
DecimalBase = "????"
|
||||
HexadecimalBase = "????"
|
||||
BinaryBase = "????"
|
||||
PrimeFactors = "????"
|
||||
MixedFraction = "????"
|
||||
EuclideanDivision = "????"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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...) */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()];
|
||||
|
||||
@@ -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() :
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
MathModule = "math"
|
||||
CmathModule = "cmath"
|
||||
IonModule = "ion"
|
||||
KandinskyModule = "kandinsky"
|
||||
MathModule = "math"
|
||||
TimeModule = "time"
|
||||
TurtleModule = "turtle"
|
||||
ForLoopMenu = "For"
|
||||
IfStatementMenu = "If"
|
||||
|
||||
27
apps/exam_mode_configuration.h
Normal file
27
apps/exam_mode_configuration.h
Normal 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
|
||||
|
||||
38
apps/exam_mode_configuration_non_official.cpp
Normal file
38
apps/exam_mode_configuration_non_official.cpp
Normal 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;
|
||||
}
|
||||
53
apps/exam_mode_configuration_official.cpp
Normal file
53
apps/exam_mode_configuration_official.cpp
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user