Files
Upsilon/apps/code/editor_controller.cpp
Ruben Dashyan 5015bc231b [apps/code/editor_controller] Fix backspace event handling
If there are only spaces on the left of the cursor, then a backspace
should remove two spaces (or one if there is only one of it). The number
of spaces was miscomputed.
2019-06-06 10:00:32 +02:00

133 lines
4.9 KiB
C++

#include "editor_controller.h"
#include "menu_controller.h"
#include "script_parameter_controller.h"
#include "app.h"
#include <escher/metric.h>
#include <ion.h>
using namespace Shared;
namespace Code {
EditorController::EditorController(MenuController * menuController, App * pythonDelegate) :
ViewController(nullptr),
m_editorView(this, pythonDelegate),
m_script(Ion::Storage::Record()),
m_menuController(menuController)
{
m_editorView.setTextAreaDelegates(this, this);
}
void EditorController::setScript(Script script) {
m_script = script;
Script::Data scriptData = m_script.value();
size_t availableScriptSize = scriptData.size + Ion::Storage::sharedStorage()->availableSize();
assert(sizeof(m_areaBuffer) >= availableScriptSize);
// We cannot use strlcpy as the first char reprensenting the importation status can be 0.
memcpy(m_areaBuffer, (const char *)scriptData.buffer, scriptData.size);
m_editorView.setText(m_areaBuffer+1, availableScriptSize-1); // 1 char is taken by the importation status flag
}
// TODO: this should be done in textAreaDidFinishEditing maybe??
bool EditorController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::Back || event == Ion::Events::Home) {
saveScript();
stackController()->pop();
return event != Ion::Events::Home;
}
return false;
}
void EditorController::didBecomeFirstResponder() {
app()->setFirstResponder(&m_editorView);
}
void EditorController::viewWillAppear() {
m_editorView.loadSyntaxHighlighter();
m_editorView.setCursorLocation(m_editorView.text() + strlen(m_editorView.text()));
}
void EditorController::viewDidDisappear() {
m_menuController->scriptContentEditionDidFinish();
}
bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) {
if (event == Ion::Events::Var) {
/* We save the script before displaying the Variable box to add new
* functions or variables. */
saveScript();
return false;
}
if (static_cast<App *>(textArea->app())->textInputDidReceiveEvent(textArea, event)) {
return true;
}
if (event == Ion::Events::EXE) {
textArea->handleEventWithText("\n", true, false);
return true;
}
if (event == Ion::Events::Backspace) {
/* If the cursor is on the left of the text of a line, backspace one
* indentation space at a time. */
const char * text = textArea->text();
const char * cursorLocation = textArea->cursorLocation();
const char * firstNonSpace = UTF8Helper::NotCodePointSearch(text, ' ', true, cursorLocation);
assert(firstNonSpace >= text);
bool cursorIsPrecededOnTheLineBySpacesOnly = false;
size_t numberOfSpaces = cursorLocation - firstNonSpace;
if (UTF8Helper::CodePointIs(firstNonSpace, '\n')) {
cursorIsPrecededOnTheLineBySpacesOnly = true;
numberOfSpaces -= UTF8Decoder::CharSizeOfCodePoint('\n');
} else if (firstNonSpace == text) {
cursorIsPrecededOnTheLineBySpacesOnly = true;
}
numberOfSpaces = numberOfSpaces / UTF8Decoder::CharSizeOfCodePoint(' ');
if (cursorIsPrecededOnTheLineBySpacesOnly && numberOfSpaces >= k_indentationSpacesNumber) {
for (int i = 0; i < k_indentationSpacesNumber; i++) {
textArea->removeCodePoint();
}
return true;
}
} else if (event == Ion::Events::Space) {
/* If the cursor is on the left of the text of a line, a space triggers an
* indentation. */
const char * text = textArea->text();
const char * firstNonSpace = UTF8Helper::NotCodePointSearch(text, ' ', true, textArea->cursorLocation());
assert(firstNonSpace >= text);
if (UTF8Helper::CodePointIs(firstNonSpace, '\n')) {
assert(UTF8Decoder::CharSizeOfCodePoint(' ') == 1);
char indentationBuffer[k_indentationSpacesNumber+1];
for (int i = 0; i < k_indentationSpacesNumber; i++) {
indentationBuffer[i] = ' ';
}
indentationBuffer[k_indentationSpacesNumber] = 0;
textArea->handleEventWithText(indentationBuffer);
return true;
}
}
return false;
}
VariableBoxController * EditorController::variableBoxForInputEventHandler(InputEventHandler * textInput) {
VariableBoxController * varBox = static_cast<App *>(app())->variableBoxController();
varBox->loadFunctionsAndVariables();
return varBox;
}
InputEventHandlerDelegateApp * EditorController::inputEventHandlerDelegateApp() {
return static_cast<App *>(app());
}
StackViewController * EditorController::stackController() {
return static_cast<StackViewController *>(parentResponder());
}
void EditorController::saveScript() {
size_t sizeOfValue = strlen(m_areaBuffer+1)+1+1; // size of scriptContent + size of importation status
Script::ErrorStatus err = m_script.setValue({.buffer=m_areaBuffer, .size=sizeOfValue});
assert(err != Script::ErrorStatus::NotEnoughSpaceAvailable && err != Script::ErrorStatus::RecordDoesNotExist); // This should not happen as we set the text area according to the available space in the Kallax
(void) err;
}
}