[apps] Redesign Probability Calculation page

This commit is contained in:
Émilie Feral
2017-12-21 17:34:15 +01:00
committed by EmilieNumworks
parent e8793c429c
commit fd06d428e5
16 changed files with 623 additions and 555 deletions

View File

@@ -9,9 +9,10 @@ app_objs += $(addprefix apps/probability/,\
calculation/right_integral_calculation.o\
calculation/finite_integral_calculation.o\
calculation_controller.o\
calculation_view.o\
calculation_cell.o\
calculation_type_controller.o\
cell.o\
image_table_view.o\
image_cell.o\
law/binomial_law.o\
law/exponential_law.o\
law/law.o\
@@ -23,6 +24,7 @@ app_objs += $(addprefix apps/probability/,\
law_controller.o\
law_curve_view.o\
parameters_controller.o\
responder_image_cell.o\
)
i18n_files += $(addprefix apps/probability/,\

View File

@@ -0,0 +1,75 @@
#include "calculation_cell.h"
#include "responder_image_cell.h"
#include "../i18n.h"
#include <assert.h>
namespace Probability {
CalculationCell::CalculationCell(Responder * parentResponder, char * draftTextBuffer, TextFieldDelegate * textFieldDelegate) :
m_text(KDText::FontSize::Large, I18n::Message::Default, 0.5f, 0.5f),
m_calculation(parentResponder, textFieldDelegate, draftTextBuffer),
m_isResponder(true)
{
}
Responder * CalculationCell::responder() {
if (m_isResponder) {
return &m_calculation;
}
return nullptr;
}
void CalculationCell::setResponder(bool shouldBeResponder) {
m_isResponder = shouldBeResponder;
}
void CalculationCell::setHighlighted(bool highlight) {
HighlightCell::setHighlighted(highlight);
m_calculation.setHighlighted(highlight);
}
KDSize CalculationCell::minimalSizeForOptimalDisplay() const {
KDSize textSize = m_text.minimalSizeForOptimalDisplay();
return KDSize(2*k_margin+textSize.width()+calculationCellWidth()+ResponderImageCell::k_outline, KDText::charSize().height());
}
void CalculationCell::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(bounds(), KDColorWhite);
if (m_isResponder) {
KDSize textSize = m_text.minimalSizeForOptimalDisplay();
ctx->strokeRect(KDRect(2*k_margin+textSize.width()-ResponderImageCell::k_outline, ResponderImageCell::k_margin, calculationCellWidth()+2*ResponderImageCell::k_outline, ImageCell::k_height+2*ResponderImageCell::k_outline), Palette::GreyMiddle);
}
}
EditableTextCell * CalculationCell::editableTextCell() {
return &m_calculation;
}
MessageTextView * CalculationCell::messageTextView() {
return &m_text;
}
int CalculationCell::numberOfSubviews() const {
return 2;
}
View * CalculationCell::subviewAtIndex(int index) {
assert(index >= 0 && index < 2);
if (index == 0) {
return &m_text;
}
return &m_calculation;
}
void CalculationCell::layoutSubviews() {
KDSize textSize = m_text.minimalSizeForOptimalDisplay();
m_text.setFrame(KDRect(k_margin, 0, textSize.width(), bounds().height()));
m_calculation.setFrame(KDRect(2*k_margin+textSize.width(), ResponderImageCell::k_totalMargin, calculationCellWidth(), ImageCell::k_height));
}
KDCoordinate CalculationCell::calculationCellWidth() const {
KDCoordinate calculationCellWidth = m_calculation.minimalSizeForOptimalDisplay().width();
return min(k_maxTextFieldWidth, max(k_minTextFieldWidth, calculationCellWidth));
}
}

View File

@@ -0,0 +1,33 @@
#ifndef PROBABILITY_CALCULATION_CELL_H
#define PROBABILITY_CALCULATION_CELL_H
#include <escher.h>
namespace Probability {
class CalculationCell : public HighlightCell {
public:
CalculationCell(Responder * parentResponder = nullptr, char * draftTextBuffer = nullptr, TextFieldDelegate * textFieldDelegate = nullptr);
Responder * responder() override;
void setResponder(bool shouldbeResponder);
void setHighlighted(bool highlight) override;
KDSize minimalSizeForOptimalDisplay() const override;
void drawRect(KDContext * ctx, KDRect rect) const override;
EditableTextCell * editableTextCell();
MessageTextView * messageTextView();
private:
constexpr static KDCoordinate k_margin = 5;
constexpr static KDCoordinate k_minTextFieldWidth = 4*KDText::charSize().width();
constexpr static KDCoordinate k_maxTextFieldWidth = 10*KDText::charSize().width();
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
KDCoordinate calculationCellWidth() const;
MessageTextView m_text;
EditableTextCell m_calculation;
bool m_isResponder;
};
}
#endif

View File

@@ -6,6 +6,14 @@
#include "calculation/left_integral_calculation.h"
#include "calculation/right_integral_calculation.h"
#include "calculation/finite_integral_calculation.h"
#include "images/calcul1_icon.h"
#include "images/calcul2_icon.h"
#include "images/calcul3_icon.h"
#include "images/calcul4_icon.h"
#include "images/focused_calcul1_icon.h"
#include "images/focused_calcul2_icon.h"
#include "images/focused_calcul3_icon.h"
#include "images/focused_calcul4_icon.h"
#include <assert.h>
#include <cmath>
@@ -14,9 +22,9 @@ using namespace Shared;
namespace Probability {
CalculationController::ContentView::ContentView(Responder * parentResponder, CalculationController * calculationController, Calculation * calculation, Law * law) :
CalculationController::ContentView::ContentView(SelectableTableView * selectableTableView, Law * law, Calculation * calculation) :
m_titleView(KDText::FontSize::Small, I18n::Message::ComputeProbability, 0.5f, 0.5f, Palette::GreyDark, Palette::WallScreen),
m_calculationView(parentResponder, calculationController, calculation, law),
m_selectableTableView(selectableTableView),
m_lawCurveView(law, calculation)
{
}
@@ -33,25 +41,45 @@ View * CalculationController::ContentView::subviewAtIndex(int index) {
if (index == 1) {
return &m_lawCurveView;
}
return &m_calculationView;
return m_selectableTableView;
}
void CalculationController::ContentView::layoutSubviews() {
KDCoordinate titleHeight = KDText::charSize(KDText::FontSize::Small).height()+k_titleHeightMargin;
m_titleView.setFrame(KDRect(0, 0, bounds().width(), titleHeight));
KDCoordinate calculationHeight = m_calculationView.minimalSizeForOptimalDisplay().height();
m_calculationView.setFrame(KDRect(0, titleHeight, bounds().width(), bounds().height()-titleHeight));
KDCoordinate calculationHeight = ResponderImageCell::k_oneCellHeight;
m_selectableTableView->setFrame(KDRect(0, titleHeight, bounds().width(), calculationHeight));
m_lawCurveView.setFrame(KDRect(0, titleHeight+calculationHeight, bounds().width(), bounds().height() - calculationHeight - titleHeight));
}
CalculationController::CalculationController(Responder * parentResponder, Law * law, Calculation * calculation) :
ViewController(parentResponder),
m_contentView(this, this, calculation, law),
m_contentView(&m_selectableTableView, law, calculation),
m_selectableTableView(this, this, 0, 0, 0, 0, 0, 0, this, nullptr, false, true, KDColorWhite),
m_imageCell(&m_selectableTableView, law, calculation, this),
m_draftTextBuffer{},
m_calculation(calculation),
m_law(law)
{
assert(law != nullptr);
assert(calculation != nullptr);
for (int i = 0; i < k_numberOfCalculationCells; i++) {
m_calculationCells[i].editableTextCell()->setParentResponder(&m_selectableTableView);
m_calculationCells[i].editableTextCell()->textField()->setDelegate(this);
m_calculationCells[i].editableTextCell()->textField()->setDraftTextBuffer(m_draftTextBuffer);
}
}
void CalculationController::didEnterResponderChain(Responder * previousResponder) {
App::Snapshot * snapshot = (App::Snapshot *)app()->snapshot();
snapshot->setActivePage(App::Snapshot::Page::Calculations);
updateTitle();
reloadLawCurveView();
m_selectableTableView.reloadData();
}
void CalculationController::didBecomeFirstResponder() {
app()->setFirstResponder(&m_selectableTableView);
}
View * CalculationController::view() {
@@ -62,12 +90,157 @@ const char * CalculationController::title() {
return m_titleBuffer;
}
void CalculationController::viewWillAppear() {
ViewController::viewWillAppear();
selectCellAtLocation(1, 0);
}
void CalculationController::viewDidDisappear() {
m_selectableTableView.deselectTable();
ViewController::viewDidDisappear();
}
int CalculationController::numberOfRows() {
return 1;
}
int CalculationController::numberOfColumns() {
return m_calculation->numberOfParameters()+1;
}
KDCoordinate CalculationController::columnWidth(int i) {
if (i == 0) {
return m_imageCell.minimalSizeForOptimalDisplay().width();
}
return m_calculationCells[i-1].minimalSizeForOptimalDisplay().width();
}
KDCoordinate CalculationController::rowHeight(int j) {
return ResponderImageCell::k_oneCellHeight;
}
KDCoordinate CalculationController::cumulatedWidthFromIndex(int j) {
int result = 0;
for (int k = 0; k < j; k++) {
result += columnWidth(k);
}
return result;
}
int CalculationController::indexFromCumulatedWidth(KDCoordinate offsetX) {
int result = 0;
int i = 0;
while (result < offsetX && i < numberOfColumns()) {
result += columnWidth(i++);
}
return (result < offsetX || offsetX == 0) ? i : i - 1;
}
KDCoordinate CalculationController::cumulatedHeightFromIndex(int j) {
return rowHeight(0) * j;
}
int CalculationController::indexFromCumulatedHeight(KDCoordinate offsetY) {
KDCoordinate height = rowHeight(0);
if (height == 0) {
return 0;
}
return (offsetY - 1) / height;
}
HighlightCell * CalculationController::reusableCell(int index, int type) {
if (type == 0) {
assert(index == 0);
return &m_imageCell;
}
assert(index >= 0 && index < k_numberOfCalculationCells);
return &m_calculationCells[index];
}
int CalculationController::reusableCellCount(int type) {
if (type == 0) {
return 1;
}
return k_numberOfCalculationCells;
}
int CalculationController::typeAtLocation(int i, int j) {
if (i == 0 && j == 0) {
return 0;
}
return 1;
}
void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
if (i == 0) {
ResponderImageCell * myCell = static_cast<ResponderImageCell *>(cell);
const Image * images[CalculationTypeController::k_numberOfImages] = {ImageStore::Calcul1Icon, ImageStore::Calcul2Icon, ImageStore::Calcul3Icon, ImageStore::Calcul4Icon};
const Image * focusedImages[CalculationTypeController::k_numberOfImages] = {ImageStore::FocusedCalcul1Icon, ImageStore::FocusedCalcul2Icon, ImageStore::FocusedCalcul3Icon, ImageStore::FocusedCalcul4Icon};
myCell->setImage(images[(int)m_calculation->type()], focusedImages[(int)m_calculation->type()]);
} else {
CalculationCell * myCell = static_cast<CalculationCell *>(cell);
myCell->messageTextView()->setMessage(m_calculation->legendForParameterAtIndex(i-1));
bool calculationCellIsResponder = true;
if ((m_law->type() != Law::Type::Normal && i == 3) || (m_calculation->type() == Calculation::Type::Discrete && i == 2)) {
calculationCellIsResponder = false;
}
myCell->setResponder(calculationCellIsResponder);
TextField * field = static_cast<CalculationCell *>(cell)->editableTextCell()->textField();
if (field->isEditing()) {
return;
}
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
Complex<double>::convertFloatToText(m_calculation->parameterAtIndex(i-1), buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal);
field->setText(buffer);
}
}
bool CalculationController::textFieldDidHandleEvent(::TextField * textField, Ion::Events::Event event, bool returnValue, bool textHasChanged) {
if (returnValue && textHasChanged) {
m_selectableTableView.reloadData(); //TODO: optimize with reloadCell at index?
}
return returnValue;
}
bool CalculationController::textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) {
return TextFieldDelegate::textFieldShouldFinishEditing(textField, event)
|| (event == Ion::Events::Right && textField->cursorLocation() == textField->draftTextLength() && selectedColumn() < m_calculation->numberOfParameters())
|| (event == Ion::Events::Left && textField->cursorLocation() == 0);
}
bool CalculationController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
App * probaApp = (App *)app();
Context * globalContext = probaApp->container()->globalContext();
double floatBody = Expression::approximateToScalar<double>(text, *globalContext);
if (std::isnan(floatBody) || std::isinf(floatBody)) {
app()->displayWarning(I18n::Message::UndefinedValue);
return false;
}
if (m_calculation->type() != Calculation::Type::FiniteIntegral && selectedColumn() == 2) {
if (floatBody < 0.0) {
floatBody = 0.0;
}
if (floatBody > 1.0) {
floatBody = 1.0;
}
}
if (!m_law->isContinuous() && (selectedColumn() == 1 || m_calculation->type() == Calculation::Type::FiniteIntegral)) {
floatBody = std::round(floatBody);
}
m_calculation->setParameterAtIndex(floatBody, selectedColumn()-1);
if (event == Ion::Events::Right || event == Ion::Events::Left) {
m_selectableTableView.handleEvent(event);
}
reload();
return true;
}
void CalculationController::reloadLawCurveView() {
m_contentView.lawCurveView()->reload();
}
void CalculationController::reload() {
m_contentView.calculationView()->reload();
m_selectableTableView.reloadData();
reloadLawCurveView();
}
@@ -93,22 +266,10 @@ void CalculationController::setCalculationAccordingToIndex(int index, bool force
return;
}
m_calculation->setLaw(m_law);
reload();
}
void CalculationController::viewWillAppear() {
reloadLawCurveView();
m_contentView.calculationView()->selectSubview(1);
}
void CalculationController::didEnterResponderChain(Responder * previousResponder) {
App::Snapshot * snapshot = (App::Snapshot *)app()->snapshot();
snapshot->setActivePage(App::Snapshot::Page::Calculations);
updateTitle();
}
void CalculationController::didBecomeFirstResponder() {
app()->setFirstResponder(m_contentView.calculationView());
TextFieldDelegateApp * CalculationController::textFieldDelegateApp() {
return (App *)app();
}
void CalculationController::updateTitle() {

View File

@@ -4,44 +4,72 @@
#include <escher.h>
#include "law/law.h"
#include "law_curve_view.h"
#include "calculation_view.h"
#include "calculation_cell.h"
#include "responder_image_cell.h"
#include "calculation/calculation.h"
#include "../shared/parameter_text_field_delegate.h"
namespace Probability {
class CalculationController : public ViewController {
class CalculationController : public ViewController, public TableViewDataSource, public SelectableTableViewDataSource, public Shared::ParameterTextFieldDelegate {
public:
CalculationController(Responder * parentResponder, Law * law, Calculation * calculation);
View * view() override;
const char * title() override;
void reload();
void reloadLawCurveView();
void setCalculationAccordingToIndex(int index, bool forceReinitialisation = false);
void viewWillAppear() override;
/* Responder */
void didEnterResponderChain(Responder * previousResponder) override;
void didBecomeFirstResponder() override;
/* ViewController */
View * view() override;
const char * title() override;
void viewWillAppear() override;
void viewDidDisappear() override;
/* TableViewDataSource */
int numberOfRows() override;
int numberOfColumns() override;
KDCoordinate columnWidth(int i) override;
KDCoordinate rowHeight(int j) override;
KDCoordinate cumulatedWidthFromIndex(int i) override;
KDCoordinate cumulatedHeightFromIndex(int j) override;
int indexFromCumulatedWidth(KDCoordinate offsetX) override;
int indexFromCumulatedHeight(KDCoordinate offsetY) override;
HighlightCell * reusableCell(int index, int type) override;
int reusableCellCount(int type) override;
int typeAtLocation(int i, int j) override;
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
/* TextField delegate */
bool textFieldDidHandleEvent(TextField * textField, Ion::Events::Event event, bool returnValue, bool textHasChanged) override;
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
void reloadLawCurveView();
void reload();
void setCalculationAccordingToIndex(int index, bool forceReinitialisation = false);
private:
constexpr static int k_numberOfCalculationCells = 3;
Shared::TextFieldDelegateApp * textFieldDelegateApp() override;
void updateTitle();
class ContentView : public View {
public:
ContentView(Responder * parentResponder, CalculationController * calculationController, Calculation * Calculation, Law * law);
ContentView(SelectableTableView * selectableTableView, Law * law, Calculation * calculation);
LawCurveView * lawCurveView() {
return &m_lawCurveView;
}
CalculationView * calculationView() {
return &m_calculationView;
}
private:
constexpr static KDCoordinate k_titleHeightMargin = 5;
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
MessageTextView m_titleView;
CalculationView m_calculationView;
SelectableTableView * m_selectableTableView;
LawCurveView m_lawCurveView;
};
ContentView m_contentView;
SelectableTableView m_selectableTableView;
ResponderImageCell m_imageCell;
char m_draftTextBuffer[TextField::maxBufferSize()];
CalculationCell m_calculationCells[k_numberOfCalculationCells];
Calculation * m_calculation;
Law * m_law;
constexpr static int k_maxNumberOfTitleCharacters = 30;

View File

@@ -0,0 +1,91 @@
#include "calculation_type_controller.h"
#include <assert.h>
#include "app.h"
#include "images/calcul1_icon.h"
#include "images/calcul2_icon.h"
#include "images/calcul3_icon.h"
#include "images/calcul4_icon.h"
#include "images/focused_calcul1_icon.h"
#include "images/focused_calcul2_icon.h"
#include "images/focused_calcul3_icon.h"
#include "images/focused_calcul4_icon.h"
namespace Probability {
CalculationTypeController::CalculationTypeController(Responder * parentResponder, Law * law, Calculation * calculation, CalculationController * calculationController) :
ViewController(parentResponder),
m_selectableTableView(this, this, 0, 0, 0, 0, 0, 0, this, nullptr, false, false),
m_law(law),
m_calculation(calculation),
m_calculationController(calculationController)
{
assert(m_calculation != nullptr);
}
View * CalculationTypeController::view() {
return &m_selectableTableView;
}
void CalculationTypeController::viewWillAppear() {
selectCellAtLocation(0, (int)m_calculation->type());
}
void CalculationTypeController::viewDidDisappear() {
m_selectableTableView.deselectTable();
}
void CalculationTypeController::didBecomeFirstResponder() {
app()->setFirstResponder(&m_selectableTableView);
}
bool CalculationTypeController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
m_calculationController->setCalculationAccordingToIndex(selectedRow());
m_calculationController->reload();
app()->dismissModalViewController();
return true;
}
if (event == Ion::Events::Back || event == Ion::Events::Right) {
if (event == Ion::Events::Right) {
m_calculationController->selectCellAtLocation(1,0);
}
app()->dismissModalViewController();
return true;
}
return false;
}
int CalculationTypeController::numberOfRows() {
if (m_law->isContinuous()) {
return k_numberOfImages-1;
}
return k_numberOfImages;
}
KDCoordinate CalculationTypeController::cellWidth() {
return ImageCell::k_width;
}
KDCoordinate CalculationTypeController::cellHeight() {
return ImageCell::k_height;
}
HighlightCell * CalculationTypeController::reusableCell(int index) {
assert(index >= 0);
assert(index < k_numberOfImages);
return &m_imageCells[index];
}
int CalculationTypeController::reusableCellCount() {
return k_numberOfImages;
}
void CalculationTypeController::willDisplayCellForIndex(HighlightCell * cell, int index) {
ImageCell * myCell = (ImageCell *)cell;
const Image * images[k_numberOfImages] = {ImageStore::Calcul1Icon, ImageStore::Calcul2Icon, ImageStore::Calcul3Icon, ImageStore::Calcul4Icon};
const Image * focusedImages[k_numberOfImages] = {ImageStore::FocusedCalcul1Icon, ImageStore::FocusedCalcul2Icon, ImageStore::FocusedCalcul3Icon, ImageStore::FocusedCalcul4Icon};
myCell->setImage(images[index], focusedImages[index]);
}
}

View File

@@ -0,0 +1,38 @@
#ifndef PROBABILITY_CALCULATION_TYPE_CONTROLLER_H
#define PROBABILITY_CALCULATION_TYPE_CONTROLLER_H
#include <escher.h>
#include "calculation/calculation.h"
#include "law/law.h"
#include "image_cell.h"
namespace Probability {
class CalculationController;
class CalculationTypeController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource {
public:
CalculationTypeController(Responder * parentResponder, Law * law, Calculation * calculation, CalculationController * calculationController);
View * view() override;
void viewWillAppear() override;
void viewDidDisappear() override;
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
int numberOfRows() override;
KDCoordinate cellWidth() override;
KDCoordinate cellHeight() override;
HighlightCell * reusableCell(int index) override;
int reusableCellCount() override;
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
constexpr static int k_numberOfImages = 4;
private:
ImageCell m_imageCells[k_numberOfImages];
SelectableTableView m_selectableTableView;
Law * m_law;
Calculation * m_calculation;
CalculationController * m_calculationController;
};
}
#endif

View File

@@ -1,234 +0,0 @@
#include "calculation_view.h"
#include "app.h"
#include "../apps_container.h"
#include "calculation_controller.h"
#include <assert.h>
using namespace Poincare;
using namespace Shared;
namespace Probability {
CalculationView::CalculationView(Responder * parentResponder, CalculationController * calculationController, Calculation * calculation, Law * law) :
Responder(parentResponder),
m_imageTableView(this, law, calculation, calculationController),
m_draftTextBuffer{},
m_law(law),
m_calculation(calculation),
m_highlightedSubviewIndex(1),
m_calculationController(calculationController)
{
for (int i = 0; i < k_maxNumberOfEditableFields; i++) {
m_calculationCell[i].setParentResponder(this);
m_calculationCell[i].textField()->setDelegate(this);
m_calculationCell[i].textField()->setDraftTextBuffer(m_draftTextBuffer);
m_text[i].setAlignment(0.5f, 0.5f);
}
}
void CalculationView::didBecomeFirstResponder() {
if (m_highlightedSubviewIndex > 0) {
app()->setFirstResponder(&m_calculationCell[m_highlightedSubviewIndex-1]);
} else {
app()->setFirstResponder(&m_imageTableView);
}
}
bool CalculationView::handleEvent(Ion::Events::Event event) {
if ((event == Ion::Events::Left && m_highlightedSubviewIndex > 0) || (event == Ion::Events::Right && m_highlightedSubviewIndex < m_calculation->numberOfEditableParameters())) {
if (m_highlightedSubviewIndex == 0) {
m_imageTableView.select(false);
} else {
m_calculationCell[m_highlightedSubviewIndex-1].setHighlighted(false);
}
m_highlightedSubviewIndex = event == Ion::Events::Left ? m_highlightedSubviewIndex - 1 : m_highlightedSubviewIndex + 1;
if (m_highlightedSubviewIndex > 0) {
m_calculationCell[m_highlightedSubviewIndex-1].setHighlighted(true);
app()->setFirstResponder(&m_calculationCell[m_highlightedSubviewIndex-1]);
} else {
app()->setFirstResponder(&m_imageTableView);
}
return true;
}
return false;
}
void CalculationView::selectSubview(int subviewIndex) {
m_highlightedSubviewIndex = subviewIndex;
}
bool CalculationView::textFieldDidHandleEvent(::TextField * textField, Ion::Events::Event event, bool returnValue, bool textHasChanged) {
if (returnValue && textHasChanged) {
updateCalculationLayoutFromIndex(m_highlightedSubviewIndex-1);
}
return returnValue;
}
bool CalculationView::textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) {
return TextFieldDelegate::textFieldShouldFinishEditing(textField, event)
|| (event == Ion::Events::Right && textField->cursorLocation() == textField->draftTextLength() && m_highlightedSubviewIndex < m_calculation->numberOfEditableParameters())
|| (event == Ion::Events::Left && textField->cursorLocation() == 0);
}
bool CalculationView::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
App * probaApp = (App *)app();
Context * globalContext = probaApp->container()->globalContext();
double floatBody = Expression::approximateToScalar<double>(text, *globalContext);
if (std::isnan(floatBody) || std::isinf(floatBody)) {
app()->displayWarning(I18n::Message::UndefinedValue);
return false;
}
if (m_calculation->type() != Calculation::Type::FiniteIntegral && m_highlightedSubviewIndex == 2) {
if (floatBody < 0.0) {
floatBody = 0.0;
}
if (floatBody > 1.0) {
floatBody = 1.0;
}
}
if (!m_law->isContinuous() && (m_highlightedSubviewIndex == 1 || m_calculation->type() == Calculation::Type::FiniteIntegral)) {
floatBody = std::round(floatBody);
}
m_calculation->setParameterAtIndex(floatBody, m_highlightedSubviewIndex-1);
if (event == Ion::Events::Right || event == Ion::Events::Left) {
handleEvent(event);
}
reloadData();
updateCalculationLayoutFromIndex(0);
m_calculationController->reloadLawCurveView();
return true;
}
void CalculationView::reload() {
markRectAsDirty(bounds());
layoutSubviews();
}
void CalculationView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(KDRect(0,0, bounds().width(), ImageTableView::k_oneCellHeight), KDColorWhite);
KDSize charSize = KDText::charSize();
int numberOfCharacters;
KDCoordinate xCoordinate = ImageTableView::k_oneCellWidth + k_textWidthMargin;
for (int i = 0; i < k_maxNumberOfEditableFields; i++) {
if (m_calculation->numberOfEditableParameters() == i) {
return;
}
numberOfCharacters = strlen(I18n::translate(m_calculation->legendForParameterAtIndex(i)));
xCoordinate += numberOfCharacters*charSize.width() + k_textWidthMargin;
ctx->strokeRect(KDRect(xCoordinate-ImageTableView::k_outline, ImageTableView::k_margin, calculationCellWidth(i)+2*ImageTableView::k_outline, ImageCell::k_height+2*ImageTableView::k_outline), Palette::GreyMiddle);
xCoordinate += calculationCellWidth(i) + k_textWidthMargin;
}
}
void CalculationView::willDisplayEditableCellAtIndex(int index) {
m_calculationCell[index].setHighlighted(index == m_highlightedSubviewIndex-1);
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
Complex<double>::convertFloatToText(m_calculation->parameterAtIndex(index), buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal);
m_calculationCell[index].textField()->setText(buffer);
}
KDSize CalculationView::minimalSizeForOptimalDisplay() const {
//xCoordinate += tableSize.width() + k_textWidthMargin;
return KDSize(0, ImageTableView::k_oneCellHeight);
}
TextFieldDelegateApp * CalculationView::textFieldDelegateApp() {
return (App *)app();
}
void CalculationView::updateCalculationLayoutFromIndex(int index) {
KDSize charSize = KDText::charSize();
KDCoordinate numberOfCharacters = strlen(I18n::translate(m_calculation->legendForParameterAtIndex(0)));
KDCoordinate xCoordinate = m_imageTableView.minimalSizeForOptimalDisplay().width() + 2*k_textWidthMargin+numberOfCharacters*charSize.width();
KDCoordinate calculationWidth = calculationCellWidth(0);
if (index == 0) {
markRectAsDirty(KDRect(xCoordinate, ImageTableView::k_totalMargin-1, bounds().width() - xCoordinate, ImageCell::k_height+2));
m_calculationCell[0].setFrame(KDRect(xCoordinate, ImageTableView::k_totalMargin, calculationWidth, ImageCell::k_height));
}
xCoordinate += calculationWidth + k_textWidthMargin;
numberOfCharacters = strlen(I18n::translate(m_calculation->legendForParameterAtIndex(1)));
if (index == 0) {
m_text[1].setFrame(KDRect(xCoordinate, ImageTableView::k_totalMargin, numberOfCharacters*charSize.width(), ImageCell::k_height));
}
xCoordinate += numberOfCharacters*charSize.width() + k_textWidthMargin;
calculationWidth = calculationCellWidth(1);
if (index <= 1) {
markRectAsDirty(KDRect(xCoordinate, ImageTableView::k_totalMargin-1, bounds().width() - xCoordinate, ImageCell::k_height+2));
m_calculationCell[1].setFrame(KDRect(xCoordinate, ImageTableView::k_totalMargin, calculationWidth, ImageCell::k_height));
}
xCoordinate += calculationWidth + k_textWidthMargin;
if (m_calculation->numberOfParameters() > 2) {
numberOfCharacters = strlen(I18n::translate(m_calculation->legendForParameterAtIndex(2)));;
m_text[2].setFrame(KDRect(xCoordinate, ImageTableView::k_totalMargin, numberOfCharacters*charSize.width(), ImageCell::k_height));
xCoordinate += numberOfCharacters*charSize.width() + k_textWidthMargin;
calculationWidth = calculationCellWidth(2);
markRectAsDirty(KDRect(xCoordinate, ImageTableView::k_totalMargin-1, bounds().width() - xCoordinate, ImageCell::k_height+2));
m_calculationCell[2].setFrame(KDRect(xCoordinate, ImageTableView::k_totalMargin, calculationWidth, ImageCell::k_height));
}
}
void CalculationView::reloadData() {
for (int k = 0; k < m_calculation->numberOfParameters(); k++) {
willDisplayEditableCellAtIndex(k);
}
}
int CalculationView::numberOfSubviews() const {
return 2*m_calculation->numberOfParameters() + 1;
}
View * CalculationView::subviewAtIndex(int index) {
assert(index >= 0 && index < 7);
if (index == 0) {
return &m_imageTableView;
}
if (index == 1) {
m_text[0].setMessage(m_calculation->legendForParameterAtIndex(0));
m_text[0].setAlignment(0.5f, 0.5f);
return &m_text[0];
}
if (index == 3) {
m_text[1].setMessage(m_calculation->legendForParameterAtIndex(1));
m_text[1].setAlignment(0.5f, 0.5f);
return &m_text[1];
}
if (index == 5) {
m_text[2].setMessage(m_calculation->legendForParameterAtIndex(2));
m_text[2].setAlignment(0.5f, 0.5f);
return &m_text[2];
}
if (index == 2 || index == 4 || index == 6) {
return &m_calculationCell[(index - 2)/2];
}
return nullptr;
}
void CalculationView::layoutSubviews() {
// Reload values in textFields
reloadData();
// Reload messages
m_text[0].setMessage(m_calculation->legendForParameterAtIndex(0));
m_text[1].setMessage(m_calculation->legendForParameterAtIndex(1));
if (m_calculation->numberOfParameters() == 3) {
m_text[2].setMessage(m_calculation->legendForParameterAtIndex(2));
}
// Layout
KDSize charSize = KDText::charSize();
KDCoordinate xCoordinate = 0;
KDSize tableSize = m_imageTableView.minimalSizeForOptimalDisplay();
m_imageTableView.setFrame(KDRect(xCoordinate, 0, tableSize));
xCoordinate += tableSize.width() + k_textWidthMargin;
KDCoordinate numberOfCharacters = strlen(I18n::translate(m_calculation->legendForParameterAtIndex(0)));
m_text[0].setFrame(KDRect(xCoordinate, ImageTableView::k_totalMargin, numberOfCharacters*charSize.width(), ImageCell::k_height));
updateCalculationLayoutFromIndex(0);
}
KDCoordinate CalculationView::calculationCellWidth(int index) const {
KDCoordinate calculationCellWidth = m_calculationCell[index].minimalSizeForOptimalDisplay().width();
return min(k_maxTextFieldWidth, max(k_minTextFieldWidth, calculationCellWidth));
}
}

View File

@@ -1,58 +0,0 @@
#ifndef PROBABILITY_CALCULATION_VIEW_H
#define PROBABILITY_CALCULATION_VIEW_H
#include <escher.h>
#include "law/law.h"
#include "image_table_view.h"
#include "calculation/calculation.h"
#include "../shared/parameter_text_field_delegate.h"
namespace Probability {
class CalculationController;
class CalculationView : public View, public Responder, public Shared::ParameterTextFieldDelegate {
public:
CalculationView(Responder * parentResponder, CalculationController * calculationController, Calculation * Calculation, Law * law);
/* Responder */
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
void selectSubview(int subviewIndex);
/* TextField delegate */
bool textFieldDidHandleEvent(TextField * textField, Ion::Events::Event event, bool returnValue, bool textHasChanged) override;
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
/* View */
void reload();
void drawRect(KDContext * ctx, KDRect rect) const override;
void willDisplayEditableCellAtIndex(int index);
KDSize minimalSizeForOptimalDisplay() const override;
constexpr static int k_maxNumberOfEditableFields = 3;
private:
constexpr static KDCoordinate k_minTextFieldWidth = 4*KDText::charSize().width();
constexpr static KDCoordinate k_maxTextFieldWidth = 10*KDText::charSize().width();
constexpr static KDCoordinate k_textWidthMargin = 5;
Shared::TextFieldDelegateApp * textFieldDelegateApp() override;
void updateCalculationLayoutFromIndex(int index);
void reloadData();
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
KDCoordinate calculationCellWidth(int index) const;
ImageTableView m_imageTableView;
MessageTextView m_text[k_maxNumberOfEditableFields];
char m_draftTextBuffer[TextField::maxBufferSize()];
EditableTextCell m_calculationCell[k_maxNumberOfEditableFields];
Law * m_law;
Calculation * m_calculation;
int m_highlightedSubviewIndex;
CalculationController * m_calculationController;
};
/*class ScrollableCalculationView : public ScrollableView, public ScrollView
}*/
}
#endif

View File

@@ -0,0 +1,42 @@
#include "image_cell.h"
#include <assert.h>
namespace Probability {
ImageCell::ImageCell() :
HighlightCell(),
m_icon(nullptr),
m_focusedIcon(nullptr)
{
}
void ImageCell::setHighlighted(bool highlight) {
HighlightCell::setHighlighted(highlight);
if (isHighlighted()) {
m_iconView.setImage(m_focusedIcon);
} else {
m_iconView.setImage(m_icon);
}
}
void ImageCell::setImage(const Image * image, const Image * focusedImage) {
m_icon = image;
m_focusedIcon = focusedImage;
setHighlighted(isHighlighted());
markRectAsDirty(bounds());
}
int ImageCell::numberOfSubviews() const {
return 1;
}
View * ImageCell::subviewAtIndex(int index) {
assert(index == 0);
return &m_iconView;
}
void ImageCell::layoutSubviews() {
m_iconView.setFrame(bounds());
}
}

View File

@@ -0,0 +1,26 @@
#ifndef PROBABILITY_IMAGE_CELL_H
#define PROBABILITY_IMAGE_CELL_H
#include <escher.h>
namespace Probability {
class ImageCell : public HighlightCell {
public:
ImageCell();
void setHighlighted(bool highlight) override;
void setImage(const Image * image, const Image * focusedImage);
constexpr static KDCoordinate k_width = 39;
constexpr static KDCoordinate k_height = 23;
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
ImageView m_iconView;
const Image * m_icon;
const Image * m_focusedIcon;
};
}
#endif

View File

@@ -1,164 +0,0 @@
#include "image_table_view.h"
#include <assert.h>
#include "app.h"
#include "images/calcul1_icon.h"
#include "images/calcul2_icon.h"
#include "images/calcul3_icon.h"
#include "images/calcul4_icon.h"
#include "images/focused_calcul1_icon.h"
#include "images/focused_calcul2_icon.h"
#include "images/focused_calcul3_icon.h"
#include "images/focused_calcul4_icon.h"
namespace Probability {
ImageCell::ImageCell() :
HighlightCell(),
m_icon(nullptr),
m_focusedIcon(nullptr)
{
}
int ImageCell::numberOfSubviews() const {
return 1;
}
View * ImageCell::subviewAtIndex(int index) {
assert(index == 0);
return &m_iconView;
}
void ImageCell::layoutSubviews() {
m_iconView.setFrame(bounds());
}
void ImageCell::reloadCell() {
HighlightCell::reloadCell();
if (isHighlighted()) {
m_iconView.setImage(m_focusedIcon);
} else {
m_iconView.setImage(m_icon);
}
}
void ImageCell::setImage(const Image * image, const Image * focusedImage) {
m_icon = image;
m_focusedIcon = focusedImage;
}
ImageTableView::ImageTableView(Responder * parentResponder, Law * law, Calculation * calculation, CalculationController * calculationController) :
View(),
Responder(parentResponder),
m_selectableTableView(this, this, 0, 0, 0, 0, 0, 0, this, nullptr, false, false),
m_isSelected(false),
m_law(law),
m_calculation(calculation),
m_calculationController(calculationController)
{
assert(m_calculation != nullptr);
}
void ImageTableView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->strokeRect(KDRect(k_margin, k_margin, ImageCell::k_width+2*k_outline, ImageCell::k_height+2*k_outline), Palette::GreyMiddle);
}
KDSize ImageTableView::minimalSizeForOptimalDisplay() const {
return KDSize(2*k_totalMargin+ImageCell::k_width, k_totalMargin+k_numberOfImages*ImageCell::k_height);
}
void ImageTableView::didBecomeFirstResponder() {
m_selectableTableView.reloadData();
app()->setFirstResponder(&m_selectableTableView);
}
void ImageTableView::didEnterResponderChain(Responder * previousFirstResponder) {
selectCellAtLocation(0, 0);
}
void ImageTableView::willExitResponderChain(Responder * nextFirstResponder) {
m_selectableTableView.deselectTable();
}
bool ImageTableView::handleEvent(Ion::Events::Event event) {
if ((event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Down) && !m_isSelected) {
select(true);
return true;
}
if ((event == Ion::Events::OK || event == Ion::Events::EXE) && m_isSelected) {
m_calculationController->setCalculationAccordingToIndex(selectedRow());
select(false);
return true;
}
if (event == Ion::Events::Back) {
select(false);
return true;
}
return false;
}
void ImageTableView::select(bool select) {
if (!select) {
m_selectableTableView.deselectTable();
m_isSelected = select;
m_selectableTableView.reloadData();
/* The dropdown menu is drawn on the law curve view, so when we deselect
* the dropdown menu, we need to redraw the law curve view */
m_calculationController->reload();
m_selectableTableView.selectCellAtLocation(0, 0);
} else {
m_isSelected = select;
m_selectableTableView.reloadData();
m_selectableTableView.selectCellAtLocation(0, (int)m_calculation->type());
}
}
int ImageTableView::numberOfRows() {
if (m_isSelected) {
if (m_law->isContinuous()) {
return k_numberOfImages-1;
}
return k_numberOfImages;
}
return 1;
}
KDCoordinate ImageTableView::cellHeight() {
return ImageCell::k_height;
}
HighlightCell * ImageTableView::reusableCell(int index) {
assert(index >= 0);
assert(index < k_numberOfImages);
return &m_imageCells[index];
}
int ImageTableView::reusableCellCount() {
return k_numberOfImages;
}
void ImageTableView::willDisplayCellForIndex(HighlightCell * cell, int index) {
ImageCell * myCell = (ImageCell *)cell;
const Image * images[k_numberOfImages] = {ImageStore::Calcul1Icon, ImageStore::Calcul2Icon, ImageStore::Calcul3Icon, ImageStore::Calcul4Icon};
const Image * focusedImages[k_numberOfImages] = {ImageStore::FocusedCalcul1Icon, ImageStore::FocusedCalcul2Icon, ImageStore::FocusedCalcul3Icon, ImageStore::FocusedCalcul4Icon};
if (!m_isSelected) {
myCell->setImage(images[(int)m_calculation->type()], focusedImages[(int)m_calculation->type()]);
} else {
myCell->setImage(images[index], focusedImages[index]);
}
myCell->reloadCell();
}
int ImageTableView::numberOfSubviews() const {
return 1;
}
View * ImageTableView::subviewAtIndex(int index) {
assert(index == 0);
return &m_selectableTableView;
}
void ImageTableView::layoutSubviews() {
m_selectableTableView.setFrame(KDRect(k_totalMargin, k_totalMargin, bounds().width()-2*k_totalMargin, bounds().height()-k_totalMargin));
}
}

View File

@@ -1,62 +0,0 @@
#ifndef PROBABILITY_IMAGE_TABLE_VIEW_H
#define PROBABILITY_IMAGE_TABLE_VIEW_H
#include <escher.h>
#include "calculation/calculation.h"
namespace Probability {
class CalculationController;
class ImageCell : public HighlightCell {
public:
ImageCell();
void reloadCell() override;
void setImage(const Image * image, const Image * focusedImage);
constexpr static KDCoordinate k_width = 39;
constexpr static KDCoordinate k_height = 23;
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
ImageView m_iconView;
const Image * m_icon;
const Image * m_focusedIcon;
};
class ImageTableView : public View, public Responder, public SimpleListViewDataSource, public SelectableTableViewDataSource {
public:
ImageTableView(Responder * parentResponder, Law * law, Calculation * calculation, CalculationController * calculationController);
void drawRect(KDContext * ctx, KDRect rect) const override;
KDSize minimalSizeForOptimalDisplay() const override;
void didBecomeFirstResponder() override;
void didEnterResponderChain(Responder * previousFirstResponder) override;
void willExitResponderChain(Responder * nextFirstResponder) override;
bool handleEvent(Ion::Events::Event event) override;
void select(bool select);
int numberOfRows() override;
KDCoordinate cellHeight() override;
HighlightCell * reusableCell(int index) override;
int reusableCellCount() override;
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
constexpr static KDCoordinate k_outline = 1;
constexpr static KDCoordinate k_margin = 3;
constexpr static KDCoordinate k_totalMargin = k_outline+k_margin;
constexpr static KDCoordinate k_oneCellWidth = 2*k_totalMargin+ImageCell::k_width;
constexpr static KDCoordinate k_oneCellHeight = 2*k_totalMargin+ImageCell::k_height;
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
constexpr static int k_numberOfImages = 4;
ImageCell m_imageCells[k_numberOfImages];
SelectableTableView m_selectableTableView;
bool m_isSelected;
Law * m_law;
Calculation * m_calculation;
CalculationController * m_calculationController;
};
}
#endif

View File

@@ -0,0 +1,53 @@
#include "responder_image_cell.h"
#include <assert.h>
namespace Probability {
ResponderImageCell::ResponderImageCell(Responder * parentResponder, Law * law, Calculation * calculation, CalculationController * calculationController) :
HighlightCell(),
Responder(parentResponder),
m_calculationTypeController(nullptr, law, calculation, calculationController)
{
}
void ResponderImageCell::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(bounds(), KDColorWhite);
ctx->strokeRect(KDRect(k_margin, k_margin, ImageCell::k_width+2*k_outline, ImageCell::k_height+2*k_outline), Palette::GreyMiddle);
}
KDSize ResponderImageCell::minimalSizeForOptimalDisplay() const {
return KDSize(2*k_totalMargin+ImageCell::k_width, 2*k_totalMargin*ImageCell::k_height);
}
bool ResponderImageCell::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Down) {
KDPoint topLeftAngle = app()->modalView()->pointFromPointInView(this, KDPoint(k_totalMargin, k_totalMargin));
app()->displayModalViewController(&m_calculationTypeController, 0.0f, 0.0f, topLeftAngle.y(), topLeftAngle.x());
return true;
}
return false;
}
void ResponderImageCell::setHighlighted(bool highlight) {
HighlightCell::setHighlighted(highlight);
m_imageCell.setHighlighted(highlight);
}
void ResponderImageCell::setImage(const Image * image, const Image * focusedImage) {
m_imageCell.setImage(image, focusedImage);
}
int ResponderImageCell::numberOfSubviews() const {
return 1;
}
View * ResponderImageCell::subviewAtIndex(int index) {
assert(index == 0);
return &m_imageCell;
}
void ResponderImageCell::layoutSubviews() {
m_imageCell.setFrame(KDRect(k_totalMargin, k_totalMargin, bounds().width()-2*k_totalMargin, bounds().height()-2*k_totalMargin));
}
}

View File

@@ -0,0 +1,37 @@
#ifndef PROBABILITY_RESPONDER_IMAGE_CELL_H
#define PROBABILITY_RESPONDER_IMAGE_CELL_H
#include <escher.h>
#include "calculation/calculation.h"
#include "calculation_type_controller.h"
#include "image_cell.h"
namespace Probability {
class ResponderImageCell : public HighlightCell, public Responder {
public:
ResponderImageCell(Responder * parentResponder, Law * law, Calculation * calculation, CalculationController * calculationController);
Responder * responder() override {
return this;
}
void drawRect(KDContext * ctx, KDRect rect) const override;
KDSize minimalSizeForOptimalDisplay() const override;
bool handleEvent(Ion::Events::Event event) override;
void setHighlighted(bool highlight) override;
void setImage(const Image * image, const Image * focusedImage);
constexpr static KDCoordinate k_outline = 1;
constexpr static KDCoordinate k_margin = 3;
constexpr static KDCoordinate k_totalMargin = k_outline+k_margin;
constexpr static KDCoordinate k_oneCellWidth = 2*k_totalMargin+ImageCell::k_width;
constexpr static KDCoordinate k_oneCellHeight = 2*k_totalMargin+ImageCell::k_height;
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
ImageCell m_imageCell;
CalculationTypeController m_calculationTypeController;
};
}
#endif

View File

@@ -6,7 +6,7 @@
class ListViewDataSource : public TableViewDataSource {
public:
KDCoordinate cellWidth();
virtual KDCoordinate cellWidth();
KDCoordinate columnWidth(int i) override;
int numberOfColumns() override;
void willDisplayCellAtLocation(HighlightCell * cell, int x, int y) override;