mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[code] Program store that uses an accordeon buffer to store the
programs. The user can now add scripts. A contextual menu displays various options when clicking on a script, such as editing. Change-Id: I124a17f7584b0beedb897e8499ca0a1bb6c13cbe
This commit is contained in:
@@ -10,6 +10,8 @@ app_objs += $(addprefix apps/code/,\
|
||||
editor_controller.o\
|
||||
menu_controller.o\
|
||||
program.o\
|
||||
program_parameter_controller.o\
|
||||
program_store.o\
|
||||
)
|
||||
|
||||
i18n_files += $(addprefix apps/code/,\
|
||||
|
||||
@@ -17,9 +17,8 @@ const Image * App::Descriptor::icon() {
|
||||
return ImageStore::CodeIcon;
|
||||
}
|
||||
|
||||
App::Snapshot::Snapshot() :
|
||||
m_program()
|
||||
{
|
||||
App::Snapshot::Snapshot() {
|
||||
m_programStore.addDefaultProgram();
|
||||
}
|
||||
|
||||
App * App::Snapshot::unpack(Container * container) {
|
||||
@@ -27,7 +26,7 @@ App * App::Snapshot::unpack(Container * container) {
|
||||
}
|
||||
|
||||
void App::Snapshot::reset() {
|
||||
m_program.setContent("");
|
||||
m_programStore.deleteAll();
|
||||
}
|
||||
|
||||
App::Descriptor * App::Snapshot::descriptor() {
|
||||
@@ -35,14 +34,14 @@ App::Descriptor * App::Snapshot::descriptor() {
|
||||
return &descriptor;
|
||||
}
|
||||
|
||||
Program * App::Snapshot::program() {
|
||||
return &m_program;
|
||||
ProgramStore * App::Snapshot::programStore() {
|
||||
return &m_programStore;
|
||||
}
|
||||
|
||||
App::App(Container * container, Snapshot * snapshot) :
|
||||
::App(container, snapshot, &m_codeStackViewController, I18n::Message::Warning),
|
||||
m_listFooter(&m_codeStackViewController, &m_menuController, &m_menuController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey),
|
||||
m_menuController(&m_listFooter, snapshot->program(), &m_listFooter),
|
||||
m_menuController(&m_listFooter, snapshot->programStore(), &m_listFooter),
|
||||
m_codeStackViewController(&m_modalViewController, &m_listFooter)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include "menu_controller.h"
|
||||
#include "program.h"
|
||||
#include "program_store.h"
|
||||
#include "../shared/message_controller.h"
|
||||
|
||||
namespace Code {
|
||||
@@ -22,9 +22,9 @@ public:
|
||||
App * unpack(Container * container) override;
|
||||
void reset() override;
|
||||
Descriptor * descriptor() override;
|
||||
Program * program();
|
||||
ProgramStore * programStore();
|
||||
private:
|
||||
Program m_program;
|
||||
ProgramStore m_programStore;
|
||||
};
|
||||
StackViewController * stackViewController() { return &m_codeStackViewController; }
|
||||
private:
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
AddScript = "Add a script"
|
||||
ScriptOptions = "Script options"
|
||||
EditScript = "Edit script"
|
||||
RenameScript = "Rename script"
|
||||
AutoImportScript = "Auto import."
|
||||
DeleteScript = "Delete script"
|
||||
Console = "Console"
|
||||
ConsoleError = "Error"
|
||||
EditProgram = "Programm bearbeiten"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
AddScript = "Add a script"
|
||||
ScriptOptions = "Script options"
|
||||
EditScript = "Edit script"
|
||||
RenameScript = "Rename script"
|
||||
AutoImportScript = "Auto import."
|
||||
DeleteScript = "Delete script"
|
||||
Console = "Console"
|
||||
ConsoleError = "Error"
|
||||
EditProgram = "Edit program"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
AddScript = "Add a script"
|
||||
ScriptOptions = "Script options"
|
||||
EditScript = "Edit script"
|
||||
RenameScript = "Rename script"
|
||||
AutoImportScript = "Auto import."
|
||||
DeleteScript = "Delete script"
|
||||
Console = "Console"
|
||||
ConsoleError = "Error"
|
||||
EditProgram = "Editar el programa"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
AddScript = "Add a script"
|
||||
ScriptOptions = "Script options"
|
||||
EditScript = "Edit script"
|
||||
RenameScript = "Rename script"
|
||||
AutoImportScript = "Auto import."
|
||||
DeleteScript = "Delete script"
|
||||
Console = "Console"
|
||||
ConsoleError = "Error"
|
||||
EditProgram = "Editer le programme"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
AddScript = "Add a script"
|
||||
ScriptOptions = "Script options"
|
||||
EditScript = "Edit script"
|
||||
RenameScript = "Rename script"
|
||||
AutoImportScript = "Auto import."
|
||||
DeleteScript = "Delete script"
|
||||
Console = "Console"
|
||||
ConsoleError = "Error"
|
||||
EditProgram = "Editar programa"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace Code {
|
||||
|
||||
EditorController::EditorController(Program * program) :
|
||||
EditorController::EditorController() :
|
||||
ViewController(nullptr),
|
||||
m_view(this, program->editableContent(), program->bufferSize())
|
||||
m_view(this)
|
||||
{
|
||||
}
|
||||
|
||||
View * EditorController::view() {
|
||||
return &m_view;
|
||||
void EditorController::setProgram(Program program){
|
||||
m_view.setText(program.editableContent(), program.bufferSize());
|
||||
}
|
||||
|
||||
bool EditorController::handleEvent(Ion::Events::Event event) {
|
||||
|
||||
@@ -8,8 +8,9 @@ namespace Code {
|
||||
|
||||
class EditorController : public ViewController {
|
||||
public:
|
||||
EditorController(Program * program);
|
||||
View * view() override;
|
||||
EditorController();
|
||||
void setProgram(Program program);
|
||||
View * view() override { return &m_view; }
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
private:
|
||||
|
||||
@@ -4,19 +4,22 @@
|
||||
|
||||
namespace Code {
|
||||
|
||||
MenuController::MenuController(Responder * parentResponder, Program * program, ButtonRowController * footer) :
|
||||
MenuController::MenuController(Responder * parentResponder, ProgramStore * programStore, ButtonRowController * footer) :
|
||||
ViewController(parentResponder),
|
||||
ButtonRowDelegate(nullptr, footer),
|
||||
m_programStore(programStore),
|
||||
m_addNewProgramCell(I18n::Message::AddScript),
|
||||
m_editorController(program),
|
||||
m_consoleController(parentResponder),
|
||||
m_selectableTableView(this, this, 0, 1, Metric::CommonTopMargin, Metric::CommonRightMargin, Metric::CommonBottomMargin, Metric::CommonLeftMargin, this),
|
||||
m_consoleButton(this, I18n::Message::Console, Invocation([](void * context, void * sender) {
|
||||
MenuController * menu = (MenuController *)context;
|
||||
menu->app()->displayModalViewController(menu->consoleController(), 0.5f, 0.5f);
|
||||
}, this))
|
||||
}, this)),
|
||||
m_selectableTableView(this, this, 0, 1, 0, 0, 0, 0, this, nullptr, false),
|
||||
m_consoleController(parentResponder),
|
||||
m_programParameterController(nullptr, I18n::Message::ScriptOptions, m_programStore)
|
||||
{
|
||||
m_stackViewController = (StackViewController *) (parentResponder->parentResponder()); // TODO: Dirty?
|
||||
for (int i = 0; i< k_maxNumberOfCells; i++) {
|
||||
m_cells[i].setMessageFontSize(KDText::FontSize::Large);
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleController * MenuController::consoleController() {
|
||||
@@ -38,46 +41,65 @@ bool MenuController::handleEvent(Ion::Events::Event event) {
|
||||
footer()->setSelectedButton(0);
|
||||
return true;
|
||||
} else if (event == Ion::Events::Up) {
|
||||
footer()->setSelectedButton(-1);
|
||||
m_selectableTableView.selectCellAtLocation(0, numberOfRows()-1);
|
||||
app()->setFirstResponder(&m_selectableTableView);
|
||||
return true;
|
||||
if (m_selectableTableView.selectedRow()<0) {
|
||||
footer()->setSelectedButton(-1);
|
||||
m_selectableTableView.selectCellAtLocation(0, numberOfRows()-1);
|
||||
app()->setFirstResponder(&m_selectableTableView);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ViewController * vc[2] = {&m_editorController, &m_consoleController};
|
||||
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
|
||||
app()->displayModalViewController(vc[selectedRow()], 0.5f, 0.5f);
|
||||
return true;
|
||||
int selectedRow = m_selectableTableView.selectedRow();
|
||||
if (selectedRow >= 0 && selectedRow < m_programStore->numberOfPrograms()) {
|
||||
configureProgram();
|
||||
return true;
|
||||
} else if (selectedRow == m_programStore->numberOfPrograms()) {
|
||||
addProgram();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MenuController::configureProgram() {
|
||||
m_programParameterController.setProgram(m_selectableTableView.selectedRow());
|
||||
stackViewController()->push(&m_programParameterController);
|
||||
}
|
||||
|
||||
void MenuController::addProgram() {
|
||||
m_selectableTableView.selectCellAtLocation(0, 0);
|
||||
m_programStore->addDefaultProgram();
|
||||
m_selectableTableView.reloadData();
|
||||
m_selectableTableView.selectCellAtLocation(0, numberOfRows()-2);
|
||||
}
|
||||
|
||||
int MenuController::numberOfRows() {
|
||||
return k_totalNumberOfCells + 1;
|
||||
return m_programStore->numberOfPrograms() + 1;
|
||||
//TODO do not add the addProgram row if there can be no more programs stored.
|
||||
};
|
||||
|
||||
KDCoordinate MenuController::cellHeight() {
|
||||
return Metric::ParameterCellHeight;
|
||||
return k_rowHeight;
|
||||
}
|
||||
|
||||
HighlightCell * MenuController::reusableCell(int index) {
|
||||
assert(index >= 0);
|
||||
if (index < k_totalNumberOfCells) {
|
||||
if (index < m_programStore->numberOfPrograms()) {
|
||||
return &m_cells[index];
|
||||
}
|
||||
assert(index == k_totalNumberOfCells);
|
||||
assert(index == m_programStore->numberOfPrograms());
|
||||
return &m_addNewProgramCell;
|
||||
}
|
||||
|
||||
int MenuController::reusableCellCount() {
|
||||
return k_totalNumberOfCells + 1;
|
||||
return m_programStore->numberOfPrograms() + 1;
|
||||
}
|
||||
|
||||
void MenuController::willDisplayCellForIndex(HighlightCell * cell, int index) {
|
||||
if (index < k_totalNumberOfCells) {
|
||||
if (index < m_programStore->numberOfPrograms()) {
|
||||
MessageTableCell * myCell = (MessageTableCell *)cell;
|
||||
I18n::Message titles[k_totalNumberOfCells] = {I18n::Message::EditProgram, I18n::Message::Console};
|
||||
// TODO: translate Console in the .i18n
|
||||
myCell->setMessage(titles[index]);
|
||||
// TODO: store script names
|
||||
myCell->setMessage(I18n::Message::Console);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +109,11 @@ int MenuController::numberOfButtons(ButtonRowController::Position position) cons
|
||||
|
||||
Button * MenuController::buttonAtIndex(int index, ButtonRowController::Position position) const {
|
||||
assert(index == 0);
|
||||
return (Button *) (&m_consoleButton);
|
||||
return const_cast<Button *>(&m_consoleButton);
|
||||
}
|
||||
|
||||
StackViewController * MenuController::stackViewController() {
|
||||
return static_cast<StackViewController *>(parentResponder()->parentResponder());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,17 +3,19 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include "console_controller.h"
|
||||
#include "editor_controller.h"
|
||||
#include "program_parameter_controller.h"
|
||||
#include <apps/shared/new_function_cell.h>
|
||||
#include "program.h"
|
||||
#include "program_store.h"
|
||||
|
||||
namespace Code {
|
||||
|
||||
class MenuController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource, public ButtonRowDelegate {
|
||||
public:
|
||||
MenuController(Responder * parentResponder, Program * program, ButtonRowController * footer);
|
||||
MenuController(Responder * parentResponder, ProgramStore * programStore, ButtonRowController * footer);
|
||||
ConsoleController * consoleController();
|
||||
StackViewController * stackViewController() { return m_stackViewController; }
|
||||
StackViewController * stackViewController();
|
||||
void configureProgram();
|
||||
void addProgram();
|
||||
|
||||
/* ViewController */
|
||||
View * view() override;
|
||||
@@ -31,14 +33,15 @@ public:
|
||||
int numberOfButtons(ButtonRowController::Position position) const override;
|
||||
Button * buttonAtIndex(int index, ButtonRowController::Position position) const override;
|
||||
private:
|
||||
constexpr static int k_totalNumberOfCells = 2;
|
||||
MessageTableCell m_cells[k_totalNumberOfCells];
|
||||
constexpr static int k_maxNumberOfCells = 5; //TODO
|
||||
static constexpr KDCoordinate k_rowHeight = 50; //TODO create common parent class with Shared::ListController
|
||||
ProgramStore * m_programStore;
|
||||
MessageTableCell m_cells[k_maxNumberOfCells];
|
||||
Shared::NewFunctionCell m_addNewProgramCell;
|
||||
StackViewController * m_stackViewController;
|
||||
EditorController m_editorController;
|
||||
ConsoleController m_consoleController;
|
||||
SelectableTableView m_selectableTableView;
|
||||
Button m_consoleButton;
|
||||
SelectableTableView m_selectableTableView;
|
||||
ConsoleController m_consoleController;
|
||||
ProgramParameterController m_programParameterController;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,52 +2,21 @@
|
||||
|
||||
namespace Code {
|
||||
|
||||
Program::Program() :
|
||||
m_buffer()
|
||||
Program::Program(char * textBuffer, size_t sizeOfBuffer) :
|
||||
m_bufferSize(sizeOfBuffer),
|
||||
m_textBuffer(textBuffer)
|
||||
{
|
||||
const char program[] = R"(# This program draws a Mandelbrot fractal set
|
||||
# N_iteration: degree of precision
|
||||
import kandinsky
|
||||
N_iteration = 10
|
||||
for x in range(320):
|
||||
for y in range(240):
|
||||
# Compute the mandelbrot sequence for the point c = (c_r, c_i) with start value z = (z_r, z_i)
|
||||
z_r = 0
|
||||
z_i = 0
|
||||
# Rescale to fit the drawing screen 320x222
|
||||
c_r = 2.7*x/319-2.1
|
||||
c_i = -1.87*y/221+0.935
|
||||
i = 0
|
||||
while (i < N_iteration) and ((z_r * z_r) + (z_i * z_i) < 4):
|
||||
i = i + 1
|
||||
stock = z_r
|
||||
z_r = z_r * z_r - z_i * z_i + c_r
|
||||
z_i = 2 * stock * z_i + c_i
|
||||
# Choose the color of the dot from the Mandelbrot sequence
|
||||
rgb = int(255*i/N_iteration)
|
||||
col = kandinsky.color(int(rgb),int(rgb*0.75),int(rgb*0.25))
|
||||
# Draw a pixel colored in 'col' at position (x,y)
|
||||
kandinsky.set_pixel(x,y,col))";
|
||||
|
||||
constexpr size_t len = sizeof(program);
|
||||
static_assert(len < k_bufferSize, "Default program length bigger than buffer size");
|
||||
memcpy(m_buffer, program, len);
|
||||
}
|
||||
|
||||
const char * Program::readOnlyContent() const {
|
||||
return m_buffer;
|
||||
return m_textBuffer;
|
||||
}
|
||||
|
||||
char * Program::editableContent() {
|
||||
return m_buffer;
|
||||
return m_textBuffer;
|
||||
}
|
||||
|
||||
void Program::setContent(const char * program) {
|
||||
memcpy(m_buffer, program, k_bufferSize);
|
||||
}
|
||||
|
||||
int Program::bufferSize() const {
|
||||
return k_bufferSize;
|
||||
return m_bufferSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,14 +7,13 @@ namespace Code {
|
||||
|
||||
class Program {
|
||||
public:
|
||||
Program();
|
||||
Program(char * textBuffer, size_t sizeOfBuffer);
|
||||
const char * readOnlyContent() const;
|
||||
char * editableContent();
|
||||
void setContent(const char * program);
|
||||
int bufferSize() const;
|
||||
private:
|
||||
constexpr static int k_bufferSize = 1024;
|
||||
char m_buffer[k_bufferSize];
|
||||
size_t m_bufferSize;
|
||||
char * m_textBuffer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
78
apps/code/program_parameter_controller.cpp
Normal file
78
apps/code/program_parameter_controller.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "program_parameter_controller.h"
|
||||
|
||||
namespace Code {
|
||||
|
||||
ProgramParameterController::ProgramParameterController(Responder * parentResponder, I18n::Message title, ProgramStore * programStore) :
|
||||
ViewController(parentResponder),
|
||||
m_pageTitle(title),
|
||||
m_editProgram(I18n::Message::Default),
|
||||
m_renameProgram(I18n::Message::Default),
|
||||
m_autoImportProgram(I18n::Message::Default),
|
||||
m_deleteProgram(I18n::Message::Default),
|
||||
m_selectableTableView(this, this, 0, 1, Metric::CommonTopMargin, Metric::CommonRightMargin,
|
||||
Metric::CommonBottomMargin, Metric::CommonLeftMargin, this),
|
||||
m_programStore(programStore)
|
||||
{
|
||||
}
|
||||
|
||||
void ProgramParameterController::setProgram(int i){
|
||||
m_editorController.setProgram(m_programStore->editableProgram(i));
|
||||
}
|
||||
|
||||
View * ProgramParameterController::view() {
|
||||
return &m_selectableTableView;
|
||||
}
|
||||
|
||||
const char * ProgramParameterController::title() {
|
||||
return I18n::translate(m_pageTitle);
|
||||
}
|
||||
|
||||
bool ProgramParameterController::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::OK || event == Ion::Events::EXE || (event == Ion::Events::Right && selectedRow() == 1)) {
|
||||
switch (selectedRow()) {
|
||||
case 0:
|
||||
app()->displayModalViewController(&m_editorController, 0.5f, 0.5f);
|
||||
return true;
|
||||
//TODO other cases
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProgramParameterController::didBecomeFirstResponder() {
|
||||
if (selectedRow() < 0) {
|
||||
selectCellAtLocation(0, 0);
|
||||
}
|
||||
app()->setFirstResponder(&m_selectableTableView);
|
||||
}
|
||||
|
||||
KDCoordinate ProgramParameterController::cellHeight() {
|
||||
return Metric::ParameterCellHeight;
|
||||
}
|
||||
|
||||
HighlightCell * ProgramParameterController::reusableCell(int index) {
|
||||
assert(index >= 0);
|
||||
assert(index < k_totalNumberOfCell);
|
||||
HighlightCell * cells[] = {&m_editProgram, &m_renameProgram, &m_autoImportProgram, &m_deleteProgram};
|
||||
return cells[index];
|
||||
|
||||
}
|
||||
|
||||
int ProgramParameterController::reusableCellCount() {
|
||||
return k_totalNumberOfCell;
|
||||
}
|
||||
|
||||
int ProgramParameterController::numberOfRows() {
|
||||
return k_totalNumberOfCell;
|
||||
}
|
||||
|
||||
void ProgramParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) {
|
||||
MessageTableCell * myCell = (MessageTableCell *)cell;
|
||||
I18n::Message labels[k_totalNumberOfCell] = {I18n::Message::EditScript, I18n::Message::RenameScript, I18n::Message::AutoImportScript, I18n::Message::DeleteScript};
|
||||
myCell->setMessage(labels[index]);
|
||||
}
|
||||
|
||||
}
|
||||
43
apps/code/program_parameter_controller.h
Normal file
43
apps/code/program_parameter_controller.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef CODE_PROGRAM_PARAMETER_CONTROLLER_H
|
||||
#define CODE_PROGRAM_PARAMETER_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "../i18n.h"
|
||||
#include "editor_controller.h"
|
||||
#include "program_store.h"
|
||||
|
||||
namespace Code {
|
||||
|
||||
class ProgramParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource {
|
||||
public:
|
||||
ProgramParameterController(Responder * parentResponder, I18n::Message title, ProgramStore * programStore);
|
||||
void setProgram(int i);
|
||||
|
||||
/* ViewController */
|
||||
View * view() override;
|
||||
const char * title() override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
|
||||
/* SimpleListViewDataSource */
|
||||
KDCoordinate cellHeight() override;
|
||||
HighlightCell * reusableCell(int index) override;
|
||||
int reusableCellCount() override;
|
||||
int numberOfRows() override;
|
||||
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
|
||||
|
||||
private:
|
||||
constexpr static int k_totalNumberOfCell = 4;
|
||||
I18n::Message m_pageTitle;
|
||||
MessageTableCell m_editProgram;
|
||||
MessageTableCell m_renameProgram;
|
||||
MessageTableCellWithSwitch m_autoImportProgram;
|
||||
MessageTableCell m_deleteProgram;
|
||||
SelectableTableView m_selectableTableView;
|
||||
EditorController m_editorController;
|
||||
ProgramStore * m_programStore;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
208
apps/code/program_store.cpp
Normal file
208
apps/code/program_store.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#include "program_store.h"
|
||||
#include "string.h"
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Code {
|
||||
|
||||
ProgramStore::ProgramStore() :
|
||||
m_numberOfPrograms(0),
|
||||
m_lastProgramEdited(-1)
|
||||
{
|
||||
for (int i = 0; i<k_historySize; i ++) {
|
||||
m_history[i] = FreeSpaceMarker;
|
||||
}
|
||||
}
|
||||
|
||||
Program ProgramStore::editableProgram(int i) {
|
||||
assert(i >= 0 && i < numberOfPrograms());
|
||||
cleanAndMoveFreeSpaceAfterProgram(i);
|
||||
int beginningOfProgram = indexOfProgram(i);
|
||||
|
||||
// Compute the size of the program, including the free space of m_history
|
||||
int sizeOfEditableProgram = 0;
|
||||
for (int j=beginningOfProgram; j<k_historySize-1; j++) {
|
||||
if (m_history[j] == FreeSpaceMarker && m_history[j+1] != FreeSpaceMarker) {
|
||||
break;
|
||||
}
|
||||
sizeOfEditableProgram++;
|
||||
}
|
||||
m_lastProgramEdited = i;
|
||||
return Program(&m_history[beginningOfProgram], sizeOfEditableProgram);
|
||||
}
|
||||
|
||||
Program ProgramStore::program(int i) {
|
||||
assert(i >= 0 && i < numberOfPrograms());
|
||||
int beginningOfProgram = indexOfProgram(i);
|
||||
return Program(&m_history[beginningOfProgram], strlen(&m_history[beginningOfProgram] + 1));
|
||||
}
|
||||
|
||||
int ProgramStore::numberOfPrograms() const {
|
||||
return m_numberOfPrograms;
|
||||
}
|
||||
|
||||
Program ProgramStore::editableNewProgram() {
|
||||
cleanAndMoveFreeSpaceAfterProgram(numberOfPrograms()-1);
|
||||
m_numberOfPrograms++;
|
||||
return Program(&m_history[indexOfFirstFreeSpaceMarker()], sizeOfFreeSpace() - 1);
|
||||
}
|
||||
|
||||
void ProgramStore::deleteProgram(int i){
|
||||
assert (i >= 0 && i < numberOfPrograms());
|
||||
cleanAndMoveFreeSpaceAfterProgram(i);
|
||||
int indexOfCharToDelete = indexOfProgram(i);
|
||||
while (m_history[indexOfCharToDelete] != FreeSpaceMarker) {
|
||||
m_history[indexOfCharToDelete] = FreeSpaceMarker;
|
||||
indexOfCharToDelete++;
|
||||
}
|
||||
m_numberOfPrograms--;
|
||||
}
|
||||
|
||||
void ProgramStore::deleteAll(){
|
||||
for (int i = 0; i<numberOfPrograms(); i++){
|
||||
deleteProgram(i);
|
||||
//TODO moves the free space around, better to just put all the buffer to -1 ??
|
||||
}
|
||||
m_numberOfPrograms = 0;
|
||||
}
|
||||
|
||||
int ProgramStore::indexOfProgram(int i) const {
|
||||
assert (i >= 0 && i < numberOfPrograms());
|
||||
int currentProgramNumber = 0;
|
||||
int beginningOfProgram = 0;
|
||||
while (m_history[beginningOfProgram] == FreeSpaceMarker) {
|
||||
beginningOfProgram++;
|
||||
}
|
||||
if (i == 0) {
|
||||
return beginningOfProgram;
|
||||
}
|
||||
for (int j=beginningOfProgram; j<k_historySize; j++) {
|
||||
if (m_history[j] == 0) {
|
||||
currentProgramNumber++;
|
||||
if (currentProgramNumber == i) {
|
||||
j++;
|
||||
while (m_history[j] == FreeSpaceMarker) {
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ProgramStore::lastIndexOfProgram(int i) const {
|
||||
int indexOfPrgm = indexOfProgram(i);
|
||||
int lastIndexOfProgram = indexOfPrgm + strlen(&m_history[indexOfPrgm]);
|
||||
return lastIndexOfProgram;
|
||||
}
|
||||
|
||||
int ProgramStore::indexOfFirstFreeSpaceMarker() const {
|
||||
for (int i=0; i<k_historySize; i++) {
|
||||
if (m_history[i] == FreeSpaceMarker) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ProgramStore::sizeOfFreeSpace() const {
|
||||
int sizeOfFreeSpace = 0;
|
||||
for (int i=0; i<k_historySize; i++) {
|
||||
if (m_history[i] == FreeSpaceMarker) {
|
||||
sizeOfFreeSpace++;
|
||||
} else {
|
||||
if (sizeOfFreeSpace > 0) {
|
||||
return sizeOfFreeSpace;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeOfFreeSpace;
|
||||
}
|
||||
|
||||
void ProgramStore::cleanFreeSpace() {
|
||||
if (m_lastProgramEdited < 0 || m_lastProgramEdited >= numberOfPrograms()) {
|
||||
return;
|
||||
}
|
||||
int indexOfCharToChangeIntoFreeSpaceMarker = lastIndexOfProgram(m_lastProgramEdited) + 1;
|
||||
while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != FreeSpaceMarker) {
|
||||
m_history[indexOfCharToChangeIntoFreeSpaceMarker] = FreeSpaceMarker;
|
||||
indexOfCharToChangeIntoFreeSpaceMarker ++;
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramStore::moveFreeSpaceAfterProgram(int i) {
|
||||
m_lastProgramEdited = i;
|
||||
|
||||
// If the free space is already just after the program, return
|
||||
int indexOfFreeSpace = indexOfFirstFreeSpaceMarker();
|
||||
int lastIndexOfPrgm = lastIndexOfProgram(i);
|
||||
if (indexOfFreeSpace == lastIndexOfPrgm + 1){
|
||||
return;
|
||||
}
|
||||
// Else move the free space depending on its relative position with the program
|
||||
int freeSpaceSize = sizeOfFreeSpace();
|
||||
if (indexOfFreeSpace > lastIndexOfPrgm) {
|
||||
int indexOfNextProgram = lastIndexOfPrgm+1;
|
||||
int len = indexOfFreeSpace - indexOfNextProgram;
|
||||
// Use memmove to avoid overwriting
|
||||
memmove(&m_history[indexOfNextProgram + freeSpaceSize], &m_history[indexOfNextProgram], len);
|
||||
for (int j = indexOfNextProgram; j<indexOfNextProgram+freeSpaceSize; j++) {
|
||||
m_history[j] = FreeSpaceMarker;
|
||||
}
|
||||
} else {
|
||||
int indexOfPrgm = indexOfProgram(i);
|
||||
int len = lastIndexOfPrgm - (indexOfFreeSpace + freeSpaceSize) + 1;
|
||||
// Use memcopy to avoid overwriting
|
||||
memcpy(&m_history[indexOfFreeSpace], &m_history[indexOfPrgm], len);
|
||||
for (int j = lastIndexOfPrgm - freeSpaceSize + 1 ; j<=lastIndexOfPrgm; j++) {
|
||||
m_history[j] = FreeSpaceMarker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramStore::cleanAndMoveFreeSpaceAfterProgram(int i) {
|
||||
if (i >= 0 && i<numberOfPrograms()){
|
||||
cleanFreeSpace();
|
||||
moveFreeSpaceAfterProgram(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgramStore::addDefaultProgram() {
|
||||
const char program[] = R"(# This program draws a Mandelbrot fractal set
|
||||
# N_iteration: degree of precision
|
||||
import kandinsky
|
||||
N_iteration = 10
|
||||
for x in range(320):
|
||||
for y in range(240):
|
||||
# Compute the mandelbrot sequence for the point c = (c_r, c_i) with start value z = (z_r, z_i)
|
||||
z_r = 0
|
||||
z_i = 0
|
||||
# Rescale to fit the drawing screen 320x222
|
||||
c_r = 2.7*x/319-2.1
|
||||
c_i = -1.87*y/221+0.935
|
||||
i = 0
|
||||
while (i < N_iteration) and ((z_r * z_r) + (z_i * z_i) < 4):
|
||||
i = i + 1
|
||||
stock = z_r
|
||||
z_r = z_r * z_r - z_i * z_i + c_r
|
||||
z_i = 2 * stock * z_i + c_i
|
||||
# Choose the color of the dot from the Mandelbrot sequence
|
||||
rgb = int(255*i/N_iteration)
|
||||
col = kandinsky.color(int(rgb),int(rgb*0.75),int(rgb*0.25))
|
||||
# Draw a pixel colored in 'col' at position (x,y)
|
||||
kandinsky.set_pixel(x,y,col))";
|
||||
|
||||
int len = strlen(program);
|
||||
if (len + 1 > sizeOfFreeSpace() - 1) { // We keep at keast one free char.
|
||||
return false;
|
||||
}
|
||||
cleanAndMoveFreeSpaceAfterProgram(numberOfPrograms()-1);
|
||||
memcpy(&m_history[indexOfFirstFreeSpaceMarker()], program, len+1);
|
||||
m_numberOfPrograms++;
|
||||
m_lastProgramEdited = m_numberOfPrograms-1;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
46
apps/code/program_store.h
Normal file
46
apps/code/program_store.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef CODE_PROGRAM_STORE_H
|
||||
#define CODE_PROGRAM_STORE_H
|
||||
|
||||
#include "program.h"
|
||||
|
||||
namespace Code {
|
||||
|
||||
class ProgramStore {
|
||||
public:
|
||||
ProgramStore();
|
||||
Program editableProgram(int i);
|
||||
/* editableProgram moves the free space of m_history at the end of the
|
||||
* ith script. It returns a Program object that points to the beginning of the
|
||||
* wanted script and has a length taking into account both the script and the
|
||||
* free space. */
|
||||
Program program(int i);
|
||||
/* programAtIndex returns a Program object that points to the beginning of the
|
||||
* ith script and has the length of the script. */
|
||||
int numberOfPrograms() const;
|
||||
Program editableNewProgram();
|
||||
bool addDefaultProgram();
|
||||
void deleteProgram(int i);
|
||||
void deleteAll();
|
||||
private:
|
||||
static constexpr char FreeSpaceMarker = 0x01;
|
||||
/* We made sure that 0x01 is not used in ion/include/ion/charset.h */
|
||||
static constexpr int k_historySize = 1024;
|
||||
int indexOfProgram(int i) const;
|
||||
int lastIndexOfProgram(int i) const;
|
||||
int indexOfFirstFreeSpaceMarker() const;
|
||||
int sizeOfFreeSpace() const;
|
||||
void cleanFreeSpace();
|
||||
void moveFreeSpaceAfterProgram(int i);
|
||||
void cleanAndMoveFreeSpaceAfterProgram(int i);
|
||||
char m_history[k_historySize];
|
||||
/* The m_history variable sequentially stores scripts as text buffers. The
|
||||
* free bytes of m_history contain the FreeSpaceMarker. By construction,
|
||||
* there is always at least one free byte, and the free space is always
|
||||
* continuous. */
|
||||
int m_numberOfPrograms;
|
||||
int m_lastProgramEdited;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -9,17 +9,19 @@
|
||||
|
||||
class TextArea : public ScrollableView, public ScrollViewDataSource {
|
||||
public:
|
||||
TextArea(Responder * parentResponder, char * textBuffer, size_t textBufferSize,
|
||||
TextArea(Responder * parentResponder, char * textBuffer = nullptr, size_t textBufferSize = 0,
|
||||
TextAreaDelegate * delegate = nullptr, KDText::FontSize fontSize = KDText::FontSize::Large,
|
||||
KDColor textColor = KDColorBlack, KDColor backgroundColor = KDColorWhite);
|
||||
|
||||
void setDelegate(TextAreaDelegate * delegate);
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void setText(char * textBuffer, size_t textBufferSize);
|
||||
|
||||
private:
|
||||
class Text {
|
||||
public:
|
||||
Text(char * buffer, size_t bufferSize);
|
||||
void setText(char * buffer, size_t bufferSize);
|
||||
|
||||
class Line {
|
||||
public:
|
||||
@@ -84,6 +86,7 @@ private:
|
||||
KDColor textColor, KDColor backgroundColor);
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
KDSize minimalSizeForOptimalDisplay() const override;
|
||||
void setText(char * textBuffer, size_t textBufferSize);
|
||||
void insertText(const char * text);
|
||||
void moveCursorIndex(int deltaX);
|
||||
void moveCursorGeo(int deltaX, int deltaY);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <escher/text_area.h>
|
||||
#include <escher/clipboard.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
@@ -15,7 +14,11 @@ TextArea::Text::Text(char * buffer, size_t bufferSize) :
|
||||
m_buffer(buffer),
|
||||
m_bufferSize(bufferSize)
|
||||
{
|
||||
assert(m_buffer != nullptr);
|
||||
}
|
||||
|
||||
void TextArea::Text::setText(char * buffer, size_t bufferSize) {
|
||||
m_buffer = buffer;
|
||||
m_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
TextArea::Text::Line::Line(const char * text) :
|
||||
@@ -41,6 +44,7 @@ TextArea::Text::LineIterator & TextArea::Text::LineIterator::operator++() {
|
||||
}
|
||||
|
||||
size_t TextArea::Text::indexAtPosition(Position p) {
|
||||
assert(m_buffer != nullptr);
|
||||
if (p.line() < 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -60,6 +64,7 @@ size_t TextArea::Text::indexAtPosition(Position p) {
|
||||
}
|
||||
|
||||
TextArea::Text::Position TextArea::Text::positionAtIndex(size_t index) {
|
||||
assert(m_buffer != nullptr);
|
||||
assert(index < m_bufferSize);
|
||||
const char * target = m_buffer + index;
|
||||
size_t y = 0;
|
||||
@@ -75,6 +80,7 @@ TextArea::Text::Position TextArea::Text::positionAtIndex(size_t index) {
|
||||
}
|
||||
|
||||
void TextArea::Text::insertChar(char c, size_t index) {
|
||||
assert(m_buffer != nullptr);
|
||||
assert(index < m_bufferSize-1);
|
||||
char previous = c;
|
||||
for (size_t i=index; i<m_bufferSize; i++) {
|
||||
@@ -88,6 +94,7 @@ void TextArea::Text::insertChar(char c, size_t index) {
|
||||
}
|
||||
|
||||
char TextArea::Text::removeChar(size_t index) {
|
||||
assert(m_buffer != nullptr);
|
||||
assert(index < m_bufferSize-1);
|
||||
char deletedChar = m_buffer[index];
|
||||
for (size_t i=index; i<m_bufferSize; i++) {
|
||||
@@ -100,6 +107,7 @@ char TextArea::Text::removeChar(size_t index) {
|
||||
}
|
||||
|
||||
int TextArea::Text::removeRemainingLine(size_t index, int direction) {
|
||||
assert(m_buffer != nullptr);
|
||||
assert(index >= 0 && index < m_bufferSize);
|
||||
int jump = index;
|
||||
while (m_buffer[jump] != '\n' && m_buffer[jump] != 0 && jump >= 0) {
|
||||
@@ -127,6 +135,7 @@ int TextArea::Text::removeRemainingLine(size_t index, int direction) {
|
||||
}
|
||||
|
||||
TextArea::Text::Position TextArea::Text::span() const {
|
||||
assert(m_buffer != nullptr);
|
||||
size_t width = 0;
|
||||
size_t height = 0;
|
||||
for (Line l : *this) {
|
||||
@@ -209,6 +218,11 @@ void TextArea::ContentView::layoutSubviews() {
|
||||
m_cursorView.setFrame(cursorRect());
|
||||
}
|
||||
|
||||
void TextArea::TextArea::ContentView::setText(char * textBuffer, size_t textBufferSize) {
|
||||
m_text.setText(textBuffer, textBufferSize);
|
||||
m_cursorIndex = 0;
|
||||
}
|
||||
|
||||
void TextArea::TextArea::ContentView::insertText(const char * text) {
|
||||
int textSize = strlen(text);
|
||||
if (m_text.textLength() + textSize >= m_text.bufferSize() || textSize == 0) {
|
||||
@@ -307,6 +321,10 @@ TextArea::TextArea(Responder * parentResponder, char * textBuffer,
|
||||
assert(textBufferSize < INT_MAX/2);
|
||||
}
|
||||
|
||||
void TextArea::setText(char * textBuffer, size_t textBufferSize) {
|
||||
m_contentView.setText(textBuffer, textBufferSize);
|
||||
}
|
||||
|
||||
bool TextArea::TextArea::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Left) {
|
||||
m_contentView.moveCursorIndex(-1);
|
||||
|
||||
Reference in New Issue
Block a user