mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[Update] Upstream
This commit is contained in:
2
.github/workflows/ci-workflow.yml
vendored
2
.github/workflows/ci-workflow.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
steps:
|
||||
- uses: numworks/setup-emscripten@v2
|
||||
with:
|
||||
sdk: latest-fastcomp
|
||||
sdk: 1.39.16-fastcomp
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -78,7 +78,13 @@ KDCoordinate IllustratedListController::rowHeight(int j) {
|
||||
return 0;
|
||||
}
|
||||
Shared::ExpiringPointer<Calculation> calculation = m_calculationStore.calculationAtIndex(calculationIndex);
|
||||
return calculation->height(App::app()->localContext(), true, true) + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness;
|
||||
constexpr bool expanded = true;
|
||||
KDCoordinate result = calculation->memoizedHeight(expanded);
|
||||
if (result < 0) {
|
||||
result = ScrollableThreeExpressionsCell::Height(calculation.pointer());
|
||||
calculation->setMemoizedHeight(expanded, result);
|
||||
}
|
||||
return result + Metric::CellSeparatorThickness;
|
||||
}
|
||||
|
||||
int IllustratedListController::typeAtLocation(int i, int j) {
|
||||
|
||||
@@ -65,6 +65,18 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
|
||||
layoutSubviews();
|
||||
}
|
||||
|
||||
KDCoordinate ScrollableThreeExpressionsCell::Height(Calculation * calculation) {
|
||||
ScrollableThreeExpressionsCell cell;
|
||||
cell.setCalculation(calculation);
|
||||
KDRect leftFrame = KDRectZero;
|
||||
KDRect centerFrame = KDRectZero;
|
||||
KDRect approximateSignFrame = KDRectZero;
|
||||
KDRect rightFrame = KDRectZero;
|
||||
cell.subviewFrames(&leftFrame, ¢erFrame, &approximateSignFrame, &rightFrame);
|
||||
KDRect unionedFrame = leftFrame.unionedWith(centerFrame).unionedWith(rightFrame);
|
||||
return unionedFrame.height() + 2 * ScrollableThreeExpressionsView::k_margin;
|
||||
}
|
||||
|
||||
void ScrollableThreeExpressionsCell::didBecomeFirstResponder() {
|
||||
reinitSelection();
|
||||
Container::activeApp()->setFirstResponder(&m_view);
|
||||
|
||||
@@ -10,12 +10,16 @@ namespace Calculation {
|
||||
|
||||
class ScrollableThreeExpressionsView : public Shared::AbstractScrollableMultipleExpressionsView {
|
||||
public:
|
||||
static constexpr KDCoordinate k_margin = Metric::CommonSmallMargin;
|
||||
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
|
||||
setMargins(k_margin, k_margin, k_margin, k_margin); // Left Right margins are already added by TableCell
|
||||
setBackgroundColor(KDColorWhite);
|
||||
}
|
||||
void resetMemoization();
|
||||
void setCalculation(Calculation * calculation);
|
||||
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
|
||||
return m_contentCell.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
|
||||
}
|
||||
private:
|
||||
class ContentCell : public Shared::AbstractScrollableMultipleExpressionsView::ContentCell {
|
||||
public:
|
||||
@@ -28,12 +32,13 @@ private:
|
||||
};
|
||||
|
||||
ContentCell * contentCell() override { return &m_contentCell; };
|
||||
const ContentCell * constContentCell() const override { return &m_contentCell; };
|
||||
const ContentCell * constContentCell() const override { return &m_contentCell; };
|
||||
ContentCell m_contentCell;
|
||||
};
|
||||
|
||||
class ScrollableThreeExpressionsCell : public TableCell, public Responder {
|
||||
public:
|
||||
static KDCoordinate Height(Calculation * calculation);
|
||||
ScrollableThreeExpressionsCell() :
|
||||
Responder(nullptr),
|
||||
m_view(this) {}
|
||||
@@ -58,6 +63,9 @@ public:
|
||||
void setSelectedSubviewPosition(ScrollableThreeExpressionsView::SubviewPosition subviewPosition) { m_view.setSelectedSubviewPosition(subviewPosition); }
|
||||
|
||||
void reinitSelection();
|
||||
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
|
||||
return m_view.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
|
||||
}
|
||||
private:
|
||||
// Remove label margin added by TableCell because they're already handled by ScrollableThreeExpressionsView
|
||||
KDCoordinate labelMargin() const override { return 0; }
|
||||
|
||||
@@ -4,6 +4,6 @@ AdditionalResults = "Weitere Ergebnisse"
|
||||
DecimalBase = "Dezimal"
|
||||
HexadecimalBase = "Hexadezimal"
|
||||
BinaryBase = "Binär"
|
||||
PrimeFactors = "Primfaktor"
|
||||
MixedFraction = "Gemischte Fraktion"
|
||||
EuclideanDivision = "Euklidische Division"
|
||||
PrimeFactors = "Primfaktoren"
|
||||
MixedFraction = "Gemischte Zahl"
|
||||
EuclideanDivision = "Division mit Rest"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "calculation.h"
|
||||
#include "../shared/poincare_helpers.h"
|
||||
#include "../shared/scrollable_multiple_expressions_view.h"
|
||||
#include "../global_preferences.h"
|
||||
#include "../exam_mode_configuration.h"
|
||||
#include <poincare/exception_checkpoint.h>
|
||||
@@ -124,123 +125,12 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul
|
||||
}
|
||||
}
|
||||
|
||||
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 inputWidth = inputLayout.layoutSize().width();
|
||||
float singleMargin = 2 * Metric::CommonSmallMargin;
|
||||
float doubleMargin = 4 * Metric::CommonSmallMargin;
|
||||
bool singleLine = false;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayOutput(context) == DisplayOutput::ExactOnly) {
|
||||
KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
|
||||
KDCoordinate exactOutputWidth = exactLayout.layoutSize().width();
|
||||
singleLine = exactOutputWidth + inputWidth < maxWidth - 40;
|
||||
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
|
||||
KDCoordinate exactOutputBaseline = exactLayout.baseline();
|
||||
result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin;
|
||||
} else {
|
||||
if (allExpressionsInline) {
|
||||
KDCoordinate exactOutputBaseline = exactLayout.baseline();
|
||||
result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline);
|
||||
} else {
|
||||
result = inputHeight + exactOutputHeight + doubleMargin;
|
||||
}
|
||||
}
|
||||
void Calculation::setMemoizedHeight(bool expanded, KDCoordinate height) {
|
||||
if (expanded) {
|
||||
m_expandedHeight = height;
|
||||
} 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();
|
||||
KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width();
|
||||
singleLine = approximateOutputWidth + inputWidth < maxWidth - 40;
|
||||
if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) {
|
||||
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
|
||||
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
|
||||
result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin;
|
||||
} else {
|
||||
if (allExpressionsInline) {
|
||||
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
|
||||
result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline);
|
||||
} else {
|
||||
result = inputHeight + approximateOutputHeight + doubleMargin;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded));
|
||||
KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
|
||||
KDCoordinate exactOutputBaseline = exactLayout.baseline();
|
||||
KDCoordinate exactOutputWidth = exactLayout.layoutSize().width();
|
||||
KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width();
|
||||
singleLine = exactOutputWidth + approximateOutputWidth + inputWidth < maxWidth - 70;
|
||||
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
|
||||
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) {
|
||||
result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin;
|
||||
} else {
|
||||
if (allExpressionsInline) {
|
||||
result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline));
|
||||
} else {
|
||||
KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) + std::max(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline);
|
||||
result = inputHeight + outputHeight + doubleMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_height = height;
|
||||
}
|
||||
|
||||
/* For all display outputs except ExactAndApproximateToggle, the selected
|
||||
* height and the usual height are identical. We update both heights in
|
||||
* theses cases. */
|
||||
if (displayOutput(context) != DisplayOutput::ExactAndApproximateToggle) {
|
||||
m_height = result;
|
||||
m_expandedHeight = result;
|
||||
} else {
|
||||
if (expanded) {
|
||||
m_expandedHeight = result;
|
||||
} else {
|
||||
m_height = result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
|
||||
|
||||
@@ -84,7 +84,8 @@ public:
|
||||
Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout);
|
||||
|
||||
// Memoization of height
|
||||
KDCoordinate height(Poincare::Context * context, bool expanded = false, bool allExpressionsInline = false);
|
||||
KDCoordinate memoizedHeight(bool expanded) { return expanded ? m_expandedHeight : m_height; }
|
||||
void setMemoizedHeight(bool expanded, KDCoordinate height);
|
||||
|
||||
// Displayed output
|
||||
DisplayOutput displayOutput(Poincare::Context * context);
|
||||
|
||||
@@ -7,7 +7,10 @@ namespace Calculation {
|
||||
|
||||
class ExpressionField : public ::ExpressionField {
|
||||
public:
|
||||
using ::ExpressionField::ExpressionField;
|
||||
ExpressionField(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandler, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
|
||||
::ExpressionField(parentResponder, inputEventHandler, textFieldDelegate, layoutFieldDelegate) {
|
||||
setLayoutInsertionCursorEvent(Ion::Events::Up);
|
||||
}
|
||||
protected:
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
};
|
||||
|
||||
@@ -166,13 +166,17 @@ void HistoryController::tableViewDidChangeSelection(SelectableTableView * t, int
|
||||
}
|
||||
if (previousSelectedCellY == -1) {
|
||||
setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
|
||||
} else if (selectedRow() < previousSelectedCellY) {
|
||||
setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
|
||||
} else if (selectedRow() > previousSelectedCellY) {
|
||||
setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
|
||||
} else if (selectedRow() == -1) {
|
||||
setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
|
||||
} else {
|
||||
HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell());
|
||||
SubviewType nextSelectedSubviewType = selectedSubviewType();
|
||||
if (!selectedCell->displaysSingleLine()) {
|
||||
nextSelectedSubviewType = previousSelectedCellY < selectedRow() ? SubviewType::Input : SubviewType::Output;
|
||||
}
|
||||
setSelectedSubviewType(nextSelectedSubviewType, false, previousSelectedCellX, previousSelectedCellY);
|
||||
}
|
||||
// The selectedCell may change during setSelectedSubviewType
|
||||
HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell());
|
||||
if (selectedCell == nullptr) {
|
||||
return;
|
||||
@@ -208,7 +212,13 @@ KDCoordinate HistoryController::rowHeight(int j) {
|
||||
return 0;
|
||||
}
|
||||
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(j);
|
||||
return calculation->height(App::app()->localContext(), j == selectedRow() && selectedSubviewType() == SubviewType::Output);
|
||||
bool expanded = j == selectedRow() && selectedSubviewType() == SubviewType::Output;
|
||||
KDCoordinate result = calculation->memoizedHeight(expanded);
|
||||
if (result < 0) {
|
||||
result = HistoryViewCell::Height(calculation.pointer(), expanded);
|
||||
calculation->setMemoizedHeight(expanded, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int HistoryController::typeAtLocation(int i, int j) {
|
||||
|
||||
@@ -11,9 +11,6 @@ namespace Calculation {
|
||||
|
||||
/* HistoryViewCellDataSource */
|
||||
|
||||
HistoryViewCellDataSource::HistoryViewCellDataSource() :
|
||||
m_selectedSubviewType(SubviewType::Output) {}
|
||||
|
||||
void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedCellX, int previousSelectedCellY) {
|
||||
HistoryViewCell * selectedCell = nullptr;
|
||||
HistoryViewCell * previouslySelectedCell = nullptr;
|
||||
@@ -37,19 +34,26 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType,
|
||||
|
||||
/* HistoryViewCell */
|
||||
|
||||
HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
|
||||
Responder(parentResponder),
|
||||
m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
|
||||
m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
|
||||
m_calculationExpanded(false),
|
||||
m_inputView(this, Metric::CommonLargeMargin, Metric::CommonSmallMargin),
|
||||
m_scrollableOutputView(this)
|
||||
{
|
||||
m_calculationCRC32 = 0;
|
||||
KDCoordinate HistoryViewCell::Height(Calculation * calculation, bool expanded) {
|
||||
HistoryViewCell cell(nullptr);
|
||||
cell.setCalculation(calculation, expanded);
|
||||
KDRect ellipsisFrame = KDRectZero;
|
||||
KDRect inputFrame = KDRectZero;
|
||||
KDRect outputFrame = KDRectZero;
|
||||
cell.computeSubviewFrames(Ion::Display::Width, KDCOORDINATE_MAX, &ellipsisFrame, &inputFrame, &outputFrame);
|
||||
return k_margin + inputFrame.unionedWith(outputFrame).height() + k_margin;
|
||||
}
|
||||
|
||||
Shared::ScrollableTwoExpressionsView * HistoryViewCell::outputView() {
|
||||
return &m_scrollableOutputView;
|
||||
HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
|
||||
Responder(parentResponder),
|
||||
m_calculationCRC32(0),
|
||||
m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
|
||||
m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
|
||||
m_inputView(this, k_inputViewHorizontalMargin, k_inputOutputViewsVerticalMargin),
|
||||
m_scrollableOutputView(this),
|
||||
m_calculationExpanded(false),
|
||||
m_calculationSingleLine(false)
|
||||
{
|
||||
}
|
||||
|
||||
void HistoryViewCell::setEven(bool even) {
|
||||
@@ -137,15 +141,6 @@ void HistoryViewCell::cellDidSelectSubview(HistoryViewCellDataSource::SubviewTyp
|
||||
reloadScroll();
|
||||
}
|
||||
|
||||
KDColor HistoryViewCell::backgroundColor() const {
|
||||
KDColor background = m_even ? Palette::CalculationBackgroundEven : Palette::CalculationBackgroundOdd;
|
||||
return background;
|
||||
}
|
||||
|
||||
int HistoryViewCell::numberOfSubviews() const {
|
||||
return 2 + displayedEllipsis();
|
||||
}
|
||||
|
||||
View * HistoryViewCell::subviewAtIndex(int index) {
|
||||
/* 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
|
||||
@@ -169,29 +164,69 @@ View * HistoryViewCell::subviewAtIndex(int index) {
|
||||
return views[index];
|
||||
}
|
||||
|
||||
bool HistoryViewCell::ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth) {
|
||||
// k_margin is the separation between the input and output.
|
||||
return (inputViewWidth + k_margin + outputViewWidth) < Ion::Display::Width - Metric::EllipsisCellWidth;
|
||||
}
|
||||
|
||||
void HistoryViewCell::layoutSubviews(bool force) {
|
||||
KDCoordinate maxFrameWidth = bounds().width();
|
||||
if (displayedEllipsis()) {
|
||||
m_ellipsis.setFrame(KDRect(maxFrameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, bounds().height()), force);
|
||||
maxFrameWidth -= Metric::EllipsisCellWidth;
|
||||
} else {
|
||||
m_ellipsis.setFrame(KDRectZero, force); // Required to mark previous rect as dirty
|
||||
KDRect frameBounds = bounds();
|
||||
if (bounds().width() <= 0 || bounds().height() <= 0) {
|
||||
// TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews
|
||||
return;
|
||||
}
|
||||
KDRect ellipsisFrame = KDRectZero;
|
||||
KDRect inputFrame = KDRectZero;
|
||||
KDRect outputFrame = KDRectZero;
|
||||
computeSubviewFrames(frameBounds.width(), frameBounds.height(), &ellipsisFrame, &inputFrame, &outputFrame);
|
||||
|
||||
m_ellipsis.setFrame(ellipsisFrame, force); // Required even if ellipsisFrame is KDRectZero, to mark previous rect as dirty
|
||||
m_inputView.setFrame(inputFrame,force);
|
||||
m_scrollableOutputView.setFrame(outputFrame, force);
|
||||
}
|
||||
|
||||
void HistoryViewCell::computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame) {
|
||||
assert(ellipsisFrame != nullptr && inputFrame != nullptr && outputFrame != nullptr);
|
||||
|
||||
if (displayedEllipsis()) {
|
||||
*ellipsisFrame = KDRect(frameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, frameHeight);
|
||||
frameWidth -= Metric::EllipsisCellWidth;
|
||||
} else {
|
||||
*ellipsisFrame = KDRectZero;
|
||||
}
|
||||
|
||||
KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay();
|
||||
m_inputView.setFrame(KDRect(
|
||||
0, 0,
|
||||
std::min(maxFrameWidth, inputSize.width()),
|
||||
inputSize.height()),
|
||||
force);
|
||||
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
|
||||
int singleLine = outputSize.width() + inputSize.width() < bounds().width() - 6;
|
||||
int outputHeight = (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) ? (std::max(0, inputSize.height() - outputSize.height()) / 2) + std::max(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height();
|
||||
m_scrollableOutputView.setFrame(KDRect(
|
||||
std::max(0, maxFrameWidth - outputSize.width()),
|
||||
outputHeight,
|
||||
std::min(maxFrameWidth, outputSize.width()),
|
||||
outputSize.height()),
|
||||
force);
|
||||
|
||||
/* To compute if the calculation is on a single line, use the expanded width
|
||||
* if there is both an exact and an approximate layout. */
|
||||
m_calculationSingleLine = ViewsCanBeSingleLine(inputSize.width(), m_scrollableOutputView.minimalSizeForOptimalDisplayFullSize().width());
|
||||
|
||||
KDCoordinate inputY = k_margin;
|
||||
KDCoordinate outputY = k_margin;
|
||||
if (m_calculationSingleLine) {
|
||||
KDCoordinate inputBaseline = m_inputView.layout().baseline();
|
||||
KDCoordinate outputBaseline = m_scrollableOutputView.baseline();
|
||||
KDCoordinate baselineDifference = outputBaseline - inputBaseline;
|
||||
if (baselineDifference > 0) {
|
||||
inputY += baselineDifference;
|
||||
} else {
|
||||
outputY += -baselineDifference;
|
||||
}
|
||||
} else {
|
||||
outputY += inputSize.height();
|
||||
}
|
||||
|
||||
*inputFrame = KDRect(
|
||||
0,
|
||||
inputY,
|
||||
std::min(frameWidth, inputSize.width()),
|
||||
inputSize.height());
|
||||
*outputFrame = KDRect(
|
||||
std::max(0, frameWidth - outputSize.width()),
|
||||
outputY,
|
||||
std::min(frameWidth, outputSize.width()),
|
||||
outputSize.height());
|
||||
}
|
||||
|
||||
void HistoryViewCell::resetMemoization() {
|
||||
@@ -285,31 +320,41 @@ void HistoryViewCell::didBecomeFirstResponder() {
|
||||
}
|
||||
|
||||
bool HistoryViewCell::handleEvent(Ion::Events::Event event) {
|
||||
assert(m_dataSource);
|
||||
assert(m_dataSource != nullptr);
|
||||
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;
|
||||
assert(type != HistoryViewCellDataSource::SubviewType::None);
|
||||
HistoryViewCellDataSource::SubviewType otherSubviewType = HistoryViewCellDataSource::SubviewType::None;
|
||||
if (m_calculationSingleLine) {
|
||||
static_assert(
|
||||
static_cast<int>(HistoryViewCellDataSource::SubviewType::None) == 0
|
||||
&& static_cast<int>(HistoryViewCellDataSource::SubviewType::Input) == 1
|
||||
&& static_cast<int>(HistoryViewCellDataSource::SubviewType::Output) == 2
|
||||
&& static_cast<int>(HistoryViewCellDataSource::SubviewType::Ellipsis) == 3,
|
||||
"The array types is not well-formed anymore");
|
||||
HistoryViewCellDataSource::SubviewType types[] = {
|
||||
HistoryViewCellDataSource::SubviewType::None,
|
||||
HistoryViewCellDataSource::SubviewType::Input,
|
||||
HistoryViewCellDataSource::SubviewType::Output,
|
||||
displayedEllipsis() ? HistoryViewCellDataSource::SubviewType::Ellipsis : HistoryViewCellDataSource::SubviewType::None,
|
||||
HistoryViewCellDataSource::SubviewType::None,
|
||||
};
|
||||
if (event == Ion::Events::Right || event == Ion::Events::Left) {
|
||||
otherSubviewType = types[static_cast<int>(type) + (event == Ion::Events::Right ? 1 : -1)];
|
||||
}
|
||||
m_dataSource->setSelectedSubviewType(otherSubviewType, true);
|
||||
return true;
|
||||
} else if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input)
|
||||
|| (event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis))
|
||||
{
|
||||
otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
|
||||
} else if (event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) {
|
||||
otherSubviewType = HistoryViewCellDataSource::SubviewType::Input;
|
||||
} else if (event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) {
|
||||
otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryViewCell::displayedEllipsis() const {
|
||||
return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None;
|
||||
if (otherSubviewType == HistoryViewCellDataSource::SubviewType::None) {
|
||||
return false;
|
||||
}
|
||||
m_dataSource->setSelectedSubviewType(otherSubviewType, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,14 +12,14 @@ class HistoryViewCell;
|
||||
class HistoryViewCellDataSource {
|
||||
public:
|
||||
enum class SubviewType {
|
||||
None,
|
||||
Input,
|
||||
Output,
|
||||
Ellipsis
|
||||
None = 0,
|
||||
Input = 1,
|
||||
Output = 2,
|
||||
Ellipsis = 3
|
||||
};
|
||||
HistoryViewCellDataSource();
|
||||
HistoryViewCellDataSource() : m_selectedSubviewType(SubviewType::Output) {}
|
||||
void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1);
|
||||
SubviewType selectedSubviewType() { return m_selectedSubviewType; }
|
||||
SubviewType selectedSubviewType() const { 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
|
||||
@@ -31,39 +31,51 @@ private:
|
||||
|
||||
class HistoryViewCell : public ::EvenOddCell, public Responder {
|
||||
public:
|
||||
constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin;
|
||||
constexpr static KDCoordinate k_inputOutputViewsVerticalMargin = k_margin;
|
||||
constexpr static KDCoordinate k_inputViewHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
|
||||
static KDCoordinate Height(Calculation * calculation, bool expanded);
|
||||
HistoryViewCell(Responder * parentResponder = nullptr);
|
||||
static bool ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth);
|
||||
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; }
|
||||
bool displaysSingleLine() const {
|
||||
return m_calculationSingleLine;
|
||||
}
|
||||
Responder * responder() override {
|
||||
return this;
|
||||
}
|
||||
Poincare::Layout layout() const override;
|
||||
KDColor backgroundColor() const override;
|
||||
KDColor backgroundColor() const override { return m_even ? KDColorWhite : Palette::WallScreen; }
|
||||
void resetMemoization();
|
||||
void setCalculation(Calculation * calculation, bool expanded);
|
||||
int numberOfSubviews() const override;
|
||||
int numberOfSubviews() const override { return 2 + displayedEllipsis(); }
|
||||
View * subviewAtIndex(int index) override;
|
||||
void layoutSubviews(bool force = false) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
Shared::ScrollableTwoExpressionsView * outputView();
|
||||
Shared::ScrollableTwoExpressionsView * outputView() { return &m_scrollableOutputView; }
|
||||
Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; }
|
||||
private:
|
||||
constexpr static KDCoordinate k_resultWidth = 80;
|
||||
void computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame);
|
||||
void reloadScroll();
|
||||
void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType);
|
||||
bool displayedEllipsis() const;
|
||||
bool displayedEllipsis() const {
|
||||
return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None;
|
||||
}
|
||||
uint32_t m_calculationCRC32;
|
||||
Calculation::DisplayOutput m_calculationDisplayOutput;
|
||||
Calculation::AdditionalInformationType m_calculationAdditionInformation;
|
||||
bool m_calculationExpanded;
|
||||
ScrollableExpressionView m_inputView;
|
||||
Shared::ScrollableTwoExpressionsView m_scrollableOutputView;
|
||||
EvenOddCellWithEllipsis m_ellipsis;
|
||||
HistoryViewCellDataSource * m_dataSource;
|
||||
bool m_calculationExpanded;
|
||||
bool m_calculationSingleLine;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ PythonTurtleBlue = "Blaue Farbe"
|
||||
PythonTurtleBrown = "Braune Farbe"
|
||||
PythonTurtleCircle = "Circle of radius r pixels"
|
||||
PythonTurtleColor = "Stiftfarbe setzen"
|
||||
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
|
||||
PythonTurtleForward = "Move forward by x pixels"
|
||||
PythonTurtleFunction = "turtle module function prefix"
|
||||
PythonTurtleGoto = "Move to (x,y) coordinates"
|
||||
|
||||
@@ -167,6 +167,7 @@ PythonTurtleBlue = "Blue color"
|
||||
PythonTurtleBrown = "Brown color"
|
||||
PythonTurtleCircle = "Circle of radius r pixels"
|
||||
PythonTurtleColor = "Set the pen color"
|
||||
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
|
||||
PythonTurtleForward = "Move forward by x pixels"
|
||||
PythonTurtleFunction = "turtle module function prefix"
|
||||
PythonTurtleGoto = "Move to (x,y) coordinates"
|
||||
|
||||
@@ -167,6 +167,7 @@ PythonTurtleBlue = "Blue color"
|
||||
PythonTurtleBrown = "Brown color"
|
||||
PythonTurtleCircle = "Circle of radius r pixels"
|
||||
PythonTurtleColor = "Set the pen color"
|
||||
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
|
||||
PythonTurtleForward = "Move forward by x pixels"
|
||||
PythonTurtleFunction = "turtle module function prefix"
|
||||
PythonTurtleGoto = "Move to (x,y) coordinates"
|
||||
|
||||
@@ -167,6 +167,7 @@ PythonTurtleBlue = "Couleur bleue"
|
||||
PythonTurtleBrown = "Couleur marron"
|
||||
PythonTurtleCircle = "Cercle de rayon r pixels"
|
||||
PythonTurtleColor = "Modifie la couleur du tracé"
|
||||
PythonTurtleColorMode = "Met le mode de couleur à 1.0 ou 255"
|
||||
PythonTurtleForward = "Avance de x pixels"
|
||||
PythonTurtleFunction = "Préfixe fonction du module turtle"
|
||||
PythonTurtleGoto = "Va au point de coordonnées (x,y)"
|
||||
|
||||
@@ -167,6 +167,7 @@ PythonTurtleBlue = "Kék szín"
|
||||
PythonTurtleBrown = "Barna szín"
|
||||
PythonTurtleCircle = "r pixel sugarú kör"
|
||||
PythonTurtleColor = "Állítsa be az toll színét"
|
||||
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
|
||||
PythonTurtleForward = "Ugrás x pixelrel"
|
||||
PythonTurtleFunction = "teknös modul funkció elötag"
|
||||
PythonTurtleGoto = "Mozgatás (x, y) koordinátákra"
|
||||
|
||||
@@ -167,6 +167,7 @@ PythonTurtleBlue = "Blue color"
|
||||
PythonTurtleBrown = "Brown color"
|
||||
PythonTurtleCircle = "Circle of radius r pixels"
|
||||
PythonTurtleColor = "Set the pen color"
|
||||
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
|
||||
PythonTurtleForward = "Move forward by x pixels"
|
||||
PythonTurtleFunction = "turtle module function prefix"
|
||||
PythonTurtleGoto = "Move to (x,y) coordinates"
|
||||
|
||||
@@ -208,6 +208,7 @@ PythonTurtleCommandBrown = "'brown'"
|
||||
PythonTurtleCommandCircle = "circle(r)"
|
||||
PythonTurtleCommandColor = "color('c')/color(r,g,b)"
|
||||
PythonTurtleCommandColorWithoutArg = "color(\x11)"
|
||||
PythonTurtleCommandColorMode = "colormode(x)"
|
||||
PythonTurtleCommandForward = "forward(x)"
|
||||
PythonTurtleCommandGoto = "goto(x,y)"
|
||||
PythonTurtleCommandGreen = "'green'"
|
||||
|
||||
@@ -145,6 +145,7 @@ const ToolboxMessageTree TurtleModuleChildren[] = {
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandShowturtle, I18n::Message::PythonTurtleShowturtle, false),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHideturtle, I18n::Message::PythonTurtleHideturtle, false),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColor, I18n::Message::PythonTurtleColor, false, I18n::Message::PythonTurtleCommandColorWithoutArg),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlue, I18n::Message::PythonTurtleBlue, false),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGreen, I18n::Message::PythonTurtleGreen, false),
|
||||
@@ -285,6 +286,7 @@ const ToolboxMessageTree catalogChildren[] = {
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandCircle, I18n::Message::PythonTurtleCircle),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCmathFunction, I18n::Message::PythonCmathFunction, false, I18n::Message::PythonCommandCmathFunctionWithoutArg),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandComplex, I18n::Message::PythonComplex),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCopySign, I18n::Message::PythonCopySign),
|
||||
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCos, I18n::Message::PythonCos),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Apps = "Anwendungen"
|
||||
AppsCapital = "STAY HOME(GA)"
|
||||
ForbidenAppInExamMode1 = "Diese Anwendung ist im"
|
||||
ForbidenAppInExamMode2 = "Prüfungsmodus verboten"
|
||||
ForbidenAppInExamMode2 = "Prüfungsmodus nicht erlaubt."
|
||||
|
||||
@@ -3,12 +3,12 @@ ProbaAppCapital = "WAHRSCHEINLICHKEIT"
|
||||
ChooseDistribution = "Wählen Sie eine Verteilung"
|
||||
Binomial = "Binomial"
|
||||
Geometric = "Geometrische"
|
||||
Uniforme = "Uniform"
|
||||
Uniforme = "Gleichverteilung"
|
||||
Normal = "Normal"
|
||||
ChiSquared = "Chi-Quadrat"
|
||||
UniformDistribution = "Uniformverteilung"
|
||||
ExponentialDistribution = "Exponentialverteilung"
|
||||
GeometricDistribution = "Geometrischeverteilung"
|
||||
GeometricDistribution = "Geometrische Verteilung"
|
||||
PoissonDistribution = "Poisson-Verteilung"
|
||||
ChiSquaredDistribution = "Chi-Quadrat-Verteilung"
|
||||
StudentDistribution = "Student-Verteilung"
|
||||
|
||||
@@ -15,6 +15,7 @@ app_regression_test_src += $(addprefix apps/regression/model/,\
|
||||
logistic_model.cpp \
|
||||
model.cpp \
|
||||
power_model.cpp \
|
||||
proportional_model.cpp \
|
||||
quadratic_model.cpp \
|
||||
quartic_model.cpp \
|
||||
trigonometric_model.cpp \
|
||||
|
||||
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Wert in diesem Fenster nicht erreicht"
|
||||
NumberOfDots = "Punktanzahl"
|
||||
Covariance = "Kovarianz"
|
||||
Linear = "Lineare"
|
||||
Proportional = "Proportional"
|
||||
Quadratic = "Quadratische"
|
||||
Cubic = "Kubische"
|
||||
Quartic = "Biquadratische"
|
||||
|
||||
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Value not reached in this window"
|
||||
NumberOfDots = "Number of points"
|
||||
Covariance = "Covariance"
|
||||
Linear = "Linear"
|
||||
Proportional = "Proportional"
|
||||
Quadratic = "Quadratic"
|
||||
Cubic = "Cubic"
|
||||
Quartic = "Quartic"
|
||||
|
||||
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valor no alcanzado en esta ventana"
|
||||
NumberOfDots = "Número de puntos"
|
||||
Covariance = "Covarianza"
|
||||
Linear = "Lineal"
|
||||
Proportional = "Proporcional"
|
||||
Quadratic = "Cuadrática"
|
||||
Cubic = "Cúbica"
|
||||
Quartic = "Cuártica"
|
||||
|
||||
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valeur non atteinte dans cette fenêtre"
|
||||
NumberOfDots = "Nombre de points"
|
||||
Covariance = "Covariance"
|
||||
Linear = "Linéaire"
|
||||
Proportional = "Proportionnelle"
|
||||
Quadratic = "Quadratique"
|
||||
Cubic = "Cubique"
|
||||
Quartic = "Quartique"
|
||||
|
||||
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Az ablakban az érték még nem volt elérve"
|
||||
NumberOfDots = "Pontok száma"
|
||||
Covariance = "Kovariancia"
|
||||
Linear = "Lineáris"
|
||||
Proportional = "Proportional"
|
||||
Quadratic = "Másodfokú"
|
||||
Cubic = "Kocka"
|
||||
Quartic = "Kvartikus"
|
||||
|
||||
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valor não alcançado nesta janela"
|
||||
NumberOfDots = "Número de pontos"
|
||||
Covariance = "Covariancia"
|
||||
Linear = "Linear"
|
||||
Proportional = "Proporcional"
|
||||
Quadratic = "Quadrática"
|
||||
Cubic = "Cúbica"
|
||||
Quartic = "Quarto grau"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
ProportionalRegressionFormula = " y=a·x "
|
||||
QuadraticRegressionFormula = " y=a·x^2+b·x+c "
|
||||
CubicRegressionFormula = " y=a·x^3+b·x^2+c·x+d "
|
||||
QuarticRegressionFormula = " y=a·x^4+b·x^3+c·x^2+d·x+e "
|
||||
|
||||
@@ -188,13 +188,14 @@ void GraphController::reloadBannerView() {
|
||||
}
|
||||
|
||||
if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear) {
|
||||
int index = model->numberOfCoefficients();
|
||||
// Set "r=..."
|
||||
numberOfChar = 0;
|
||||
legend = " r=";
|
||||
double r = m_store->correlationCoefficient(*m_selectedSeriesIndex);
|
||||
numberOfChar += strlcpy(buffer, legend, bufferSize);
|
||||
numberOfChar += PoincareHelpers::ConvertFloatToText<double>(r, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits);
|
||||
m_bannerView.subTextAtIndex(2)->setText(buffer);
|
||||
m_bannerView.subTextAtIndex(0+index)->setText(buffer);
|
||||
|
||||
// Set "r2=..."
|
||||
numberOfChar = 0;
|
||||
@@ -202,11 +203,11 @@ void GraphController::reloadBannerView() {
|
||||
double r2 = m_store->squaredCorrelationCoefficient(*m_selectedSeriesIndex);
|
||||
numberOfChar += strlcpy(buffer, legend, bufferSize);
|
||||
numberOfChar += PoincareHelpers::ConvertFloatToText<double>(r2, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits);
|
||||
m_bannerView.subTextAtIndex(3)->setText(buffer);
|
||||
m_bannerView.subTextAtIndex(1+index)->setText(buffer);
|
||||
|
||||
// Clean the last subview
|
||||
buffer[0] = 0;
|
||||
m_bannerView.subTextAtIndex(4)->setText(buffer);
|
||||
m_bannerView.subTextAtIndex(2+index)->setText(buffer);
|
||||
|
||||
} else {
|
||||
// Empty all non used subviews
|
||||
|
||||
@@ -55,24 +55,21 @@ double CubicModel::evaluate(double * modelCoefficients, double x) const {
|
||||
}
|
||||
|
||||
double CubicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: x^3
|
||||
return x*x*x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: x^2
|
||||
return x*x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 2) {
|
||||
// Derivate: x
|
||||
return x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 3) {
|
||||
// Derivate: 1
|
||||
return 1;
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
switch (derivateCoefficientIndex) {
|
||||
case 0:
|
||||
// Derivate with respect to a: x^3
|
||||
return x*x*x;
|
||||
case 1:
|
||||
// Derivate with respect to b: x^2
|
||||
return x*x;
|
||||
case 2:
|
||||
// Derivate with respect to c: x
|
||||
return x;
|
||||
default:
|
||||
// Derivate with respect to d: 1
|
||||
assert(derivateCoefficientIndex == 3);
|
||||
return 1.0;
|
||||
};
|
||||
}
|
||||
|
||||
Expression CubicModel::expression(double * modelCoefficients) {
|
||||
|
||||
@@ -86,18 +86,15 @@ void ExponentialModel::fit(Store * store, int series, double * modelCoefficients
|
||||
}
|
||||
|
||||
double ExponentialModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
double a = modelCoefficients[0];
|
||||
double b = modelCoefficients[1];
|
||||
const double b = modelCoefficients[1];
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: exp(b*x)
|
||||
// Derivate with respect to a: exp(b*x)
|
||||
return exp(b*x);
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: a*x*exp(b*x)
|
||||
return a*x*exp(b*x);
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
assert(derivateCoefficientIndex == 1);
|
||||
// Derivate with respect to b: a*x*exp(b*x)
|
||||
double a = modelCoefficients[0];
|
||||
return a*x*exp(b*x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,15 +38,12 @@ void LinearModel::fit(Store * store, int series, double * modelCoefficients, Poi
|
||||
|
||||
double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: x
|
||||
// Derivate with respect to a: x
|
||||
return x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: 1;
|
||||
return 1;
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
assert(derivateCoefficientIndex == 1);
|
||||
// Derivate with respect to b: 1
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,16 +33,13 @@ double LogarithmicModel::levelSet(double * modelCoefficients, double xMin, doubl
|
||||
|
||||
double LogarithmicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: ln(x)
|
||||
assert(x >0);
|
||||
// Derivate with respect to a: ln(x)
|
||||
assert(x > 0);
|
||||
return log(x);
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: 1
|
||||
return 1;
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
assert(derivateCoefficientIndex == 1);
|
||||
// Derivate with respect to b: 1
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
bool LogarithmicModel::dataSuitableForFit(Store * store, int series) const {
|
||||
|
||||
@@ -61,21 +61,18 @@ double LogisticModel::partialDerivate(double * modelCoefficients, int derivateCo
|
||||
double a = modelCoefficients[0];
|
||||
double b = modelCoefficients[1];
|
||||
double c = modelCoefficients[2];
|
||||
double denominator = 1.0+a*exp(-b*x);
|
||||
double denominator = 1.0 + a * exp(-b * x);
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: exp(-b*x)*(-1 * c/(1.0+a*exp(-b*x))^2)
|
||||
return -exp(-b*x) * c/(denominator * denominator);
|
||||
// Derivate with respect to a: exp(-b*x)*(-1 * c/(1.0+a*exp(-b*x))^2)
|
||||
return -exp(-b * x) * c / (denominator * denominator);
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
|
||||
return x*a*exp(-b*x)*c/(denominator * denominator);
|
||||
// Derivate with respect to b: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
|
||||
return x * a * exp(-b * x) * c / (denominator * denominator);
|
||||
}
|
||||
if (derivateCoefficientIndex == 2) {
|
||||
// Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
|
||||
return 1.0/denominator;
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
assert(derivateCoefficientIndex == 2);
|
||||
// Derivate with respect to c: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
|
||||
return 1.0 / denominator;
|
||||
}
|
||||
|
||||
void LogisticModel::specializedInitCoefficientsForFit(double * modelCoefficients, double defaultValue, Store * store, int series) const {
|
||||
@@ -86,7 +83,7 @@ void LogisticModel::specializedInitCoefficientsForFit(double * modelCoefficients
|
||||
* and c. Twice the standard vertical deviation is a rough estimate of c
|
||||
* that is "close enough" to c to seed the coefficient, without being too
|
||||
* dependent on outliers.*/
|
||||
modelCoefficients[2] = 2.0*store->standardDeviationOfColumn(series, 1);
|
||||
modelCoefficients[2] = 2.0 * store->standardDeviationOfColumn(series, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,17 +14,18 @@ class Store;
|
||||
class Model {
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
Linear = 0,
|
||||
Quadratic = 1,
|
||||
Cubic = 2,
|
||||
Quartic = 3,
|
||||
Logarithmic = 4,
|
||||
Exponential = 5,
|
||||
Power = 6,
|
||||
Trigonometric = 7,
|
||||
Logistic = 8
|
||||
Linear = 0,
|
||||
Proportional = 1,
|
||||
Quadratic = 2,
|
||||
Cubic = 3,
|
||||
Quartic = 4,
|
||||
Logarithmic = 5,
|
||||
Exponential = 6,
|
||||
Power = 7,
|
||||
Trigonometric = 8,
|
||||
Logistic = 9
|
||||
};
|
||||
static constexpr int k_numberOfModels = 9;
|
||||
static constexpr int k_numberOfModels = 10;
|
||||
static constexpr int k_maxNumberOfCoefficients = 5; // This has to verify: k_maxNumberOfCoefficients < Matrix::k_maxNumberOfCoefficients
|
||||
virtual ~Model() = default;
|
||||
virtual Poincare::Layout layout() = 0;
|
||||
|
||||
@@ -44,19 +44,16 @@ double PowerModel::partialDerivate(double * modelCoefficients, int derivateCoeff
|
||||
double a = modelCoefficients[0];
|
||||
double b = modelCoefficients[1];
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: pow(x,b)
|
||||
// Derivate with respect to a: pow(x,b)
|
||||
return pow(x,b);
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
assert(x >= 0);
|
||||
/* We assume all xi are positive.
|
||||
* For x = 0, a*pow(x,b) = 0, the partial derivate along b is 0
|
||||
* For x > 0, a*pow(x,b) = a*exp(b*ln(x)), the partial derivate along b is
|
||||
* ln(x)*a*pow(x,b) */
|
||||
return x == 0 ? 0 : log(x)*a*pow(x, b);
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
assert(derivateCoefficientIndex == 1);
|
||||
assert(x >= 0);
|
||||
/* We assume all xi are positive.
|
||||
* For x = 0, a*pow(x,b) = 0, the partial derivate with respect to b is 0
|
||||
* For x > 0, a*pow(x,b) = a*exp(b*ln(x)), the partial derivate with respect
|
||||
* to b is ln(x)*a*pow(x,b) */
|
||||
return x == 0.0 ? 0.0 : log(x) * a * pow(x, b);
|
||||
}
|
||||
|
||||
void PowerModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) {
|
||||
|
||||
40
apps/regression/model/proportional_model.cpp
Normal file
40
apps/regression/model/proportional_model.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "proportional_model.h"
|
||||
#include "../store.h"
|
||||
#include <poincare/layout_helper.h>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Regression {
|
||||
|
||||
Layout ProportionalModel::layout() {
|
||||
if (m_layout.isUninitialized()) {
|
||||
const char * s = "a·X";
|
||||
m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont);
|
||||
}
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
double ProportionalModel::evaluate(double * modelCoefficients, double x) const {
|
||||
return modelCoefficients[0] * x;
|
||||
}
|
||||
|
||||
double ProportionalModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) {
|
||||
const double a = modelCoefficients[0];
|
||||
if (a == 0.0) {
|
||||
return NAN;
|
||||
}
|
||||
return y/a;
|
||||
}
|
||||
|
||||
void ProportionalModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) {
|
||||
modelCoefficients[0] = store->slope(series);
|
||||
}
|
||||
|
||||
double ProportionalModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
assert(derivateCoefficientIndex == 0);
|
||||
// Derivate: x
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
24
apps/regression/model/proportional_model.h
Normal file
24
apps/regression/model/proportional_model.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef REGRESSION_PROPORTIONAL_MODEL_H
|
||||
#define REGRESSION_PROPORTIONAL_MODEL_H
|
||||
|
||||
#include "model.h"
|
||||
|
||||
namespace Regression {
|
||||
|
||||
class ProportionalModel : public Model {
|
||||
public:
|
||||
using Model::Model;
|
||||
Poincare::Layout layout() override;
|
||||
I18n::Message formulaMessage() const override { return I18n::Message::ProportionalRegressionFormula; }
|
||||
double evaluate(double * modelCoefficients, double x) const override;
|
||||
double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override;
|
||||
void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override;
|
||||
double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override;
|
||||
int numberOfCoefficients() const override { return 1; }
|
||||
int bannerLinesCount() const override { return 2; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -47,19 +47,16 @@ double QuadraticModel::evaluate(double * modelCoefficients, double x) const {
|
||||
|
||||
double QuadraticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: x^2
|
||||
// Derivate with respect to a: x^2
|
||||
return x*x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: x
|
||||
// Derivate with respect to b: x
|
||||
return x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 2) {
|
||||
// Derivate: 1
|
||||
return 1;
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
assert(derivateCoefficientIndex == 2);
|
||||
// Derivate with respect to c: 1
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
Expression QuadraticModel::expression(double * modelCoefficients) {
|
||||
|
||||
@@ -64,28 +64,24 @@ double QuarticModel::evaluate(double * modelCoefficients, double x) const {
|
||||
}
|
||||
|
||||
double QuarticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: x^4
|
||||
return x*x*x*x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: x^3
|
||||
return x*x*x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 2) {
|
||||
// Derivate: x^2
|
||||
return x*x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 3) {
|
||||
// Derivate: x
|
||||
return x;
|
||||
}
|
||||
if (derivateCoefficientIndex == 4) {
|
||||
// Derivate: 1
|
||||
return 1;
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
switch (derivateCoefficientIndex) {
|
||||
case 0:
|
||||
// Derivate with respect to a: x^4
|
||||
return x*x*x*x;
|
||||
case 1:
|
||||
// Derivate with respect to b: x^3
|
||||
return x*x*x;
|
||||
case 2:
|
||||
// Derivate with respect to c: x^2
|
||||
return x*x;
|
||||
case 3:
|
||||
// Derivate with respect to d: x
|
||||
return x;
|
||||
default:
|
||||
assert(derivateCoefficientIndex == 4);
|
||||
// Derivate with respect to e: 1
|
||||
return 1.0;
|
||||
};
|
||||
}
|
||||
|
||||
Expression QuarticModel::expression(double * modelCoefficients) {
|
||||
|
||||
@@ -46,28 +46,27 @@ double TrigonometricModel::evaluate(double * modelCoefficients, double x) const
|
||||
}
|
||||
|
||||
double TrigonometricModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
|
||||
if (derivateCoefficientIndex == 3) {
|
||||
// Derivate with respect to d: 1
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
double a = modelCoefficients[0];
|
||||
double b = modelCoefficients[1];
|
||||
double c = modelCoefficients[2];
|
||||
double radianX = x * toRadians(Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
|
||||
if (derivateCoefficientIndex == 0) {
|
||||
// Derivate: sin(b*x+c)
|
||||
return sin(b*radianX+c);
|
||||
// Derivate with respect to a: sin(b*x+c)
|
||||
return sin(b * radianX + c);
|
||||
}
|
||||
if (derivateCoefficientIndex == 1) {
|
||||
// Derivate: x*a*cos(b*x+c);
|
||||
return radianX*a*cos(b*radianX+c);
|
||||
// Derivate with respect to b: x*a*cos(b*x+c);
|
||||
return radianX * a * cos(b * radianX + c);
|
||||
}
|
||||
if (derivateCoefficientIndex == 2) {
|
||||
// Derivate: a*cos(b*x+c)
|
||||
return a*cos(b*radianX+c);
|
||||
}
|
||||
if (derivateCoefficientIndex == 3) {
|
||||
// Derivate: 1
|
||||
return 1.0;
|
||||
}
|
||||
assert(false);
|
||||
return 0.0;
|
||||
assert(derivateCoefficientIndex == 2);
|
||||
// Derivatewith respect to c: a*cos(b*x+c)
|
||||
return a * cos(b * radianX + c);
|
||||
}
|
||||
|
||||
void TrigonometricModel::specializedInitCoefficientsForFit(double * modelCoefficients, double defaultValue, Store * store, int series) const {
|
||||
|
||||
@@ -82,7 +82,7 @@ HighlightCell * RegressionController::reusableCell(int index, int type) {
|
||||
void RegressionController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
|
||||
assert(i == 0);
|
||||
assert(j >= 0 && j < k_numberOfRows);
|
||||
I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic};
|
||||
I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Proportional, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic};
|
||||
MessageTableCellWithExpression * castedCell = static_cast<MessageTableCellWithExpression *>(cell);
|
||||
castedCell->setMessage(messages[j]);
|
||||
castedCell->setLayout(m_store->regressionModel((Model::Type) j)->layout());
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
int numberOfRows() const override { return k_numberOfRows; }
|
||||
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
|
||||
private:
|
||||
constexpr static int k_numberOfRows = 9;
|
||||
constexpr static int k_numberOfRows = 10;
|
||||
constexpr static int k_numberOfCells = 6; // (240 - 70) / 35
|
||||
MessageTableCellWithExpression m_regressionCells[k_numberOfCells];
|
||||
SelectableTableView m_selectableTableView;
|
||||
|
||||
@@ -11,7 +11,7 @@ using namespace Shared;
|
||||
|
||||
namespace Regression {
|
||||
|
||||
static_assert(Model::k_numberOfModels == 9, "Number of models changed, Regression::Store() needs to adapt");
|
||||
static_assert(Model::k_numberOfModels == 10, "Number of models changed, Regression::Store() needs to adapt");
|
||||
static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regression::Store() needs to adapt (m_seriesChecksum)");
|
||||
|
||||
Store::Store() :
|
||||
@@ -285,7 +285,7 @@ double Store::squaredCorrelationCoefficient(int series) const {
|
||||
}
|
||||
|
||||
Model * Store::regressionModel(int index) {
|
||||
Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel};
|
||||
Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_proportionalModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel};
|
||||
return models[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "model/logarithmic_model.h"
|
||||
#include "model/logistic_model.h"
|
||||
#include "model/power_model.h"
|
||||
#include "model/proportional_model.h"
|
||||
#include "model/quadratic_model.h"
|
||||
#include "model/quartic_model.h"
|
||||
#include "model/trigonometric_model.h"
|
||||
@@ -79,6 +80,7 @@ private:
|
||||
uint32_t m_seriesChecksum[k_numberOfSeries];
|
||||
Model::Type m_regressionTypes[k_numberOfSeries];
|
||||
LinearModel m_linearModel;
|
||||
ProportionalModel m_proportionalModel;
|
||||
QuadraticModel m_quadraticModel;
|
||||
CubicModel m_cubicModel;
|
||||
QuarticModel m_quarticModel;
|
||||
|
||||
@@ -43,6 +43,13 @@ QUIZ_CASE(linear_regression) {
|
||||
assert_regression_is(x, y, 4, Model::Type::Linear, coefficients);
|
||||
}
|
||||
|
||||
QUIZ_CASE(proportional_regression) {
|
||||
double x[] = {7.0, 5.0, 1.0, 9.0, 3.0};
|
||||
double y[] = {-41.4851, -29.62186, -6.454245, -53.4976, -18.03325};
|
||||
double coefficients[] = {-5.89};
|
||||
assert_regression_is(x, y, 5, Model::Type::Proportional, coefficients);
|
||||
}
|
||||
|
||||
QUIZ_CASE(quadratic_regression) {
|
||||
double x[] = {-34.0, -12.0, 5.0, 86.0, -2.0};
|
||||
double y[] = {-8241.389, -1194.734, -59.163, - 46245.39, -71.774};
|
||||
|
||||
@@ -5,7 +5,7 @@ DisplayMode = "Zahlenformat"
|
||||
EditionMode = "Eingabe"
|
||||
EditionLinear = "Linear "
|
||||
Edition2D = "Natürlich "
|
||||
ComplexFormat = "Komplex"
|
||||
ComplexFormat = "Komplexe Zahlen"
|
||||
ExamMode = "Prüfungsmodus"
|
||||
ExamModeActive = "Modus erneut starten"
|
||||
ToDeactivateExamMode1 = "Um den Prüfungsmodus auszuschalten,"
|
||||
@@ -29,7 +29,7 @@ Engineering = "Technisch "
|
||||
Scientific = "Wissenschaftlich "
|
||||
SignificantFigures = "Signifikante Stellen "
|
||||
Real = "Reell "
|
||||
Cartesian = "Algebraische "
|
||||
Cartesian = "Kartesisch "
|
||||
Polar = "Polar "
|
||||
Brightness = "Helligkeit"
|
||||
SoftwareVersion = "Epsilon version"
|
||||
@@ -39,9 +39,9 @@ MicroPythonVersion = "µPythonversion"
|
||||
ResultDisplay = "Ergebniswiedergabe"
|
||||
DefaultResult = "Erweitert "
|
||||
CompactResult = "Compact "
|
||||
FontSizes = "Python Schriftgröße"
|
||||
LargeFont = "Groß "
|
||||
SmallFont = "Klein "
|
||||
FontSizes = "Python-Schriftgröße"
|
||||
LargeFont = "Große "
|
||||
SmallFont = "Kleine "
|
||||
SerialNumber = "Seriennummer"
|
||||
UpdatePopUp = "Erinnerung: Update"
|
||||
BetaPopUp = "Beta pop-up"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
ActivateDeactivate = "Aktivieren/Deaktivieren"
|
||||
ActivateDutchExamMode = "Activate Dutch exam mode"
|
||||
ActivateExamMode = "Starten Prüfungsmodus"
|
||||
ActivateExamMode = "Prüfungsmodus starten"
|
||||
ActiveExamModeMessage1 = "Alle Ihre Daten werden "
|
||||
ActiveExamModeMessage2 = "gelöscht, wenn Sie den "
|
||||
ActiveExamModeMessage3 = "Prüfungsmodus einschalten."
|
||||
ActiveDutchExamModeMessage1 = "All your data will be deleted when"
|
||||
ActiveDutchExamModeMessage2 = "you activate the exam mode. Python"
|
||||
ActiveDutchExamModeMessage3 = "application will be unavailable."
|
||||
Axis = "Achsen"
|
||||
Axis = "Achse"
|
||||
Cancel = "Abbrechen"
|
||||
ClearColumn = "Spalte löschen"
|
||||
ColumnOptions = "Optionen der Spalte"
|
||||
@@ -63,7 +63,7 @@ StatTab = "Stats"
|
||||
StandardDeviation = "Standardabweichung"
|
||||
Step = "Schrittwert"
|
||||
StorageMemoryFull1 = "Der Speicher ist voll. Löschen Sie"
|
||||
StorageMemoryFull2 = "von Daten und versuchen Sie es erneut."
|
||||
StorageMemoryFull2 = "einige Daten und versuchen Sie es erneut."
|
||||
StoreExpressionNotAllowed = "'store' ist verboten"
|
||||
SyntaxError = "Syntaxfehler"
|
||||
Sym = "sym"
|
||||
|
||||
@@ -129,7 +129,7 @@ InvSortCommandWithArg = "sort>(L)"
|
||||
K = "k"
|
||||
Lambda = "λ"
|
||||
LcmCommandWithArg = "lcm(p,q)"
|
||||
LinearRegressionFormula = " y=a·x+b "
|
||||
LinearRegressionFormula = " y=a·x+b "
|
||||
LogCommandWithArg = "log(x,a)"
|
||||
MatrixCommand = "[[\x11]]"
|
||||
MatrixCommandWithArg = "[[1,2][3,4]]"
|
||||
|
||||
@@ -57,7 +57,11 @@ float InteractiveCurveViewController::addMargin(float y, float range, bool isVer
|
||||
assert(topMarginRatio + bottomMarginRatio < 1); // Assertion so that the formula is correct
|
||||
float ratioDenominator = 1 - bottomMarginRatio - topMarginRatio;
|
||||
float ratio = isMin ? -bottomMarginRatio : topMarginRatio;
|
||||
ratio = ratio / ratioDenominator;
|
||||
/* We want to add slightly more than the required margin, so that
|
||||
* InteractiveCurveViewRange::panToMakePointVisible does not think a point is
|
||||
* invisible due to precision problems when checking if it is outside the
|
||||
* required margin. This is why we add a 1.05f factor. */
|
||||
ratio = 1.05f * ratio / ratioDenominator;
|
||||
return y + ratio * range;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "interactive_curve_view_range.h"
|
||||
#include <ion.h>
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <poincare/preferences.h>
|
||||
@@ -200,30 +199,38 @@ void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) {
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation) {
|
||||
float xRange = xMax() - xMin();
|
||||
float yRange = yMax() - yMin();
|
||||
if (x < xMin() + leftMarginRation*xRange - FLT_EPSILON && !std::isinf(x) && !std::isnan(x)) {
|
||||
m_yAuto = false;
|
||||
float newXMin = x - leftMarginRation*xRange;
|
||||
m_xRange.setMax(newXMin + xRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetXMin(newXMin, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRatio, float leftMarginRatio) {
|
||||
if (!std::isinf(x) && !std::isnan(x)) {
|
||||
const float xRange = xMax() - xMin();
|
||||
const float leftMargin = leftMarginRatio * xRange;
|
||||
if (x < xMin() + leftMargin) {
|
||||
m_yAuto = false;
|
||||
const float newXMin = x - leftMargin;
|
||||
m_xRange.setMax(newXMin + xRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetXMin(newXMin, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
}
|
||||
const float rightMargin = rightMarginRatio * xRange;
|
||||
if (x > xMax() - rightMargin) {
|
||||
m_yAuto = false;
|
||||
m_xRange.setMax(x + rightMargin, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetXMin(xMax() - xRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
}
|
||||
}
|
||||
if (x > xMax() - rightMarginRatio*xRange + FLT_EPSILON && !std::isinf(x) && !std::isnan(x)) {
|
||||
m_yAuto = false;
|
||||
m_xRange.setMax(x + rightMarginRatio*xRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetXMin(xMax() - xRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
}
|
||||
if (y < yMin() + bottomMarginRation*yRange - FLT_EPSILON && !std::isinf(y) && !std::isnan(y)) {
|
||||
m_yAuto = false;
|
||||
float newYMin = y - bottomMarginRation*yRange;
|
||||
m_yRange.setMax(newYMin + yRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetYMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
}
|
||||
if (y > yMax() - topMarginRatio*yRange + FLT_EPSILON && !std::isinf(y) && !std::isnan(y)) {
|
||||
m_yAuto = false;
|
||||
m_yRange.setMax(y + topMarginRatio*yRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetYMin(yMax() - yRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
if (!std::isinf(y) && !std::isnan(y)) {
|
||||
const float yRange = yMax() - yMin();
|
||||
const float bottomMargin = bottomMarginRatio * yRange;
|
||||
if (y < yMin() + bottomMargin) {
|
||||
m_yAuto = false;
|
||||
const float newYMin = y - bottomMargin;
|
||||
m_yRange.setMax(newYMin + yRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetYMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
}
|
||||
const float topMargin = topMarginRatio * yRange;
|
||||
if (y > yMax() - topMargin) {
|
||||
m_yAuto = false;
|
||||
m_yRange.setMax(y + topMargin, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
MemoizedCurveViewRange::protectedSetYMin(yMax() - yRange, k_lowerMaxFloat, k_upperMaxFloat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
void setEnd(double f);
|
||||
void setStep(double f);
|
||||
void setElement(int i, double f);
|
||||
void forceRecompute(){ m_needCompute = true;}
|
||||
void reset();
|
||||
void clear();
|
||||
// TODO: decide the max number of elements after optimization
|
||||
|
||||
@@ -85,6 +85,7 @@ int IntervalParameterController::reusableParameterCellCount(int type) {
|
||||
}
|
||||
|
||||
void IntervalParameterController::buttonAction() {
|
||||
m_interval->forceRecompute();
|
||||
StackViewController * stack = stackController();
|
||||
stack->pop();
|
||||
if (stack->depth() > 1) {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Shared {
|
||||
|
||||
bool ParameterTextFieldDelegate::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Backspace && !textField->isEditing()) {
|
||||
textField->reinitDraftTextBuffer();
|
||||
textField->setEditing(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -66,11 +66,40 @@ bool RoundCursorView::eraseCursorIfPossible() {
|
||||
return false;
|
||||
}
|
||||
// Erase the cursor
|
||||
KDColor cursorWorkingBuffer[Dots::LargeDotDiameter*Dots::LargeDotDiameter];
|
||||
KDColor cursorWorkingBuffer[k_cursorSize * k_cursorSize];
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->setOrigin(currentFrame.origin());
|
||||
ctx->setClippingRect(currentFrame);
|
||||
ctx->fillRectWithPixels(KDRect(0,0,k_cursorSize, k_cursorSize), m_underneathPixelBuffer, cursorWorkingBuffer);
|
||||
KDSize cursorSize = KDSize(k_cursorSize, k_cursorSize);
|
||||
|
||||
/* We assert that the visible frame is not cropped (indeed a cursor is always
|
||||
* fully inside the window, thanks to panToMakeCursorVisible). Otherwise, we
|
||||
* would need to change this algorithm.
|
||||
*
|
||||
* +---+
|
||||
* | |<- frame m_underneathPixelBuffer: +---+
|
||||
* +----+---+--------+ |000|
|
||||
* | |xxx| |<- parentVisibleFrame |xxx|
|
||||
* | +---+ | +---+
|
||||
* | |
|
||||
* +-----------------+
|
||||
*
|
||||
* +---+
|
||||
* |xxx|: absoluteVisibleFrame
|
||||
* +---+
|
||||
*
|
||||
* What we would draw with the current algorithm:
|
||||
* +---+
|
||||
* | |<- frame
|
||||
* +----+---+--------+
|
||||
* | |000| |<- parentVisibleFrame
|
||||
* | +---+ |
|
||||
* | |
|
||||
* +-----------------+
|
||||
*
|
||||
* */
|
||||
assert(currentFrame.size() == cursorSize);
|
||||
ctx->fillRectWithPixels(KDRect(0, 0, cursorSize), m_underneathPixelBuffer, cursorWorkingBuffer);
|
||||
// TODO Restore the context to previous values?
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Shared {
|
||||
|
||||
AbstractScrollableMultipleExpressionsView::ContentCell::ContentCell() :
|
||||
m_rightExpressionView(),
|
||||
m_approximateSign(KDFont::LargeFont, I18n::Message::AlmostEqual, 0.5f, 0.5f, Palette::GreyVeryDark),
|
||||
m_approximateSign(k_font, k_defaultApproximateMessage, 0.5f, 0.5f, Palette::GreyVeryDark),
|
||||
m_centeredExpressionView(),
|
||||
m_selectedSubviewPosition(SubviewPosition::Center),
|
||||
m_displayCenter(true)
|
||||
@@ -55,27 +55,11 @@ void AbstractScrollableMultipleExpressionsView::ContentCell::reloadTextColor() {
|
||||
}
|
||||
|
||||
KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOptimalDisplay() const {
|
||||
KDSize leftSize = KDSizeZero;
|
||||
KDCoordinate leftViewBaseline = 0;
|
||||
KDCoordinate width = 0;
|
||||
if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) {
|
||||
leftSize = leftExpressionView()->minimalSizeForOptimalDisplay();
|
||||
leftViewBaseline = leftExpressionView()->layout().baseline();
|
||||
width += leftSize.width() + Metric::CommonLargeMargin;
|
||||
}
|
||||
KDSize rightExpressionSize = m_rightExpressionView.minimalSizeForOptimalDisplay();
|
||||
width += rightExpressionSize.width();
|
||||
Layout l = m_rightExpressionView.layout();
|
||||
KDCoordinate rightBaseline = l.isUninitialized() ? 0 : l.baseline();
|
||||
KDSize centeredExpressionSize = KDSizeZero;
|
||||
KDCoordinate centeredBaseline = 0;
|
||||
if (displayCenter()) {
|
||||
centeredBaseline = m_centeredExpressionView.layout().baseline();
|
||||
centeredExpressionSize = m_centeredExpressionView.minimalSizeForOptimalDisplay();
|
||||
width += centeredExpressionSize.width() + 2*Metric::CommonLargeMargin + m_approximateSign.minimalSizeForOptimalDisplay().width();
|
||||
}
|
||||
KDCoordinate height = std::max(std::max(centeredBaseline, rightBaseline), leftViewBaseline) + std::max(std::max(centeredExpressionSize.height()-centeredBaseline, rightExpressionSize.height()-rightBaseline), leftSize.height()-leftViewBaseline);
|
||||
return KDSize(width, height);
|
||||
return privateMinimalSizeForOptimalDisplay(false);
|
||||
}
|
||||
|
||||
KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOptimalDisplayFullSize() const {
|
||||
return privateMinimalSizeForOptimalDisplay(true);
|
||||
}
|
||||
|
||||
void AbstractScrollableMultipleExpressionsView::ContentCell::setSelectedSubviewPosition(AbstractScrollableMultipleExpressionsView::SubviewPosition subviewPosition) {
|
||||
@@ -111,6 +95,101 @@ int AbstractScrollableMultipleExpressionsView::ContentCell::numberOfSubviews() c
|
||||
return nbOfSubviews;
|
||||
}
|
||||
|
||||
KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::baseline(KDCoordinate * leftBaseline, KDCoordinate * centerBaseline, KDCoordinate * rightBaseline) const {
|
||||
// Left view
|
||||
KDCoordinate leftViewBaseline = (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) ?
|
||||
leftExpressionView()->layout().baseline() :
|
||||
0;
|
||||
if (leftBaseline != nullptr) {
|
||||
*leftBaseline = leftViewBaseline;
|
||||
}
|
||||
|
||||
// Center view
|
||||
KDCoordinate centerViewBaseline = displayCenter() ? m_centeredExpressionView.layout().baseline() : 0;
|
||||
if (centerBaseline != nullptr) {
|
||||
*centerBaseline = centerViewBaseline;
|
||||
}
|
||||
|
||||
// Right view
|
||||
KDCoordinate rightViewBaseline = m_rightExpressionView.layout().isUninitialized() ?
|
||||
0 :
|
||||
m_rightExpressionView.layout().baseline();
|
||||
if (rightBaseline != nullptr) {
|
||||
*rightBaseline = rightViewBaseline;
|
||||
}
|
||||
|
||||
return std::max(std::max(leftViewBaseline, centerViewBaseline), rightViewBaseline);
|
||||
}
|
||||
|
||||
void AbstractScrollableMultipleExpressionsView::ContentCell::subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
|
||||
// Subviews sizes
|
||||
KDSize leftSize = leftExpressionView() ? leftExpressionView()->minimalSizeForOptimalDisplay() : KDSizeZero;
|
||||
KDSize centerSize = displayCenter() ? m_centeredExpressionView.minimalSizeForOptimalDisplay() : KDSizeZero;
|
||||
KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay();
|
||||
|
||||
// Compute baselines
|
||||
KDCoordinate leftBaseline = 0;
|
||||
KDCoordinate centerBaseline = 0;
|
||||
KDCoordinate rightBaseline = 0;
|
||||
KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline);
|
||||
|
||||
// Layout left view
|
||||
KDCoordinate currentWidth = 0;
|
||||
if (leftExpressionView()) {
|
||||
assert(leftFrame != nullptr);
|
||||
*leftFrame = KDRect(currentWidth, viewBaseline - leftBaseline, leftSize);
|
||||
currentWidth += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
|
||||
}
|
||||
|
||||
// Layout center expression
|
||||
if (displayCenter()) {
|
||||
assert(centerFrame != nullptr && approximateSignFrame != nullptr);
|
||||
KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay();
|
||||
*centerFrame = KDRect(currentWidth, viewBaseline - centerBaseline, centerSize);
|
||||
currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + centerSize.width();
|
||||
*approximateSignFrame = KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize);
|
||||
currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + approximateSignSize.width();
|
||||
}
|
||||
|
||||
// Layout right expression
|
||||
assert(rightFrame != nullptr);
|
||||
*rightFrame = KDRect(currentWidth, viewBaseline - rightBaseline, rightSize);
|
||||
}
|
||||
|
||||
KDSize AbstractScrollableMultipleExpressionsView::ContentCell::privateMinimalSizeForOptimalDisplay(bool forceFullDisplay) const {
|
||||
KDCoordinate width = 0;
|
||||
|
||||
// Compute baselines
|
||||
KDCoordinate leftBaseline = 0;
|
||||
KDCoordinate centerBaseline = 0;
|
||||
KDCoordinate rightBaseline = 0;
|
||||
KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline);
|
||||
|
||||
KDSize leftSize = KDSizeZero;
|
||||
if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) {
|
||||
leftSize = leftExpressionView()->minimalSizeForOptimalDisplay();
|
||||
width += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
|
||||
}
|
||||
|
||||
KDSize centerSize = KDSizeZero;
|
||||
if (displayCenter() || (forceFullDisplay && !m_centeredExpressionView.layout().isUninitialized())) {
|
||||
centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay();
|
||||
width += centerSize.width() + 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width();
|
||||
}
|
||||
|
||||
KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay();
|
||||
width += rightSize.width();
|
||||
|
||||
KDCoordinate height = viewBaseline
|
||||
+ std::max(
|
||||
std::max(
|
||||
centerSize.height() - centerBaseline,
|
||||
rightSize.height() - rightBaseline),
|
||||
leftSize.height() - leftBaseline);
|
||||
|
||||
return KDSize(width, height);
|
||||
}
|
||||
|
||||
View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(int index) {
|
||||
bool leftIsVisible = leftExpressionView() != nullptr;
|
||||
if (leftIsVisible && index == 0) {
|
||||
@@ -120,36 +199,30 @@ View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(in
|
||||
return views[index - leftIsVisible];
|
||||
}
|
||||
|
||||
KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::StandardApproximateViewAndMarginsSize() {
|
||||
return 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + k_font->stringSize(I18n::translate(k_defaultApproximateMessage)).width();
|
||||
}
|
||||
|
||||
void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool force) {
|
||||
// Subviews sizes
|
||||
KDSize leftSize = leftExpressionView() ? leftExpressionView()->minimalSizeForOptimalDisplay() : KDSizeZero;
|
||||
KDCoordinate leftViewBaseline = leftExpressionView() && !leftExpressionView()->layout().isUninitialized() ? leftExpressionView()->layout().baseline() : 0;
|
||||
KDSize centeredExpressionSize = KDSizeZero;
|
||||
KDCoordinate centeredBaseline = 0;
|
||||
if (displayCenter()) {
|
||||
centeredBaseline = m_centeredExpressionView.layout().baseline();
|
||||
centeredExpressionSize = m_centeredExpressionView.minimalSizeForOptimalDisplay();
|
||||
if (bounds().width() <= 0 || bounds().height() <= 0) {
|
||||
// TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews
|
||||
return;
|
||||
}
|
||||
KDSize rightExpressionSize = m_rightExpressionView.minimalSizeForOptimalDisplay();
|
||||
KDCoordinate rightBaseline = m_rightExpressionView.layout().isUninitialized() ? 0 : m_rightExpressionView.layout().baseline();
|
||||
// Compute baseline
|
||||
KDCoordinate baseline = std::max(std::max(leftViewBaseline, rightBaseline), centeredBaseline);
|
||||
// Layout left view
|
||||
KDCoordinate currentWidth = 0;
|
||||
if (leftExpressionView()) {
|
||||
leftExpressionView()->setFrame(KDRect(currentWidth, baseline-leftViewBaseline, leftSize), force);
|
||||
currentWidth += leftSize.width() + Metric::CommonLargeMargin;
|
||||
KDRect leftFrame = KDRectZero;
|
||||
KDRect centerFrame = KDRectZero;
|
||||
KDRect approximateSignFrame = KDRectZero;
|
||||
KDRect rightFrame = KDRectZero;
|
||||
subviewFrames(&leftFrame, ¢erFrame, &approximateSignFrame, &rightFrame);
|
||||
if (leftExpressionView() != nullptr) {
|
||||
leftExpressionView()->setFrame(leftFrame, force);
|
||||
}
|
||||
// Layout centered expression
|
||||
if (displayCenter()) {
|
||||
KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay();
|
||||
m_centeredExpressionView.setFrame(KDRect(currentWidth, baseline-centeredBaseline, centeredExpressionSize), force);
|
||||
currentWidth += Metric::CommonLargeMargin+centeredExpressionSize.width();
|
||||
m_approximateSign.setFrame(KDRect(currentWidth, baseline-approximateSignSize.height()/2, approximateSignSize), force);
|
||||
currentWidth += Metric::CommonLargeMargin + approximateSignSize.width();
|
||||
if (centeredExpressionView() != nullptr) {
|
||||
centeredExpressionView()->setFrame(centerFrame, force);
|
||||
}
|
||||
m_approximateSign.setFrame(approximateSignFrame, force);
|
||||
if (rightExpressionView() != nullptr) {
|
||||
rightExpressionView()->setFrame(rightFrame, force);
|
||||
}
|
||||
// Layout right expression
|
||||
m_rightExpressionView.setFrame(KDRect(currentWidth, baseline-rightBaseline, rightExpressionSize), force);
|
||||
}
|
||||
|
||||
AbstractScrollableMultipleExpressionsView::AbstractScrollableMultipleExpressionsView(Responder * parentResponder, View * contentCell) :
|
||||
@@ -194,38 +267,40 @@ void AbstractScrollableMultipleExpressionsView::setDisplayCenter(bool display) {
|
||||
}
|
||||
|
||||
bool AbstractScrollableMultipleExpressionsView::handleEvent(Ion::Events::Event event) {
|
||||
bool leftIsVisible = false;
|
||||
KDCoordinate leftWidth = 0;
|
||||
if (contentCell()->leftExpressionView()) {
|
||||
leftWidth = contentCell()->leftExpressionView()->minimalSizeForOptimalDisplay().width();
|
||||
leftIsVisible = leftWidth - contentOffset().x() > 0;
|
||||
}
|
||||
KDCoordinate rightExpressionWidth = contentCell()->rightExpressionView()->minimalSizeForOptimalDisplay().width();
|
||||
bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - contentOffset().x() < bounds().width();
|
||||
bool centeredExpressionIsVisibleOnTheLeft = false;
|
||||
bool centeredExpressionIsVisibleOnTheRight = false;
|
||||
if (contentCell()->displayCenter()) {
|
||||
KDCoordinate centerExpressionWidth = contentCell()->centeredExpressionView()->minimalSizeForOptimalDisplay().width();
|
||||
KDCoordinate signWidth = contentCell()->approximateSign()->minimalSizeForOptimalDisplay().width();
|
||||
centeredExpressionIsVisibleOnTheLeft = leftWidth + Metric::CommonLargeMargin + centerExpressionWidth - contentOffset().x() > 0;
|
||||
centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2*Metric::CommonLargeMargin - contentOffset().x() < bounds().width();
|
||||
}
|
||||
// Select center
|
||||
if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && centeredExpressionIsVisibleOnTheLeft) ||
|
||||
(event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && centeredExpressionIsVisibleOnTheRight)) {
|
||||
setSelectedSubviewPosition(SubviewPosition::Center);
|
||||
return true;
|
||||
}
|
||||
// Select left
|
||||
if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && leftIsVisible) ||
|
||||
(event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Center && leftIsVisible)) {
|
||||
setSelectedSubviewPosition(SubviewPosition::Left);
|
||||
return true;
|
||||
}
|
||||
if ((event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Center && rightExpressionIsVisible) ||
|
||||
(event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && rightExpressionIsVisible)) {
|
||||
setSelectedSubviewPosition(SubviewPosition::Right);
|
||||
return true;
|
||||
if (event == Ion::Events::Left || event == Ion::Events::Right ) {
|
||||
bool leftIsVisible = false;
|
||||
KDCoordinate leftWidth = 0;
|
||||
if (contentCell()->leftExpressionView()) {
|
||||
leftWidth = contentCell()->leftExpressionView()->minimalSizeForOptimalDisplay().width();
|
||||
leftIsVisible = leftWidth - contentOffset().x() > 0;
|
||||
}
|
||||
KDCoordinate rightExpressionWidth = contentCell()->rightExpressionView()->minimalSizeForOptimalDisplay().width();
|
||||
bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - contentOffset().x() < bounds().width();
|
||||
bool centeredExpressionIsVisibleOnTheLeft = false;
|
||||
bool centeredExpressionIsVisibleOnTheRight = false;
|
||||
if (contentCell()->displayCenter()) {
|
||||
KDCoordinate centerExpressionWidth = contentCell()->centeredExpressionView()->minimalSizeForOptimalDisplay().width();
|
||||
KDCoordinate signWidth = contentCell()->approximateSign()->minimalSizeForOptimalDisplay().width();
|
||||
centeredExpressionIsVisibleOnTheLeft = leftWidth + k_horizontalMargin + centerExpressionWidth - contentOffset().x() > 0;
|
||||
centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2 * k_horizontalMargin - contentOffset().x() < bounds().width();
|
||||
}
|
||||
// Select center
|
||||
if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && centeredExpressionIsVisibleOnTheLeft) ||
|
||||
(event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && centeredExpressionIsVisibleOnTheRight)) {
|
||||
setSelectedSubviewPosition(SubviewPosition::Center);
|
||||
return true;
|
||||
}
|
||||
// Select left
|
||||
if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && leftIsVisible) ||
|
||||
(event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Center && leftIsVisible)) {
|
||||
setSelectedSubviewPosition(SubviewPosition::Left);
|
||||
return true;
|
||||
}
|
||||
if ((event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Center && rightExpressionIsVisible) ||
|
||||
(event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && rightExpressionIsVisible)) {
|
||||
setSelectedSubviewPosition(SubviewPosition::Right);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return ScrollableView::handleEvent(event);
|
||||
}
|
||||
|
||||
@@ -2,16 +2,23 @@
|
||||
#define SHARED_SCROLLABLE_MULTIPLE_EXPRESSIONS_VIEW_H
|
||||
|
||||
#include <escher.h>
|
||||
#include <apps/i18n.h>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class AbstractScrollableMultipleExpressionsView : public ScrollableView, public ScrollViewDataSource {
|
||||
public:
|
||||
constexpr static KDCoordinate k_horizontalMargin = Metric::CommonLargeMargin;
|
||||
enum class SubviewPosition : uint8_t {
|
||||
Left = 0,
|
||||
Center = 1,
|
||||
Right = 2
|
||||
};
|
||||
|
||||
static KDCoordinate StandardApproximateViewAndMarginsSize() {
|
||||
return ContentCell::StandardApproximateViewAndMarginsSize();
|
||||
}
|
||||
|
||||
AbstractScrollableMultipleExpressionsView(Responder * parentResponder, View * contentCell);
|
||||
::EvenOddCell * evenOddCell() {
|
||||
return contentCell();
|
||||
@@ -28,18 +35,19 @@ public:
|
||||
void setDisplayCenter(bool display);
|
||||
void reloadScroll();
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
Poincare::Layout layout() const {
|
||||
return constContentCell()->layout();
|
||||
}
|
||||
Poincare::Layout layout() const { return constContentCell()->layout(); }
|
||||
KDCoordinate baseline() const { return constContentCell()->baseline(); }
|
||||
protected:
|
||||
class ContentCell : public ::EvenOddCell {
|
||||
public:
|
||||
static KDCoordinate StandardApproximateViewAndMarginsSize();
|
||||
ContentCell();
|
||||
KDColor backgroundColor() const override;
|
||||
void setHighlighted(bool highlight) override;
|
||||
void setEven(bool even) override;
|
||||
void reloadTextColor();
|
||||
KDSize minimalSizeForOptimalDisplay() const override;
|
||||
KDSize minimalSizeForOptimalDisplayFullSize() const;
|
||||
virtual ExpressionView * leftExpressionView() const { return nullptr; }
|
||||
ExpressionView * rightExpressionView() {
|
||||
return &m_rightExpressionView;
|
||||
@@ -59,8 +67,12 @@ protected:
|
||||
void layoutSubviews(bool force = false) override;
|
||||
int numberOfSubviews() const override;
|
||||
virtual Poincare::Layout layout() const override;
|
||||
|
||||
KDCoordinate baseline(KDCoordinate * leftBaseline = nullptr, KDCoordinate * centerBaseline = nullptr, KDCoordinate * rightBaseline = nullptr) const;
|
||||
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame);
|
||||
private:
|
||||
constexpr static const KDFont * k_font = KDFont::LargeFont;
|
||||
const static I18n::Message k_defaultApproximateMessage = I18n::Message::AlmostEqual;
|
||||
KDSize privateMinimalSizeForOptimalDisplay(bool forceFullDisplay) const;
|
||||
View * subviewAtIndex(int index) override;
|
||||
ExpressionView m_rightExpressionView;
|
||||
MessageTextView m_approximateSign;
|
||||
@@ -68,8 +80,8 @@ protected:
|
||||
SubviewPosition m_selectedSubviewPosition;
|
||||
bool m_displayCenter;
|
||||
};
|
||||
virtual ContentCell * contentCell() = 0;
|
||||
virtual const ContentCell * constContentCell() const = 0;
|
||||
virtual ContentCell * contentCell() = 0;
|
||||
virtual const ContentCell * constContentCell() const = 0;
|
||||
};
|
||||
|
||||
class ScrollableTwoExpressionsView : public AbstractScrollableMultipleExpressionsView {
|
||||
@@ -82,7 +94,9 @@ public:
|
||||
Metric::CommonLargeMargin
|
||||
);
|
||||
}
|
||||
|
||||
KDSize minimalSizeForOptimalDisplayFullSize() const {
|
||||
return constContentCell()->minimalSizeForOptimalDisplayFullSize();
|
||||
}
|
||||
private:
|
||||
ContentCell * contentCell() override { return &m_contentCell; };
|
||||
const ContentCell * constContentCell() const override { return &m_contentCell; };
|
||||
|
||||
@@ -58,10 +58,15 @@ int ZoomParameterController::ContentView::numberOfSubviews() const {
|
||||
|
||||
View * ZoomParameterController::ContentView::subviewAtIndex(int index) {
|
||||
assert(index >= 0 && index < 2);
|
||||
/* The order of subview is important here :
|
||||
* If we redraw the curveView before the legendView, that can have some display issue, when exiting sleep mode, which
|
||||
can be visible, if the redraw of curveView is long (with complicated curve), so we prefer to have legendView
|
||||
at first subview.
|
||||
*/
|
||||
if (index == 0) {
|
||||
return m_curveView;
|
||||
return &m_legendView;
|
||||
}
|
||||
return &m_legendView;
|
||||
return m_curveView;
|
||||
}
|
||||
|
||||
void ZoomParameterController::ContentView::layoutSubviews(bool force) {
|
||||
|
||||
@@ -3,7 +3,7 @@ UnitTimeMenu = "Zeit"
|
||||
UnitTimeSecondMenu = "Sekunde"
|
||||
UnitTimeSecond = "Sekunde"
|
||||
UnitTimeSecondMilli = "Millisekunde"
|
||||
UnitTimeSecondMicro = "Microsekunde"
|
||||
UnitTimeSecondMicro = "Mikrosekunde"
|
||||
UnitTimeSecondNano = "Nanosekunde"
|
||||
UnitTimeMinute = "Minute"
|
||||
UnitTimeHour = "Stunde"
|
||||
@@ -11,14 +11,14 @@ UnitTimeDay = "Tag"
|
||||
UnitTimeWeek = "Woche"
|
||||
UnitTimeMonth = "Monat"
|
||||
UnitTimeYear = "Jahr"
|
||||
UnitDistanceMenu = "Distanz"
|
||||
UnitDistanceMenu = "Entfernung"
|
||||
UnitDistanceMeterMenu = "Meter"
|
||||
UnitDistanceMeterKilo = "Kilometer"
|
||||
UnitDistanceMeter = "Meter"
|
||||
UnitDistanceMeterMilli = "Millimeter"
|
||||
UnitDistanceMeterMicro = "Micrometer"
|
||||
UnitDistanceMeterNano = "Nanometer"
|
||||
UnitDistanceMeterPico = "Picometer"
|
||||
UnitDistanceMeterPico = "Pikometer"
|
||||
UnitDistanceAstronomicalUnit = "Astronomische Einheit"
|
||||
UnitDistanceLightYear = "Lichtjahr"
|
||||
UnitDistanceParsec = "Parsec"
|
||||
@@ -26,7 +26,7 @@ UnitMassMenu = "Masse"
|
||||
UnitMassGramKilo = "Kilogramm"
|
||||
UnitMassGram = "Gramm"
|
||||
UnitMassGramMilli = "Milligramm"
|
||||
UnitMassGramMicro = "Microgramm"
|
||||
UnitMassGramMicro = "Mikrogramm"
|
||||
UnitMassGramNano = "Nanogramm"
|
||||
UnitDistanceImperialMenu = "US Customary"
|
||||
UnitDistanceInch = "Inch"
|
||||
@@ -41,14 +41,14 @@ UnitMassTonne = "Tonne"
|
||||
UnitCurrentMenu = "Elektrischer Strom"
|
||||
UnitCurrentAmpere = "Ampere"
|
||||
UnitCurrentAmpereMilli = "Milliampere"
|
||||
UnitCurrentAmpereMicro = "Microampere"
|
||||
UnitTemperatureMenu = "Temperaturen"
|
||||
UnitCurrentAmpereMicro = "Mikroampere"
|
||||
UnitTemperatureMenu = "Temperatur"
|
||||
UnitTemperatureKelvin = "Kelvin"
|
||||
UnitAmountMenu = "Substanzmenge"
|
||||
UnitAmountMenu = "Stoffmenge"
|
||||
UnitAmountMole = "Mol"
|
||||
UnitAmountMoleMilli = "Millimol"
|
||||
UnitAmountMoleMicro = "Micromol"
|
||||
UnitLuminousIntensityMenu = "Helligkeit"
|
||||
UnitAmountMoleMicro = "Mikromol"
|
||||
UnitLuminousIntensityMenu = "Lichtstärke"
|
||||
UnitLuminousIntensityCandela = "Candela"
|
||||
UnitFrequencyMenu = "Frequenz"
|
||||
UnitFrequencyHertzGiga = "Gigahertz"
|
||||
@@ -61,7 +61,7 @@ UnitForceNewton = "Newton"
|
||||
UnitForceNewtonMilli = "Millinewton"
|
||||
UnitPressureMenu = "Druck"
|
||||
UnitPressurePascal = "Pascal"
|
||||
UnitPressurePascalHecto = "Hectopascal"
|
||||
UnitPressurePascalHecto = "Hektopascal"
|
||||
UnitPressureBar = "Bar"
|
||||
UnitPressureAtm = "Atmosphere"
|
||||
UnitEnergyMenu = "Energie"
|
||||
@@ -80,33 +80,33 @@ UnitPowerWattMega = "Megawatt"
|
||||
UnitPowerWattKilo = "Kilowatt"
|
||||
UnitPowerWatt = "Watt"
|
||||
UnitPowerWattMilli = "Milliwatt"
|
||||
UnitPowerWattMicro = "Microwatt"
|
||||
UnitPowerWattMicro = "Mikrowatt"
|
||||
UnitElectricChargeMenu = "Elektrische Ladung"
|
||||
UnitChargeCoulomb = "Coulomb"
|
||||
UnitPotentialMenu = "Elektrisches Potenzial"
|
||||
UnitPotentialMenu = "Elektrische Spannung"
|
||||
UnitPotentialVoltKilo = "Kilovolt"
|
||||
UnitPotentialVolt = "Volt"
|
||||
UnitPotentialVoltMilli = "Millivolt"
|
||||
UnitPotentialVoltMicro = "Microvolt"
|
||||
UnitPotentialVoltMicro = "Mikrovolt"
|
||||
UnitCapacitanceMenu = "Elektrische Kapazität"
|
||||
UnitCapacitanceFarad = "Farad"
|
||||
UnitCapacitanceFaradMilli = "Millifarad"
|
||||
UnitCapacitanceFaradMicro = "Microfarad"
|
||||
UnitCapacitanceFaradMicro = "Mikrofarad"
|
||||
UnitResistanceMenu = "Elektrischer Widerstand"
|
||||
UnitResistanceOhmKilo = "Kiloohm"
|
||||
UnitResistanceOhm = "Ohm"
|
||||
UnitConductanceMenu = "Elektrische Leitfähigkeit"
|
||||
UnitConductanceSiemens = "Siemens"
|
||||
UnitConductanceSiemensMilli = "Millisiemens"
|
||||
UnitMagneticFieldMenu = "Magnetisches Feld"
|
||||
UnitMagneticFieldMenu = "Magnetfeld"
|
||||
UnitMagneticFieldTesla = "Tesla"
|
||||
InductanceMenu = "Elektrische Induktion"
|
||||
InductanceMenu = "Elektrische Induktivität"
|
||||
UnitInductanceHenry = "Henry"
|
||||
UnitSurfaceMenu = "Fläche"
|
||||
UnitSurfaceHectar = "Hektar"
|
||||
UnitVolumeMenu = "Volumen"
|
||||
UnitVolumeLiter = "Liter"
|
||||
UnitVolumeLiterDeci = "Deciliter"
|
||||
UnitVolumeLiterDeci = "Deziliter"
|
||||
UnitVolumeLiterCenti = "Centiliter"
|
||||
UnitVolumeLiterMilli = "Milliliter"
|
||||
Toolbox = "Werkzeugkasten"
|
||||
|
||||
@@ -52,7 +52,7 @@ UnitLuminousIntensityMenu = "Intensité lumineuse"
|
||||
UnitLuminousIntensityCandela = "Candela"
|
||||
UnitFrequencyMenu = "Fréquence"
|
||||
UnitFrequencyHertzGiga = "Gigahertz"
|
||||
UnitFrequencyHertzMega = "Megahertz"
|
||||
UnitFrequencyHertzMega = "Mégahertz"
|
||||
UnitFrequencyHertzKilo = "Kilohertz"
|
||||
UnitFrequencyHertz = "Hertz"
|
||||
UnitForceMenu = "Force"
|
||||
@@ -69,14 +69,14 @@ UnitEnergyJouleMenu = "Joule"
|
||||
UnitEnergyJouleKilo = "Kilojoule"
|
||||
UnitEnergyJoule = "Joule"
|
||||
UnitEnergyJouleMilli = "Millijoule"
|
||||
UnitEnergyEletronVoltMenu = "Electronvolt"
|
||||
UnitEnergyElectronVoltMega = "Megaelectronvolt"
|
||||
UnitEnergyElectronVoltKilo = "Kiloelectronvolt"
|
||||
UnitEnergyElectronVolt = "Electronvolt"
|
||||
UnitEnergyElectronVoltMilli = "Millielectronvolt"
|
||||
UnitEnergyEletronVoltMenu = "Électronvolt"
|
||||
UnitEnergyElectronVoltMega = "Mégaélectronvolt"
|
||||
UnitEnergyElectronVoltKilo = "Kiloélectronvolt"
|
||||
UnitEnergyElectronVolt = "Électronvolt"
|
||||
UnitEnergyElectronVoltMilli = "Milliélectronvolt"
|
||||
UnitPowerMenu = "Puissance"
|
||||
UnitPowerWattGiga = "Gigawatt"
|
||||
UnitPowerWattMega = "Megawatt"
|
||||
UnitPowerWattMega = "Mégawatt"
|
||||
UnitPowerWattKilo = "Kilowatt"
|
||||
UnitPowerWatt = "Watt"
|
||||
UnitPowerWattMilli = "Milliwatt"
|
||||
@@ -106,7 +106,7 @@ UnitSurfaceMenu = "Superficie"
|
||||
UnitSurfaceHectar = "Hectare"
|
||||
UnitVolumeMenu = "Volume"
|
||||
UnitVolumeLiter = "Litre"
|
||||
UnitVolumeLiterDeci = "Decilitre"
|
||||
UnitVolumeLiterDeci = "Décilitre"
|
||||
UnitVolumeLiterCenti = "Centilitre"
|
||||
UnitVolumeLiterMilli = "Millilitre"
|
||||
Toolbox = "Boîte à outils"
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
bool isEmpty() const;
|
||||
bool inputViewHeightDidChange();
|
||||
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false);
|
||||
void setLayoutInsertionCursorEvent(Ion::Events::Event event) { m_layoutField.setInsertionCursorEvent(event); }
|
||||
|
||||
/* View */
|
||||
int numberOfSubviews() const override { return 1; }
|
||||
|
||||
@@ -19,13 +19,14 @@ public:
|
||||
ScrollableView(parentResponder, &m_contentView, this),
|
||||
EditableField(inputEventHandlerDelegate),
|
||||
m_contentView(),
|
||||
m_insertionCursorEvent(Ion::Events::None),
|
||||
m_delegate(delegate)
|
||||
{}
|
||||
void setDelegates(InputEventHandlerDelegate * inputEventHandlerDelegate, LayoutFieldDelegate * delegate) { m_inputEventHandlerDelegate = inputEventHandlerDelegate; m_delegate = delegate; }
|
||||
Poincare::Context * context() const;
|
||||
bool isEditing() const override { return m_contentView.isEditing(); }
|
||||
void setEditing(bool isEditing) override;
|
||||
void clearLayout() { m_contentView.clearLayout(); }
|
||||
void clearLayout();
|
||||
void scrollToCursor() {
|
||||
scrollToBaselinedRect(m_contentView.cursorRect(), m_contentView.cursor()->baselineWithoutSelection());
|
||||
}
|
||||
@@ -33,6 +34,7 @@ public:
|
||||
Poincare::Layout layout() const { return m_contentView.expressionView()->layout(); }
|
||||
CodePoint XNTCodePoint(CodePoint defaultXNTCodePoint) override;
|
||||
void putCursorRightOfLayout();
|
||||
void setInsertionCursorEvent(Ion::Events::Event event) { m_insertionCursorEvent = event; }
|
||||
|
||||
// ScrollableView
|
||||
void setBackgroundColor(KDColor c) override {
|
||||
@@ -60,6 +62,7 @@ private:
|
||||
void scrollRightOfLayout(Poincare::Layout layoutR);
|
||||
void scrollToBaselinedRect(KDRect rect, KDCoordinate baseline);
|
||||
void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout = false);
|
||||
bool eventShouldUpdateInsertionCursor(Ion::Events::Event event) { return event == m_insertionCursorEvent; }
|
||||
|
||||
class ContentView : public View {
|
||||
public:
|
||||
@@ -83,14 +86,30 @@ private:
|
||||
void copySelection(Poincare::Context * context);
|
||||
bool selectionIsEmpty() const;
|
||||
void deleteSelection();
|
||||
void invalidateInsertionCursor() { m_insertionCursor = Poincare::LayoutCursor(); }
|
||||
void updateInsertionCursor() {
|
||||
if (!m_insertionCursor.isDefined()) {
|
||||
m_insertionCursor = m_cursor;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int numberOfSubviews() const override { return 2; }
|
||||
View * subviewAtIndex(int index) override;
|
||||
void layoutSubviews(bool force = false) override;
|
||||
void layoutCursorSubview(bool force);
|
||||
void useInsertionCursor();
|
||||
KDRect selectionRect() const;
|
||||
Poincare::LayoutCursor m_cursor;
|
||||
/* The insertion cursor is a secondary cursor that determines where text
|
||||
* should be inserted. Most of the time this cursor is useless (and is
|
||||
* therefore disabled), but in an interface where the user can navigate out
|
||||
* of the field, it's important to keep track of where inserted text should
|
||||
* go even if the main cursor was moved.
|
||||
* For instance, this is useful in the Calculation app when the user wants
|
||||
* to type a division and scroll up the history to insert something at the
|
||||
* denominator. */
|
||||
Poincare::LayoutCursor m_insertionCursor;
|
||||
ExpressionView m_expressionView;
|
||||
TextCursorView m_cursorView;
|
||||
/* The selection starts on the left of m_selectionStart, and ends on the
|
||||
@@ -100,6 +119,7 @@ private:
|
||||
bool m_isEditing;
|
||||
};
|
||||
ContentView m_contentView;
|
||||
Ion::Events::Event m_insertionCursorEvent;
|
||||
LayoutFieldDelegate * m_delegate;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,18 +31,11 @@ class View {
|
||||
friend class Shared::RoundCursorView;
|
||||
public:
|
||||
View() : m_frame(KDRectZero), m_superview(nullptr), m_dirtyRect(KDRectZero) {}
|
||||
virtual ~View() {
|
||||
for (int i = 0; i < numberOfSubviews(); i++) {
|
||||
View * subview = subviewAtIndex(i);
|
||||
if (subview != nullptr) {
|
||||
subview->m_superview = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
View(View&& other) = default;
|
||||
View(const View& other) = delete;
|
||||
View& operator=(const View& other) = delete;
|
||||
View& operator=(View&& other) = delete;
|
||||
|
||||
void resetSuperview() {
|
||||
m_superview = nullptr;
|
||||
}
|
||||
@@ -90,6 +83,12 @@ private:
|
||||
KDPoint absoluteOrigin() const;
|
||||
KDRect absoluteVisibleFrame() const;
|
||||
|
||||
/* At destruction, subviews aren't notified that their own pointer
|
||||
* 'm_superview' is outdated. This is not an issue since all view hierarchy
|
||||
* is created or destroyed at once: when the app is packed or unpacked. The
|
||||
* view and its subviews are then destroyed concomitantly.
|
||||
* Otherwise, we would just have to implement the destructor to notify
|
||||
* subviews that 'm_superview = nullptr'. */
|
||||
View * m_superview;
|
||||
KDRect m_dirtyRect;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ using namespace Poincare;
|
||||
|
||||
LayoutField::ContentView::ContentView() :
|
||||
m_cursor(),
|
||||
m_insertionCursor(),
|
||||
m_expressionView(0.0f, 0.5f, Palette::PrimaryText, Palette::BackgroundHard, &m_selectionStart, &m_selectionEnd),
|
||||
m_cursorView(),
|
||||
m_selectionStart(),
|
||||
@@ -30,12 +31,21 @@ bool LayoutField::ContentView::setEditing(bool isEditing) {
|
||||
m_expressionView.layout().invalidAllSizesPositionsAndBaselines();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// We're leaving the edition of the current layout
|
||||
useInsertionCursor();
|
||||
}
|
||||
layoutSubviews();
|
||||
markRectAsDirty(bounds());
|
||||
return false;
|
||||
}
|
||||
|
||||
void LayoutField::ContentView::useInsertionCursor() {
|
||||
if (m_insertionCursor.isDefined()) {
|
||||
m_cursor = m_insertionCursor;
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutField::ContentView::clearLayout() {
|
||||
HorizontalLayout h = HorizontalLayout::Builder();
|
||||
if (m_expressionView.setLayout(h)) {
|
||||
@@ -284,6 +294,11 @@ void LayoutField::setEditing(bool isEditing) {
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutField::clearLayout() {
|
||||
m_contentView.clearLayout(); // Replace the layout with an empty horizontal layout
|
||||
reloadScroll(); // Put the scroll to offset 0
|
||||
}
|
||||
|
||||
Context * LayoutField::context() const {
|
||||
return (m_delegate != nullptr) ? m_delegate->context() : nullptr;
|
||||
}
|
||||
@@ -318,6 +333,12 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool
|
||||
* - the text added after a toolbox selection
|
||||
* - the result of a copy-paste. */
|
||||
|
||||
/* This routing can be called even if no actual underlying event has been
|
||||
* dispatched on the LayoutField. For instance, when someone wants to insert
|
||||
* text in the field from the outside. In this scenario, let's make sure the
|
||||
* insertionCursor is invalidated. */
|
||||
m_contentView.invalidateInsertionCursor();
|
||||
|
||||
// Delete the selected layouts if needed
|
||||
deleteSelection();
|
||||
|
||||
@@ -384,6 +405,9 @@ bool LayoutField::handleEvent(Ion::Events::Event event) {
|
||||
KDSize previousSize = minimalSizeForOptimalDisplay();
|
||||
bool shouldRecomputeLayout = m_contentView.cursor()->showEmptyLayoutIfNeeded();
|
||||
bool moveEventChangedLayout = false;
|
||||
if (!eventShouldUpdateInsertionCursor(event)) {
|
||||
m_contentView.invalidateInsertionCursor();
|
||||
}
|
||||
if (privateHandleMoveEvent(event, &moveEventChangedLayout)) {
|
||||
if (!isEditing()) {
|
||||
setEditing(true);
|
||||
@@ -556,6 +580,9 @@ bool LayoutField::privateHandleMoveEvent(Ion::Events::Event event, bool * should
|
||||
LayoutCursor result;
|
||||
result = m_contentView.cursor()->cursorAtDirection(DirectionForMoveEvent(event), shouldRecomputeLayout);
|
||||
if (result.isDefined()) {
|
||||
if (eventShouldUpdateInsertionCursor(event)) {
|
||||
m_contentView.updateInsertionCursor();
|
||||
}
|
||||
m_contentView.setCursor(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,9 @@ KDRect ScrollView::visibleContentRect() {
|
||||
}
|
||||
|
||||
void ScrollView::layoutSubviews(bool force) {
|
||||
if (bounds().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
KDRect r1 = KDRectZero;
|
||||
KDRect r2 = KDRectZero;
|
||||
KDRect innerFrame = decorator()->layoutIndicators(minimalSizeForOptimalDisplay(), contentOffset(), bounds(), &r1, &r2, force);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <ion/unicode/utf8_helper.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/* ION is not your regular library. It is a library you link against, but it
|
||||
* will take care of configuring the whole environment for you. In POSIX terms,
|
||||
@@ -52,6 +53,9 @@ void decompress(const uint8_t * src, uint8_t * dst, int srcSize, int dstSize);
|
||||
// Tells whether the stack pointer is within acceptable bounds
|
||||
bool stackSafe();
|
||||
|
||||
// Collect registers in a buffer and returns the stack pointer
|
||||
uintptr_t collectRegisters(jmp_buf regs);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,8 +86,6 @@ public:
|
||||
uint32_t m_fullNameCRC32;
|
||||
};
|
||||
|
||||
Storage();
|
||||
|
||||
#if ION_STORAGE_LOG
|
||||
void log();
|
||||
#endif
|
||||
@@ -126,6 +124,8 @@ private:
|
||||
constexpr static uint32_t Magic = 0xEE0BDDBA;
|
||||
constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8);
|
||||
|
||||
Storage();
|
||||
|
||||
/* Getters/Setters on recordID */
|
||||
const char * fullNameOfRecord(const Record record);
|
||||
Record::ErrorStatus setFullNameOfRecord(const Record record, const char * fullName);
|
||||
|
||||
@@ -10,6 +10,8 @@ ifeq ($(EPSILON_TELEMETRY),1)
|
||||
ion_src += ion/src/shared/telemetry_console.cpp
|
||||
endif
|
||||
|
||||
ion_src += ion/src/shared/collect_registers.cpp
|
||||
|
||||
ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) -Iion/src/device/shared
|
||||
|
||||
$(call object_for,$(ion_device_src) $(ion_device_flasher_src) $(ion_device_bench_src)): SFLAGS += $(ION_DEVICE_SFLAGS)
|
||||
|
||||
19
ion/src/shared/collect_registers.cpp
Normal file
19
ion/src/shared/collect_registers.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <ion.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
namespace Ion {
|
||||
|
||||
/* Forbid inlining to ensure dummy to be at the top of the stack. Otherwise,
|
||||
* LTO inlining can make regs lower on the stack than some just-allocated
|
||||
* pointers. */
|
||||
__attribute__((noinline))uintptr_t collectRegisters(jmp_buf buf) {
|
||||
/* TODO: we use setjmp to get the registers values to look for python heap
|
||||
* root. However, the 'setjmp' does not guarantee that it gets all registers
|
||||
* values. We should check our setjmp implementation for the device and
|
||||
* ensure that it also works for other platforms. */
|
||||
setjmp(buf);
|
||||
int dummy;
|
||||
return (uintptr_t)&dummy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,10 +28,7 @@ constexpr char Storage::seqExtension[];
|
||||
constexpr char Storage::eqExtension[];
|
||||
|
||||
Storage * Storage::sharedStorage() {
|
||||
static Storage * storage = nullptr;
|
||||
if (storage == nullptr) {
|
||||
storage = new (staticStorageArea) Storage();
|
||||
}
|
||||
static Storage * storage = new (staticStorageArea) Storage();
|
||||
return storage;
|
||||
}
|
||||
|
||||
@@ -90,20 +87,6 @@ Storage::Record::Record(const char * basename, int basenameLength, const char *
|
||||
|
||||
// STORAGE
|
||||
|
||||
Storage::Storage() :
|
||||
m_magicHeader(Magic),
|
||||
m_buffer(),
|
||||
m_magicFooter(Magic),
|
||||
m_delegate(nullptr),
|
||||
m_lastRecordRetrieved(nullptr),
|
||||
m_lastRecordRetrievedPointer(nullptr)
|
||||
{
|
||||
assert(m_magicHeader == Magic);
|
||||
assert(m_magicFooter == Magic);
|
||||
// Set the size of the first record to 0
|
||||
overrideSizeAtPosition(m_buffer, 0);
|
||||
}
|
||||
|
||||
#if ION_STORAGE_LOG
|
||||
void Storage::log() {
|
||||
for (char * p : *this) {
|
||||
@@ -305,6 +288,22 @@ void Storage::destroyRecordsWithExtension(const char * extension) {
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE
|
||||
|
||||
Storage::Storage() :
|
||||
m_magicHeader(Magic),
|
||||
m_buffer(),
|
||||
m_magicFooter(Magic),
|
||||
m_delegate(nullptr),
|
||||
m_lastRecordRetrieved(nullptr),
|
||||
m_lastRecordRetrievedPointer(nullptr)
|
||||
{
|
||||
assert(m_magicHeader == Magic);
|
||||
assert(m_magicFooter == Magic);
|
||||
// Set the size of the first record to 0
|
||||
overrideSizeAtPosition(m_buffer, 0);
|
||||
}
|
||||
|
||||
const char * Storage::fullNameOfRecord(const Record record) {
|
||||
char * p = pointerOfRecord(record);
|
||||
if (p != nullptr) {
|
||||
|
||||
@@ -7,6 +7,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \
|
||||
dummy/language.cpp \
|
||||
)
|
||||
|
||||
ion_src += ion/src/shared/collect_registers.cpp
|
||||
|
||||
$(call object_for,ion/src/simulator/shared/main.cpp) : SFLAGS += -DEPSILON_SDL_FULLSCREEN=1
|
||||
|
||||
LDFLAGS += -ljnigraphics -llog
|
||||
|
||||
@@ -7,6 +7,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \
|
||||
dummy/callback.cpp \
|
||||
)
|
||||
|
||||
ion_src += ion/src/shared/collect_registers.cpp
|
||||
|
||||
$(call object_for,ion/src/simulator/shared/main.cpp) : SFLAGS += -DEPSILON_SDL_FULLSCREEN=1
|
||||
|
||||
ifeq ($(EPSILON_TELEMETRY),1)
|
||||
|
||||
@@ -15,6 +15,8 @@ ion_src += $(addprefix ion/src/simulator/linux/, \
|
||||
|
||||
ion_src += $(addprefix ion/src/simulator/shared/, \
|
||||
dummy/callback.cpp \
|
||||
collect_registers_x86_64.s \
|
||||
collect_registers.cpp \
|
||||
)
|
||||
|
||||
ifeq ($(EPSILON_TELEMETRY),1)
|
||||
|
||||
@@ -5,6 +5,8 @@ ion_src += $(addprefix ion/src/simulator/macos/, \
|
||||
ion_src += $(addprefix ion/src/simulator/shared/, \
|
||||
apple/language.m \
|
||||
dummy/callback.cpp \
|
||||
collect_registers_x86_64.s \
|
||||
collect_registers.cpp \
|
||||
)
|
||||
|
||||
ifeq ($(EPSILON_TELEMETRY),1)
|
||||
|
||||
19
ion/src/simulator/shared/collect_registers.cpp
Normal file
19
ion/src/simulator/shared/collect_registers.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <ion.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
// define in assembly code
|
||||
// Force the name as archs (linux/macos) don't mangle C names the same way
|
||||
extern uintptr_t collect_registers(uintptr_t * regs) asm ("_collect_registers");
|
||||
|
||||
}
|
||||
namespace Ion {
|
||||
|
||||
// Wrapper to avoid handling c++ name mangling when writing assembly code
|
||||
|
||||
uintptr_t collectRegisters(jmp_buf buf) {
|
||||
uintptr_t * regs = (uintptr_t *)buf;
|
||||
return collect_registers(regs);
|
||||
}
|
||||
|
||||
}
|
||||
25
ion/src/simulator/shared/collect_registers_x86_64.s
Normal file
25
ion/src/simulator/shared/collect_registers_x86_64.s
Normal file
@@ -0,0 +1,25 @@
|
||||
.text
|
||||
|
||||
.global _collect_registers
|
||||
|
||||
_collect_registers:
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %rbp
|
||||
pushq %rbx
|
||||
movq %rbx, (%rdi)
|
||||
movq %rbp, 8(%rdi)
|
||||
movq %r12, 16(%rdi)
|
||||
popq %rbx
|
||||
popq %rbp
|
||||
popq %r12
|
||||
movq %r13, 24(%rdi)
|
||||
movq %r14, 32(%rdi)
|
||||
popq %r13
|
||||
movq %r15, 40(%rdi)
|
||||
popq %r14
|
||||
popq %r15
|
||||
movq %rsp, %rax
|
||||
ret
|
||||
@@ -22,6 +22,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \
|
||||
dummy/language.cpp \
|
||||
)
|
||||
|
||||
ion_src += ion/src/shared/collect_registers.cpp
|
||||
|
||||
ifeq ($(EPSILON_TELEMETRY),1)
|
||||
ion_src += ion/src/simulator/shared/dummy/telemetry_init.cpp
|
||||
ion_src += ion/src/shared/telemetry_console.cpp
|
||||
|
||||
@@ -8,6 +8,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \
|
||||
dummy/callback.cpp \
|
||||
)
|
||||
|
||||
ion_src += ion/src/shared/collect_registers.cpp
|
||||
|
||||
ifeq ($(EPSILON_TELEMETRY),1)
|
||||
ion_src += ion/src/simulator/shared/dummy/telemetry_init.cpp
|
||||
ion_src += ion/src/shared/telemetry_console.cpp
|
||||
|
||||
@@ -9,6 +9,9 @@ public:
|
||||
m_width(width), m_height(height) {}
|
||||
constexpr KDCoordinate width() const { return m_width; }
|
||||
constexpr KDCoordinate height() const { return m_height; }
|
||||
bool operator==(const KDSize &other) const {
|
||||
return m_width == other.width() && m_height == other.height();
|
||||
}
|
||||
private:
|
||||
KDCoordinate m_width;
|
||||
KDCoordinate m_height;
|
||||
|
||||
@@ -174,5 +174,5 @@ KDRect KDRect::movedTo(KDPoint p) const {
|
||||
}
|
||||
|
||||
bool KDRect::isEmpty() const {
|
||||
return (width() == 0 || height() == 0);
|
||||
return (width() == 0 || height() == 0); //TODO <= 0
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef LIBA_SETJMP_H
|
||||
#define LIBA_SETJMP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "private/macros.h"
|
||||
|
||||
/* We are preseving registers:
|
||||
@@ -14,7 +15,7 @@
|
||||
|
||||
LIBA_BEGIN_DECLS
|
||||
|
||||
typedef int jmp_buf[31];
|
||||
typedef uintptr_t jmp_buf[31];
|
||||
void longjmp(jmp_buf env, int val);
|
||||
int setjmp(jmp_buf env);
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ public:
|
||||
* The result of the verification is *result. */
|
||||
static bool ExpressionMuAndVarAreOK(bool * result, const Expression & mu, const Expression & sigma, Context * context);
|
||||
private:
|
||||
/* For the standard normal distribution, P(X < y) > 0.9999995 for y >= 4.892 so the
|
||||
/* For the standard normal distribution, P(X < y) > 0.99999995 for y >= 5.33 so the
|
||||
* value displayed is 1. But this is dependent on the fact that we display
|
||||
* only 7 decimal values! */
|
||||
static_assert(Preferences::LargeNumberOfSignificantDigits == 7, "k_boundStandardNormalDistribution is ill-defined compared to LargeNumberOfSignificantDigits");
|
||||
constexpr static double k_boundStandardNormalDistribution = 4.892;
|
||||
constexpr static double k_boundStandardNormalDistribution = 5.33;
|
||||
template<typename T> static T StandardNormalCumulativeDistributiveFunctionAtAbscissa(T abscissa);
|
||||
template<typename T> static T StandardNormalCumulativeDistributiveInverseForProbability(T probability);
|
||||
};
|
||||
|
||||
@@ -88,8 +88,8 @@ T NormalDistribution::StandardNormalCumulativeDistributiveFunctionAtAbscissa(T a
|
||||
if (std::isnan(abscissa)) {
|
||||
return NAN;
|
||||
}
|
||||
if (std::isinf(abscissa) || abscissa > k_boundStandardNormalDistribution) {
|
||||
return (T)1.0;
|
||||
if (std::isinf(abscissa) || std::fabs(abscissa) > k_boundStandardNormalDistribution) {
|
||||
return abscissa > (T)0.0 ? (T)1.0 : (T)0.0;
|
||||
}
|
||||
if (abscissa == (T)0.0) {
|
||||
return (T)0.5;
|
||||
|
||||
@@ -303,6 +303,10 @@ QUIZ_CASE(poincare_approximation_function) {
|
||||
|
||||
assert_expression_approximates_to<float>("normcdf(1.2, 3.4, 31.36)", "0.3472125");
|
||||
assert_expression_approximates_to<double>("normcdf(1.2, 3.4, 31.36)", "3.4721249841587ᴇ-1");
|
||||
assert_expression_approximates_to<float>("normcdf(-1ᴇ99,3.4,31.36)", "0");
|
||||
assert_expression_approximates_to<float>("normcdf(1ᴇ99,3.4,31.36)", "1");
|
||||
assert_expression_approximates_to<float>("normcdf(-6,0,1)", "0");
|
||||
assert_expression_approximates_to<float>("normcdf(6,0,1)", "1");
|
||||
|
||||
assert_expression_approximates_to<float>("normcdf2(0.5, 3.6, 1.3, 11.56)", "0.3436388");
|
||||
assert_expression_approximates_to<double>("normcdf2(0.5, 3.6, 1.3, 11.56)", "3.4363881299147ᴇ-1");
|
||||
|
||||
@@ -487,6 +487,7 @@ Q(st)
|
||||
Q(hideturtle)
|
||||
Q(ht)
|
||||
Q(isvisible)
|
||||
Q(colormode)
|
||||
|
||||
// utime QSTRs
|
||||
Q(time)
|
||||
|
||||
@@ -1,33 +1,17 @@
|
||||
extern "C" {
|
||||
#include "modkandinsky.h"
|
||||
#include <py/objtuple.h>
|
||||
#include <py/runtime.h>
|
||||
}
|
||||
#include <kandinsky.h>
|
||||
#include <ion.h>
|
||||
#include "port.h"
|
||||
|
||||
static KDColor ColorForTuple(mp_obj_t tuple) {
|
||||
size_t len;
|
||||
mp_obj_t * elem;
|
||||
|
||||
mp_obj_get_array(tuple, &len, &elem);
|
||||
if (len != 3) {
|
||||
mp_raise_TypeError("color needs 3 components");
|
||||
}
|
||||
|
||||
return KDColor::RGB888(
|
||||
mp_obj_get_int(elem[0]),
|
||||
mp_obj_get_int(elem[1]),
|
||||
mp_obj_get_int(elem[2])
|
||||
);
|
||||
}
|
||||
|
||||
static mp_obj_t TupleForRGB(uint8_t r, uint8_t g, uint8_t b) {
|
||||
static mp_obj_t TupleForKDColor(KDColor c) {
|
||||
mp_obj_tuple_t * t = static_cast<mp_obj_tuple_t *>(MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)));
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(r);
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(g);
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(b);
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(c.red());
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(c.green());
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(c.blue());
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
|
||||
@@ -37,12 +21,18 @@ static mp_obj_t TupleForRGB(uint8_t r, uint8_t g, uint8_t b) {
|
||||
* the stackViewController and forces the window to redraw itself.
|
||||
* KDIonContext::sharedContext is set to the frame of the last object drawn. */
|
||||
|
||||
mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue) {
|
||||
return TupleForRGB(
|
||||
mp_obj_get_int(red),
|
||||
mp_obj_get_int(green),
|
||||
mp_obj_get_int(blue)
|
||||
);
|
||||
mp_obj_t modkandinsky_color(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t color;
|
||||
if (n_args == 1) {
|
||||
color = args[0];
|
||||
} else if (n_args == 2) {
|
||||
mp_raise_TypeError("color takes 1 or 3 arguments");
|
||||
return mp_const_none;
|
||||
} else {
|
||||
assert(n_args == 3);
|
||||
color = mp_obj_new_tuple(n_args, args);
|
||||
}
|
||||
return TupleForKDColor(MicroPython::ColorParser::ParseColor(color));
|
||||
}
|
||||
|
||||
/* Calling ExecutionEnvironment::displaySandbox() hides the console and switches
|
||||
@@ -54,22 +44,23 @@ mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y) {
|
||||
KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y));
|
||||
KDColor c;
|
||||
KDIonContext::sharedContext()->getPixel(point, &c);
|
||||
return TupleForRGB(c.red(), c.green(), c.blue());
|
||||
return TupleForKDColor(c);
|
||||
}
|
||||
|
||||
mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) {
|
||||
mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t input) {
|
||||
KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y));
|
||||
KDColor kdColor = ColorForTuple(color);
|
||||
KDColor kdColor = MicroPython::ColorParser::ParseColor(input);
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
KDIonContext::sharedContext()->setPixel(point, kdColor);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
//TODO Use good colors
|
||||
mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) {
|
||||
const char * text = mp_obj_str_get_str(args[0]);
|
||||
KDPoint point(mp_obj_get_int(args[1]), mp_obj_get_int(args[2]));
|
||||
KDColor textColor = (n_args >= 4) ? ColorForTuple(args[3]) : KDColorBlack;
|
||||
KDColor backgroundColor = (n_args >= 5) ? ColorForTuple(args[4]) : KDColorWhite;
|
||||
KDColor textColor = (n_args >= 4) ? MicroPython::ColorParser::ParseColor(args[3]) : KDColorBlack;
|
||||
KDColor backgroundColor = (n_args >= 5) ? MicroPython::ColorParser::ParseColor(args[4]) : KDColorWhite;
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
KDIonContext::sharedContext()->drawString(text, point, KDFont::LargeFont, textColor, backgroundColor);
|
||||
/* Before and after execution of "modkandinsky_draw_string",
|
||||
@@ -99,8 +90,7 @@ mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) {
|
||||
y = y - height;
|
||||
}
|
||||
KDRect rect(x, y, width, height);
|
||||
KDColor color = ColorForTuple(args[4]);
|
||||
|
||||
KDColor color = MicroPython::ColorParser::ParseColor(args[4]);
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
KDIonContext::sharedContext()->fillRect(rect, color);
|
||||
// Cf comment on modkandinsky_draw_string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <py/obj.h>
|
||||
|
||||
mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue);
|
||||
mp_obj_t modkandinsky_color(size_t n_args, const mp_obj_t *args);
|
||||
mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y);
|
||||
mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color);
|
||||
mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t *args);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "modkandinsky.h"
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_color_obj, modkandinsky_color);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_color_obj, 1, 3, modkandinsky_color);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(modkandinsky_get_pixel_obj, modkandinsky_get_pixel);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_set_pixel_obj, modkandinsky_set_pixel);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_string_obj, 3, 5, modkandinsky_draw_string);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
extern "C" {
|
||||
#include "modturtle.h"
|
||||
#include <py/gc.h>
|
||||
#include <py/objtuple.h>
|
||||
#include <py/runtime.h>
|
||||
}
|
||||
#include "turtle.h"
|
||||
#include "../../port.h"
|
||||
@@ -130,42 +132,54 @@ mp_obj_t modturtle_isdown() {
|
||||
return sTurtle.isPenDown() ? mp_const_true : mp_const_false;
|
||||
}
|
||||
|
||||
mp_float_t uint8tColorToDouble(uint8_t c) { return static_cast<double>(c)/255.0; }
|
||||
|
||||
mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
// pencolor()
|
||||
KDColor c = sTurtle.color();
|
||||
mp_obj_t mp_col[3];
|
||||
mp_col[0] = mp_obj_new_int_from_uint(c.red());
|
||||
mp_col[1] = mp_obj_new_int_from_uint(c.green());
|
||||
mp_col[2] = mp_obj_new_int_from_uint(c.blue());
|
||||
if(sTurtle.colorMode() == MicroPython::ColorParser::ColorMode::MaxIntensity255){
|
||||
mp_col[0] = mp_obj_new_int_from_uint(c.red());
|
||||
mp_col[1] = mp_obj_new_int_from_uint(c.green());
|
||||
mp_col[2] = mp_obj_new_int_from_uint(c.blue());
|
||||
} else {
|
||||
mp_col[0] = mp_obj_new_float(uint8tColorToDouble(c.red()));
|
||||
mp_col[1] = mp_obj_new_float(uint8tColorToDouble(c.green()));
|
||||
mp_col[2] = mp_obj_new_float(uint8tColorToDouble(c.blue()));
|
||||
}
|
||||
return mp_obj_new_tuple(3, mp_col);
|
||||
}
|
||||
if (n_args == 1) {
|
||||
if (MP_OBJ_IS_STR(args[0])) {
|
||||
// pencolor("blue")
|
||||
size_t l;
|
||||
sTurtle.setColor(mp_obj_str_get_data(args[0], &l));
|
||||
} else {
|
||||
// pencolor((r, g, b))
|
||||
mp_obj_t * rgb;
|
||||
mp_obj_get_array_fixed_n(args[0], 3, &rgb);
|
||||
sTurtle.setColor(
|
||||
KDColor::RGB888(
|
||||
mp_obj_get_int(rgb[0]),
|
||||
mp_obj_get_int(rgb[1]),
|
||||
mp_obj_get_int(rgb[2])));
|
||||
}
|
||||
} else if (n_args == 3) {
|
||||
// pencolor(r, g, b)
|
||||
sTurtle.setColor(
|
||||
KDColor::RGB888(
|
||||
mp_obj_get_int(args[0]),
|
||||
mp_obj_get_int(args[1]),
|
||||
mp_obj_get_int(args[2])));
|
||||
if (n_args == 2) {
|
||||
mp_raise_TypeError("pencolor() takes 0, 1 or 3 arguments");
|
||||
return mp_const_none;
|
||||
}
|
||||
mp_obj_t color;
|
||||
if (n_args == 1) {
|
||||
color = args[0];
|
||||
} else {
|
||||
assert(n_args == 3);
|
||||
color = mp_obj_new_tuple(n_args, args);
|
||||
}
|
||||
sTurtle.setColor(MicroPython::ColorParser::ParseColor(color, sTurtle.colorMode()));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 0){
|
||||
return mp_obj_new_int_from_uint(static_cast<int>(sTurtle.colorMode()));
|
||||
} else{
|
||||
int colorMode = mp_obj_get_int(args[0]);
|
||||
if (colorMode != static_cast<int>(MicroPython::ColorParser::ColorMode::MaxIntensity1) &&
|
||||
colorMode != static_cast<int>(MicroPython::ColorParser::ColorMode::MaxIntensity255)) {
|
||||
mp_raise_ValueError("Colormode can be 1 or 255");
|
||||
return mp_const_none;
|
||||
}
|
||||
sTurtle.setColorMode(static_cast<MicroPython::ColorParser::ColorMode>(colorMode));
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_showturtle() {
|
||||
sTurtle.setVisible(true);
|
||||
return mp_const_none;
|
||||
|
||||
@@ -25,6 +25,7 @@ mp_obj_t modturtle_pensize(size_t n_args, const mp_obj_t *args);
|
||||
mp_obj_t modturtle_isvisible();
|
||||
|
||||
mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args);
|
||||
mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args);
|
||||
|
||||
mp_obj_t modturtle_showturtle();
|
||||
mp_obj_t modturtle_hideturtle();
|
||||
|
||||
@@ -18,6 +18,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modturtle_pensize_obj, 0, 1, modturtl
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle_isdown_obj, modturtle_isdown);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modturtle_pencolor_obj, 0, 3, modturtle_pencolor);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modturtle_colormode_obj, 0, 1, modturtle_colormode);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle_reset_obj, modturtle_reset);
|
||||
|
||||
@@ -64,6 +65,7 @@ STATIC const mp_rom_map_elem_t modturtle_module_globals_table[] = {
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_color), (mp_obj_t)&modturtle_pencolor_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pencolor), (mp_obj_t)&modturtle_pencolor_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_colormode), (mp_obj_t)&modturtle_colormode_obj },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_reset), (mp_obj_t)&modturtle_reset_obj },
|
||||
|
||||
|
||||
@@ -175,27 +175,6 @@ void Turtle::setVisible(bool visible) {
|
||||
}
|
||||
}
|
||||
|
||||
void Turtle::setColor(const char * color) {
|
||||
constexpr NameColorPair pairs[] = {
|
||||
NameColorPair("blue", KDColorBlue),
|
||||
NameColorPair("red", KDColorRed),
|
||||
NameColorPair("green", Palette::Green),
|
||||
NameColorPair("yellow", KDColorYellow),
|
||||
NameColorPair("brown", Palette::Brown),
|
||||
NameColorPair("black", KDColorBlack),
|
||||
NameColorPair("white", KDColorWhite),
|
||||
NameColorPair("pink", Palette::Pink),
|
||||
NameColorPair("orange", Palette::Orange),
|
||||
NameColorPair("purple", Palette::Purple),
|
||||
NameColorPair("grey", Palette::GreyDark)
|
||||
};
|
||||
for (NameColorPair p : pairs) {
|
||||
if (strcmp(p.name(), color) == 0) {
|
||||
m_color = p.color();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Turtle::viewDidDisappear() {
|
||||
m_drawn = false;
|
||||
|
||||
@@ -8,6 +8,7 @@ extern "C" {
|
||||
#include <escher/metric.h>
|
||||
#include <kandinsky.h>
|
||||
#include <math.h>
|
||||
#include <python/port/port.h>
|
||||
|
||||
/* We check for keyboard interruptions using micropython_port_vm_hook_loop and
|
||||
* micropython_port_interruptible_msleep, but even if we catch an interruption,
|
||||
@@ -29,6 +30,7 @@ public:
|
||||
m_y(0),
|
||||
m_heading(0),
|
||||
m_color(k_defaultColor),
|
||||
m_colorMode(MicroPython::ColorParser::ColorMode::MaxIntensity255),
|
||||
m_penDown(true),
|
||||
m_visible(true),
|
||||
m_speed(k_defaultSpeed),
|
||||
@@ -71,7 +73,10 @@ public:
|
||||
void setColor(uint8_t r, uint8_t g, uint8_t b) {
|
||||
m_color = KDColor::RGB888(r, g, b);
|
||||
}
|
||||
void setColor(const char * color);
|
||||
MicroPython::ColorParser::ColorMode colorMode() const {return m_colorMode; }
|
||||
void setColorMode(MicroPython::ColorParser::ColorMode colorMode){
|
||||
m_colorMode = colorMode;
|
||||
}
|
||||
|
||||
void viewDidDisappear();
|
||||
|
||||
@@ -102,19 +107,6 @@ private:
|
||||
Forward = 2
|
||||
};
|
||||
|
||||
class NameColorPair {
|
||||
public:
|
||||
constexpr NameColorPair(const char * name, KDColor color) :
|
||||
m_name(name),
|
||||
m_color(color)
|
||||
{}
|
||||
const char * name() const { return m_name; }
|
||||
KDColor color() const { return m_color; }
|
||||
private:
|
||||
const char * m_name;
|
||||
KDColor m_color;
|
||||
};
|
||||
|
||||
void setHeadingPrivate(mp_float_t angle);
|
||||
KDPoint position(mp_float_t x, mp_float_t y) const;
|
||||
KDPoint position() const { return position(m_x, m_y); }
|
||||
@@ -149,6 +141,7 @@ private:
|
||||
mp_float_t m_heading;
|
||||
|
||||
KDColor m_color;
|
||||
MicroPython::ColorParser::ColorMode m_colorMode;
|
||||
bool m_penDown;
|
||||
bool m_visible;
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
#include "port.h"
|
||||
|
||||
#include <ion/keyboard.h>
|
||||
#include <ion.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/* py/parsenum.h is a C header which uses C keyword restrict.
|
||||
* It does not exist in C++ so we define it here in order to be able to include
|
||||
* py/parsenum.h header. */
|
||||
#ifdef __cplusplus
|
||||
#define restrict // disable
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include "py/builtin.h"
|
||||
#include "py/compile.h"
|
||||
@@ -15,6 +22,7 @@ extern "C" {
|
||||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/nlr.h"
|
||||
#include "py/parsenum.h"
|
||||
#include "py/repl.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/stackctrl.h"
|
||||
@@ -23,6 +31,8 @@ extern "C" {
|
||||
#include "mod/matplotlib/pyplot/modpyplot.h"
|
||||
}
|
||||
|
||||
#include <escher/palette.h>
|
||||
|
||||
static MicroPython::ScriptProvider * sScriptProvider = nullptr;
|
||||
static MicroPython::ExecutionEnvironment * sCurrentExecutionEnvironment = nullptr;
|
||||
|
||||
@@ -145,85 +155,130 @@ void MicroPython::registerScriptProvider(ScriptProvider * s) {
|
||||
}
|
||||
|
||||
void MicroPython::collectRootsAtAddress(char * address, int byteLength) {
|
||||
#if __EMSCRIPTEN__
|
||||
// All objects are aligned, as asserted.
|
||||
/* All addresses stored on the stack are aligned on sizeof(void *), as
|
||||
* asserted. This is a consequence of the alignment requirements of compilers
|
||||
* (Cf http://www.catb.org/esr/structure-packing/). */
|
||||
assert(((unsigned long)address) % ((unsigned long)sizeof(void *)) == 0);
|
||||
assert(byteLength % sizeof(void *) == 0);
|
||||
gc_collect_root((void **)address, byteLength / sizeof(void *));
|
||||
#else
|
||||
for (size_t i = 0; i < sizeof(void *); i++) {
|
||||
/* Objects on the stack are not necessarily aligned on sizeof(void *),
|
||||
* which is also true for pointers refering to the heap. MicroPython
|
||||
* gc_collect_root expects a table of void * that will be scanned every
|
||||
* sizeof(void *) step. So we have to scan the stack repetitively with a
|
||||
* increasing offset to be sure to check every byte for a heap address.
|
||||
* If some memory can be reinterpreted as a pointer in the heap, gc_collect_root
|
||||
* will prevent the destruction of the pointed heap memory. At worst (if
|
||||
* the interpreted pointer was in fact an unaligned object or uninitialized
|
||||
* memory), we will just keep extra objects in the heap which is not optimal
|
||||
* but does not cause any crash. */
|
||||
char * addressWithOffset = address + i;
|
||||
// Ensure to round the length to the ceiling
|
||||
size_t lengthInAddressSize = (byteLength - i + sizeof(void *) - 1)/sizeof(void *);
|
||||
gc_collect_root((void **)addressWithOffset, lengthInAddressSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gc_collect(void) {
|
||||
KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode){
|
||||
static constexpr int maxColorIntensity = static_cast<int>(ColorMode::MaxIntensity255);
|
||||
if (mp_obj_is_str(input)) {
|
||||
size_t l;
|
||||
const char * color = mp_obj_str_get_data(input, &l);
|
||||
// TODO add cyan
|
||||
constexpr NameColorPair pairs[] = {
|
||||
NameColorPair("blue", KDColorBlue),
|
||||
NameColorPair("b", KDColorBlue),
|
||||
NameColorPair("red", KDColorRed),
|
||||
NameColorPair("r", KDColorRed),
|
||||
NameColorPair("green", Palette::Green),
|
||||
NameColorPair("g", Palette::Green),
|
||||
NameColorPair("yellow", KDColorYellow),
|
||||
NameColorPair("y", KDColorYellow),
|
||||
NameColorPair("brown", Palette::Brown),
|
||||
NameColorPair("black", KDColorBlack),
|
||||
NameColorPair("k", KDColorBlack),
|
||||
NameColorPair("white", KDColorWhite),
|
||||
NameColorPair("w", KDColorWhite),
|
||||
NameColorPair("pink", Palette::Pink),
|
||||
NameColorPair("orange", Palette::Orange),
|
||||
NameColorPair("purple", Palette::Purple),
|
||||
NameColorPair("grey", Palette::GreyDark)
|
||||
};
|
||||
for (NameColorPair p : pairs) {
|
||||
if (strcmp(p.name(), color) == 0) {
|
||||
return p.color();
|
||||
}
|
||||
}
|
||||
|
||||
if (color[0] == '#') {
|
||||
// TODO handle #abc as #aabbcc (see matplotlib spec)
|
||||
if (l != 7) {
|
||||
mp_raise_ValueError("RGB hex values are 6 bytes long");
|
||||
}
|
||||
uint32_t colorInt = mp_obj_get_int(mp_parse_num_integer(color+1, strlen(color+1), 16, NULL));
|
||||
return KDColor::RGB24(colorInt);
|
||||
}
|
||||
|
||||
mp_float_t greyLevel = mp_obj_float_get(mp_parse_num_decimal(color, strlen(color), false, false, NULL));
|
||||
if (greyLevel >= 0.0 && greyLevel <= 1.0) {
|
||||
uint8_t color = maxColorIntensity * (float) greyLevel;
|
||||
return KDColor::RGB888(color, color, color);
|
||||
}
|
||||
mp_raise_ValueError("Grey levels are between 0.0 and 1.0");
|
||||
} else if(mp_obj_is_int(input)) {
|
||||
mp_raise_TypeError("Int are not colors");
|
||||
//See https://github.com/numworks/epsilon/issues/1533#issuecomment-618443492
|
||||
} else {
|
||||
size_t len;
|
||||
mp_obj_t * elem;
|
||||
|
||||
mp_obj_get_array(input, &len, &elem);
|
||||
|
||||
if (len != 3) {
|
||||
mp_raise_TypeError("Color needs 3 components");
|
||||
}
|
||||
int intensityFactor = maxColorIntensity/static_cast<int>(ColorMode);
|
||||
return KDColor::RGB888(
|
||||
intensityFactor * mp_obj_get_float(elem[0]),
|
||||
intensityFactor * mp_obj_get_float(elem[1]),
|
||||
intensityFactor * mp_obj_get_float(elem[2])
|
||||
);
|
||||
}
|
||||
mp_raise_TypeError("Color couldn't be parsed");
|
||||
}
|
||||
|
||||
void gc_collect_regs_and_stack(void) {
|
||||
// get the registers and the sp
|
||||
jmp_buf regs;
|
||||
uintptr_t sp = Ion::collectRegisters(regs);
|
||||
|
||||
void * python_stack_top = MP_STATE_THREAD(stack_top);
|
||||
assert(python_stack_top != NULL);
|
||||
|
||||
gc_collect_start();
|
||||
|
||||
modturtle_gc_collect();
|
||||
modpyplot_gc_collect();
|
||||
|
||||
/* get the registers.
|
||||
* regs is the also the last object on the stack so the stack is bound by
|
||||
* ®s and python_stack_top. */
|
||||
jmp_buf regs;
|
||||
/* TODO: we use setjmp to get the registers values to look for python heap
|
||||
* root. However, the 'setjmp' does not guarantee that it gets all registers
|
||||
* values. We should check our setjmp implementation for the device and
|
||||
* ensure that it also works for other platforms. */
|
||||
setjmp(regs);
|
||||
|
||||
void **regs_ptr = (void**)®s;
|
||||
|
||||
/* On the device, the stack is stored in reverse order, but it might not be
|
||||
* the case on a computer. We thus have to take the absolute value of the
|
||||
* addresses difference. */
|
||||
size_t stackLengthInByte;
|
||||
void ** scanStart;
|
||||
if ((uintptr_t)python_stack_top > (uintptr_t)regs_ptr) {
|
||||
if ((uintptr_t)python_stack_top > sp) {
|
||||
|
||||
/* To compute the stack length:
|
||||
* regs
|
||||
* registers
|
||||
* <----------->
|
||||
* STACK <- ...| | | | | |--|--|--|--| | | | | | |
|
||||
* ^®s ^python_stack_top
|
||||
* ^sp ^python_stack_top
|
||||
* */
|
||||
|
||||
stackLengthInByte = (uintptr_t)python_stack_top - (uintptr_t)regs_ptr;
|
||||
scanStart = regs_ptr;
|
||||
stackLengthInByte = (uintptr_t)python_stack_top - sp;
|
||||
scanStart = (void **)sp;
|
||||
|
||||
} else {
|
||||
|
||||
/* When computing the stack length, take into account regs' size.
|
||||
* regs
|
||||
* registers
|
||||
* <----------->
|
||||
* STACK -> | | | | | | | | | | | |--|--|--|--| | | |...
|
||||
* ^python_stack_top ^®s
|
||||
* ^python_stack_top ^sp
|
||||
* */
|
||||
|
||||
stackLengthInByte = (uintptr_t)regs_ptr - (uintptr_t)python_stack_top + sizeof(regs);
|
||||
stackLengthInByte = sp - (uintptr_t)python_stack_top + sizeof(regs);
|
||||
scanStart = (void **)python_stack_top;
|
||||
|
||||
}
|
||||
/* Memory error detectors might find an error here as they might split regs
|
||||
* and stack memory zones. */
|
||||
MicroPython::collectRootsAtAddress((char *)scanStart, stackLengthInByte);
|
||||
}
|
||||
|
||||
void gc_collect(void) {
|
||||
gc_collect_start();
|
||||
modturtle_gc_collect();
|
||||
modpyplot_gc_collect();
|
||||
gc_collect_regs_and_stack();
|
||||
gc_collect_end();
|
||||
}
|
||||
|
||||
@@ -260,3 +315,4 @@ const char * mp_hal_input(const char * prompt) {
|
||||
assert(sCurrentExecutionEnvironment != nullptr);
|
||||
return sCurrentExecutionEnvironment->inputText(prompt);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
extern "C" {
|
||||
#include <stddef.h>
|
||||
#include <py/obj.h>
|
||||
}
|
||||
#include <escher/view_controller.h>
|
||||
|
||||
|
||||
namespace MicroPython {
|
||||
|
||||
class ScriptProvider {
|
||||
@@ -39,6 +41,31 @@ void deinit();
|
||||
void registerScriptProvider(ScriptProvider * s);
|
||||
void collectRootsAtAddress(char * address, int len);
|
||||
|
||||
class ColorParser {
|
||||
private:
|
||||
class NameColorPair {
|
||||
public:
|
||||
constexpr NameColorPair(const char * name, KDColor color) :
|
||||
m_name(name),
|
||||
m_color(color)
|
||||
{}
|
||||
const char * name() const { return m_name; }
|
||||
KDColor color() const { return m_color; }
|
||||
private:
|
||||
const char * m_name;
|
||||
KDColor m_color;
|
||||
};
|
||||
|
||||
public:
|
||||
enum class ColorMode {
|
||||
MaxIntensity1 = 1,
|
||||
MaxIntensity255 = 255,
|
||||
};
|
||||
|
||||
static KDColor ParseColor(mp_obj_t input, ColorMode ColorMode = ColorMode::MaxIntensity255);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user