[escher] XNT button is now cyclic

This commit is contained in:
Laury
2022-07-06 22:52:49 +02:00
parent 77167d1706
commit 51a5f699c3
30 changed files with 167 additions and 48 deletions

View File

@@ -60,6 +60,7 @@ apps_src += $(addprefix apps/,\
suspend_timer.cpp \
timer_manager.cpp \
title_bar_view.cpp \
xnt_loop.cpp \
)
tests_src += $(addprefix apps/,\

View File

@@ -18,6 +18,7 @@
#include "shared/global_context.h"
#include "clock_timer.h"
#include "on_boarding/prompt_controller.h"
#include "xnt_loop.h"
#include <ion/events.h>
@@ -48,6 +49,8 @@ public:
void displayExamModePopUp(GlobalPreferences::ExamMode mode);
void shutdownDueToLowBattery();
void setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus newStatus);
CodePoint XNT(CodePoint defaultXNT, bool * shouldRemoveLastCharacter) { m_XNTLoop.XNT(defaultXNT, shouldRemoveLastCharacter); }
void resetXNT() { m_XNTLoop.reset(); }
OnBoarding::PromptController * promptController();
void redrawWindow(bool force = false);
void activateExamMode(GlobalPreferences::ExamMode examMode);
@@ -85,6 +88,7 @@ private:
OnBoarding::App::Snapshot m_onBoardingSnapshot;
HardwareTest::App::Snapshot m_hardwareTestSnapshot;
USB::App::Snapshot m_usbConnectedSnapshot;
XNTLoop m_XNTLoop;
};
#endif

View File

@@ -129,9 +129,10 @@ VariableBoxController * App::variableBoxForInputEventHandler(InputEventHandler *
}
bool App::textInputDidReceiveEvent(InputEventHandler * textInput, Ion::Events::Event event) {
const char * pythonText = Helpers::PythonTextForEvent(event);
if (pythonText != nullptr) {
textInput->handleEventWithText(pythonText);
bool shouldRemoveLastCharacter = false;
char buffer[CodePoint::MaxCodePointCharLength + 1];
if (Helpers::PythonTextForEvent(event, buffer, &shouldRemoveLastCharacter)) {
textInput->handleEventWithText(buffer, false, false, shouldRemoveLastCharacter);
return true;
}
return false;

View File

@@ -5,6 +5,7 @@
#include <escher/metric.h>
#include <ion.h>
#include "../global_preferences.h"
#include <apps/apps_container.h>
using namespace Shared;
@@ -72,6 +73,10 @@ void EditorController::viewDidDisappear() {
m_menuController->scriptContentEditionDidFinish();
}
void EditorController::textAreaDidReceiveNoneXNTEvent() {
AppsContainer::sharedAppsContainer()->resetXNT();
}
bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) {
if (App::app()->textInputDidReceiveEvent(textArea, event)) {
return true;
@@ -81,7 +86,6 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events:
return true;
}
if (event == Ion::Events::Backspace && textArea->selectionIsEmpty()) {
/* If the cursor is on the left of the text of a line, backspace one
* indentation space at a time. */

View File

@@ -30,6 +30,7 @@ public:
TELEMETRY_ID("Editor");
/* TextAreaDelegate */
void textAreaDidReceiveNoneXNTEvent() override;
bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) override;
/* InputEventHandlerDelegate */

View File

@@ -1,20 +1,24 @@
#include "helpers.h"
#include <escher/clipboard.h>
#include <apps/apps_container.h>
namespace Code {
namespace Helpers {
const char * PythonTextForEvent(Ion::Events::Event event) {
bool PythonTextForEvent(Ion::Events::Event event, char * buffer, bool * shouldRemoveLastCharacter) {
for (size_t i=0; i<NumberOfPythonTextPairs; i++) {
UTF8Helper::TextPair pair = PythonTextPairs[i];
if (event.text() == pair.firstString()) {
return pair.secondString();
strcpy(buffer, pair.secondString());
return true;
}
if (event == Ion::Events::XNT) {
return "x";
CodePoint XNT = AppsContainer::sharedAppsContainer()->XNT('x', shouldRemoveLastCharacter);
buffer[UTF8Decoder::CodePointToChars(XNT, buffer, CodePoint::MaxCodePointCharLength + 1)] = 0;
return true;
}
return false;
}
return nullptr;
}
}
}

View File

@@ -6,7 +6,7 @@
namespace Code {
namespace Helpers {
const char * PythonTextForEvent(Ion::Events::Event event);
bool PythonTextForEvent(Ion::Events::Event event, char * buffer, bool * shouldRemoveLastCharacter);
}
}

View File

@@ -426,14 +426,14 @@ bool PythonTextArea::handleEvent(Ion::Events::Event event) {
return result;
}
bool PythonTextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) {
bool PythonTextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText, bool shouldRemoveLastCharacter) {
if (*text == 0) {
return false;
}
if (m_contentView.isAutocompleting()) {
removeAutocompletion();
}
bool result = TextArea::handleEventWithText(text, indentation, forceCursorRightOfText);
bool result = TextArea::handleEventWithText(text, indentation, forceCursorRightOfText, shouldRemoveLastCharacter);
addAutocompletion();
return result;
}
@@ -493,9 +493,10 @@ bool PythonTextArea::addAutocompletionTextAtIndex(int nextIndex, int * currentIn
if (textToInsertLength > 0) {
// Try to insert the text (this might fail if the buffer is full)
if (!m_contentView.insertTextAtLocation(textToInsert, const_cast<char *>(autocompletionLocation), textToInsertLength)) {
if (!m_contentView.isAbleToInsertTextAt(textToInsertLength, autocompletionLocation, false)) {
return false;
}
m_contentView.insertTextAtLocation(textToInsert, const_cast<char *>(autocompletionLocation), textToInsertLength);
autocompletionLocation += textToInsertLength;
m_contentView.setAutocompleting(true);
m_contentView.setAutocompletionEnd(autocompletionLocation);
@@ -507,7 +508,8 @@ bool PythonTextArea::addAutocompletionTextAtIndex(int nextIndex, int * currentIn
assert(strlen(parentheses) == parenthesesLength);
/* If couldInsertText is false, we should not try to add the parentheses as
* there was already not enough space to add the autocompletion. */
if (addParentheses && m_contentView.insertTextAtLocation(parentheses, const_cast<char *>(autocompletionLocation), parenthesesLength)) {
if (addParentheses && m_contentView.isAbleToInsertTextAt(parenthesesLength, autocompletionLocation, false)) {
m_contentView.insertTextAtLocation(parentheses, const_cast<char *>(autocompletionLocation), parenthesesLength);
m_contentView.setAutocompleting(true);
m_contentView.setAutocompletionEnd(autocompletionLocation + parenthesesLength);
return true;

View File

@@ -23,7 +23,7 @@ public:
void loadSyntaxHighlighter() { m_contentView.loadSyntaxHighlighter(); }
void unloadSyntaxHighlighter() { m_contentView.unloadSyntaxHighlighter(); }
bool handleEvent(Ion::Events::Event event) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false, bool shouldRemoveLastCharacter = false) override;
/* autocompletionType returns:
* - EndOfIdentifier if there is currently autocompletion, or if the cursor is
* at the end of an identifier,

View File

@@ -9,6 +9,7 @@ namespace Shared {
class ExpressionFieldDelegateApp : public TextFieldDelegateApp, public LayoutFieldDelegate {
public:
virtual ~ExpressionFieldDelegateApp() = default;
void layoutFieldDidReceiveNoneXNTEvent() override { AppsContainer::sharedAppsContainer()->resetXNT(); }
bool layoutFieldShouldFinishEditing(LayoutField * layoutField, Ion::Events::Event event) override;
bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) override;
protected:

View File

@@ -2,11 +2,13 @@
#define SHARED_LAYOUT_FIELD_DELEGATE_H
#include "expression_field_delegate_app.h"
#include "../apps_container.h"
namespace Shared {
class LayoutFieldDelegate : public ::LayoutFieldDelegate {
public:
void layoutFieldDidReceiveNoneXNTEvent() override { AppsContainer::sharedAppsContainer()->resetXNT(); }
bool layoutFieldShouldFinishEditing(LayoutField * layoutField, Ion::Events::Event event) override;
bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) override;
bool layoutFieldDidFinishEditing(LayoutField * layoutField, Poincare::Layout layoutR, Ion::Events::Event event) override;

View File

@@ -2,11 +2,13 @@
#define SHARED_TEXT_FIELD_DELEGATE_H
#include "text_field_delegate_app.h"
#include "../apps_container.h"
namespace Shared {
class TextFieldDelegate : public ::TextFieldDelegate {
public:
void textFieldDidReceiveNoneXNTEvent() override { AppsContainer::sharedAppsContainer()->resetXNT(); }
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
protected:

View File

@@ -72,10 +72,12 @@ bool TextFieldDelegateApp::fieldDidReceiveEvent(EditableField * field, Responder
if (XNTCanBeOverriden()) {
xnt = field->XNTCodePoint(xnt);
}
bool shouldRemoveLastCharacter = false;
xnt = AppsContainer::sharedAppsContainer()->XNT(xnt, &shouldRemoveLastCharacter);
size_t length = UTF8Decoder::CodePointToChars(xnt, buffer, bufferSize);
assert(length < bufferSize - 1);
buffer[length] = 0;
return field->handleEventWithText(buffer);
return field->handleEventWithText(buffer, false, false, shouldRemoveLastCharacter);
}
return false;
}

View File

@@ -5,6 +5,7 @@
#include "input_event_handler_delegate_app.h"
#include <escher/text_field_delegate.h>
#include <apps/i18n.h>
#include "../apps_container.h"
class EditableField;
@@ -16,6 +17,7 @@ public:
Poincare::Context * localContext() override;
virtual bool XNTCanBeOverriden() const { return true; }
virtual CodePoint XNT() { return 'x'; }
virtual void textFieldDidReceiveNoneXNTEvent() override { AppsContainer::sharedAppsContainer()->resetXNT(); }
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
bool isAcceptableText(const char * text);

22
apps/xnt_loop.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include "xnt_loop.h"
#include <assert.h>
CodePoint XNTLoop::XNT(CodePoint defaultCodePoint, bool * shouldRemoveLastCharacter) {
assert(shouldRemoveLastCharacter != nullptr);
static constexpr CodePoint XNTCodePoints[] = {'x', 'n', 't', UCodePointGreekSmallLetterTheta};
int XNTCodePointSize = sizeof(XNTCodePoints) / sizeof(CodePoint);
if (m_loopIndex == -1) {
for (int i = 0; i < XNTCodePointSize; i++) {
if (XNTCodePoints[i] == defaultCodePoint) {
m_loopIndex = i;
break;
}
}
} else {
*shouldRemoveLastCharacter = true;
m_loopIndex = (m_loopIndex + 1) % XNTCodePointSize;
}
return XNTCodePoints[m_loopIndex];
}

15
apps/xnt_loop.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef APPS_XNT_LOOP
#define APPS_XNT_LOOP
#include <ion/unicode/code_point.h>
class XNTLoop {
public:
XNTLoop(): m_loopIndex(-1) {}
void reset() { m_loopIndex = -1; }
CodePoint XNT(CodePoint defaultCodePoint, bool * shouldRemoveLastCharacter);
private:
int m_loopIndex;
};
#endif

View File

@@ -10,7 +10,7 @@ class InputEventHandlerDelegate;
class InputEventHandler {
public:
InputEventHandler(InputEventHandlerDelegate * inputEventHandlerdelegate) : m_inputEventHandlerDelegate(inputEventHandlerdelegate) {}
virtual bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) { return false; }
virtual bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false, bool shouldRemoveLastCharacter = false) { return false; }
protected:
bool handleBoxEvent(Ion::Events::Event event);
InputEventHandlerDelegate * m_inputEventHandlerDelegate;

View File

@@ -27,12 +27,14 @@ public:
void abortEditionAndDismiss();
/* TextFieldDelegate */
void textFieldDidReceiveNoneXNTEvent() override;
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
bool textFieldDidAbortEditing(TextField * textField) override;
/* LayoutFieldDelegate */
void layoutFieldDidReceiveNoneXNTEvent() override;
bool layoutFieldShouldFinishEditing(LayoutField * layoutField, Ion::Events::Event event) override;
bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) override;
bool layoutFieldDidFinishEditing(LayoutField * layoutField, Poincare::Layout layoutR, Ion::Events::Event event) override;

View File

@@ -45,7 +45,7 @@ public:
}
/* Responder */
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false, bool shouldRemoveLastCharacter = false) override;
bool handleEvent(Ion::Events::Event event) override;
// TODO: factorize with TextField (see TODO of EditableField)
bool shouldFinishEditing(Ion::Events::Event event) override;

View File

@@ -9,6 +9,7 @@ class LayoutField;
class LayoutFieldDelegate : public ContextProvider{
public:
virtual void layoutFieldDidReceiveNoneXNTEvent() {};
virtual bool layoutFieldShouldFinishEditing(LayoutField * layoutField, Ion::Events::Event event) = 0;
virtual bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) = 0;
virtual bool layoutFieldDidFinishEditing(LayoutField * layoutField, Poincare::Layout layoutR, Ion::Events::Event event) { return false; }

View File

@@ -16,7 +16,7 @@ public:
TextArea(Responder * parentResponder, View * contentView, const KDFont * font = KDFont::LargeFont);
void setDelegates(InputEventHandlerDelegate * inputEventHandlerDelegate, TextAreaDelegate * delegate) { m_inputEventHandlerDelegate = inputEventHandlerDelegate; m_delegate = delegate; }
bool handleEvent(Ion::Events::Event event) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false, bool shouldRemoveLastCharacter = false) override;
void setText(char * textBuffer, size_t textBufferSize);
protected:
@@ -120,7 +120,8 @@ protected:
const char * editedText() const override { return m_text.text(); }
size_t editedTextLength() const override { return m_text.textLength(); }
const Text * getText() const { return &m_text; }
bool insertTextAtLocation(const char * text, char * location, int textLength = -1) override;
bool isAbleToInsertTextAt(int textLength, const char * location, bool shouldRemoveLastCharacter) const override;
void insertTextAtLocation(const char * text, char * location, int textLength = -1) override;
void moveCursorGeo(int deltaX, int deltaY);
bool removePreviousGlyph() override;
bool removeEndOfLine() override;

View File

@@ -5,6 +5,7 @@ class TextArea;
class TextAreaDelegate {
public:
virtual void textAreaDidReceiveNoneXNTEvent() {};
virtual bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) = 0;
};

View File

@@ -34,7 +34,7 @@ public:
void setText(const char * text);
void setEditing(bool isEditing) override { m_contentView.setEditing(isEditing); }
CodePoint XNTCodePoint(CodePoint defaultXNTCodePoint) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false, bool shouldRemoveLastCharacter = false) override;
bool handleEvent(Ion::Events::Event event) override;
constexpr static int maxBufferSize() {
return ContentView::k_maxBufferSize;
@@ -71,10 +71,11 @@ protected:
void reinitDraftTextBuffer();
void setDraftTextBufferSize(size_t size) { assert(size <= k_maxBufferSize); m_draftTextBufferSize = size; }
size_t draftTextBufferSize() const { return m_draftTextBufferSize; }
bool isAbleToInsertTextAt(int textLength, const char * location, bool shouldRemoveLastCharacter) const override;
/* If the text to be appended is too long to be added without overflowing the
* buffer, nothing is done (not even adding few letters from the text to reach
* the maximum buffer capacity) and false is returned. */
bool insertTextAtLocation(const char * text, char * location, int textLength = -1) override;
void insertTextAtLocation(const char * text, char * location, int textLength = -1) override;
KDSize minimalSizeForOptimalDisplay() const override;
bool removePreviousGlyph() override;
bool removeEndOfLine() override;

View File

@@ -7,6 +7,7 @@ class TextField;
class TextFieldDelegate {
public:
virtual void textFieldDidReceiveNoneXNTEvent() {};
virtual bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) = 0;
virtual bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) = 0;
virtual bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { return false; }

View File

@@ -49,7 +49,8 @@ protected:
// Virtual text get/add/remove
virtual const char * text() const = 0;
virtual bool insertTextAtLocation(const char * text, char * location, int textLength = -1) = 0;
virtual bool isAbleToInsertTextAt(int textLength, const char * location, bool shouldRemoveLastCharacter) const = 0;
virtual void insertTextAtLocation(const char * text, char * location, int textLength) = 0;
virtual bool removePreviousGlyph() = 0;
virtual bool removeEndOfLine() = 0;
@@ -92,7 +93,7 @@ protected:
/* If the text to be appended is too long to be added without overflowing the
* buffer, nothing is done (not even adding few letters from the text to reach
* the maximum buffer capacity) and false is returned. */
bool insertTextAtLocation(const char * textBuffer, char * location);
void insertTextAtLocation(const char * textBuffer, char * location, int textLength);
bool removeEndOfLine();
ContentView * contentView() {
return const_cast<ContentView *>(nonEditableContentView());

View File

@@ -58,6 +58,14 @@ bool InputViewController::textFieldDidAbortEditing(TextField * textField) {
return true;
}
void InputViewController::textFieldDidReceiveNoneXNTEvent() {
m_textFieldDelegate->textFieldDidReceiveNoneXNTEvent();
}
void InputViewController::layoutFieldDidReceiveNoneXNTEvent() {
m_layoutFieldDelegate->layoutFieldDidReceiveNoneXNTEvent();
}
bool InputViewController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
return m_textFieldDelegate->textFieldDidReceiveEvent(textField, event);
}

View File

@@ -355,7 +355,7 @@ void LayoutField::reload(KDSize previousSize) {
markRectAsDirty(bounds());
}
bool LayoutField::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) {
bool LayoutField::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText, bool shouldRemoveLastCharacter) {
/* The text here can be:
* - the result of a key pressed, such as "," or "cos(•)"
* - the text added after a toolbox selection
@@ -417,6 +417,9 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool
if (currentNumberOfLayouts + resultLayout.numberOfDescendants(true) >= k_maxNumberOfLayouts) {
return true;
}
if (shouldRemoveLastCharacter) {
cursor->performBackspace();
}
insertLayoutAtCursor(resultLayout, resultExpression, forceCursorRightOfText);
}
return true;
@@ -431,6 +434,9 @@ bool LayoutField::shouldFinishEditing(Ion::Events::Event event) {
}
bool LayoutField::handleEvent(Ion::Events::Event event) {
if (m_delegate && event != Ion::Events::XNT) {
m_delegate->layoutFieldDidReceiveNoneXNTEvent();
}
bool didHandleEvent = false;
KDSize previousSize = minimalSizeForOptimalDisplay();
bool shouldRecomputeLayout = m_contentView.cursor()->showEmptyLayoutIfNeeded();

View File

@@ -33,7 +33,7 @@ static inline void InsertSpacesAtLocation(int spacesCount, char * buffer, int bu
}
}
bool TextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) {
bool TextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText, bool shouldRemoveLastCharacter) {
if (*text == 0) {
return false;
}
@@ -123,11 +123,19 @@ bool TextArea::handleEventWithText(const char * text, bool indentation, bool for
return false;
}
// Insert the text
if (!insertTextAtLocation(text, insertionPosition)) {
int textLength = strlen(text);
if (!contentView()->isAbleToInsertTextAt(textLength, insertionPosition, shouldRemoveLastCharacter)) {
return true;
}
if (shouldRemoveLastCharacter) {
removePreviousGlyph();
insertionPosition = const_cast<char *>(cursorLocation());
}
// Insert the text
insertTextAtLocation(text, insertionPosition, textLength);
// Insert the indentation
if (indentation) {
UTF8Helper::PerformAtCodePoints(
@@ -160,6 +168,9 @@ bool TextArea::handleEventWithText(const char * text, bool indentation, bool for
}
bool TextArea::handleEvent(Ion::Events::Event event) {
if (m_delegate != nullptr && event != Ion::Events::XNT) {
m_delegate->textAreaDidReceiveNoneXNTEvent();
}
if (m_delegate != nullptr && m_delegate->textAreaDidReceiveEvent(this, event)) {
return true;
}
@@ -575,12 +586,21 @@ void TextArea::ContentView::setText(char * textBuffer, size_t textBufferSize) {
m_cursorLocation = text();
}
bool TextArea::ContentView::insertTextAtLocation(const char * text, char * location, int textLength) {
bool TextArea::ContentView::isAbleToInsertTextAt(int textLength, const char * location, bool shouldRemoveLastCharacter) const {
int removedCharacters = 0;
if (shouldRemoveLastCharacter) {
UTF8Decoder decoder(m_text.text(), location);
const char * previousGlyphPos = decoder.previousGlyphPosition();
assert(previousGlyphPos != nullptr);
removedCharacters = location - previousGlyphPos;
}
return m_text.textLength() + textLength - removedCharacters < m_text.bufferSize() && textLength != 0;
}
void TextArea::ContentView::insertTextAtLocation(const char * text, char * location, int textLength) {
int textLen = textLength < 0 ? strlen(text) : textLength;
assert(textLen < 0 || textLen <= strlen(text));
if (m_text.textLength() + textLen >= m_text.bufferSize() || textLen == 0) {
return false;
}
assert(isAbleToInsertTextAt(textLen, location, false));
// Scan for \n
bool lineBreak = UTF8Helper::HasCodePoint(text, '\n', text + textLen);
@@ -589,7 +609,6 @@ bool TextArea::ContentView::insertTextAtLocation(const char * text, char * locat
// Replace System parentheses (used to keep layout tree structure) by normal parentheses
Poincare::SerializationHelper::ReplaceSystemParenthesesByUserParentheses(location, textLen);
reloadRectFromPosition(location, lineBreak);
return true;
}
bool TextArea::ContentView::removePreviousGlyph() {

View File

@@ -119,14 +119,23 @@ void TextField::ContentView::reinitDraftTextBuffer() {
setCursorLocation(s_draftTextBuffer);
}
bool TextField::ContentView::insertTextAtLocation(const char * text, char * location, int textLen) {
bool TextField::ContentView::isAbleToInsertTextAt(int textLength, const char * location, bool shouldRemoveLastCharacter) const {
int removedCharacters = 0;
if (shouldRemoveLastCharacter) {
UTF8Decoder decoder(s_draftTextBuffer, location);
const char * previousGlyphPos = decoder.previousGlyphPosition();
assert(previousGlyphPos != nullptr);
removedCharacters = location - previousGlyphPos;
}
return m_currentDraftTextLength + textLength - removedCharacters < m_draftTextBufferSize && textLength != 0;
}
void TextField::ContentView::insertTextAtLocation(const char * text, char * location, int textLen) {
assert(m_isEditing);
size_t textLength = textLen < 0 ? strlen(text) : (size_t)textLen;
// TODO when paste fails because of a too big message, create a pop-up
if (m_currentDraftTextLength + textLength >= m_draftTextBufferSize || textLength == 0) {
return false;
}
assert(isAbleToInsertTextAt(textLength, location, false));
memmove(location + textLength, location, (s_draftTextBuffer + m_currentDraftTextLength + 1) - location);
@@ -139,7 +148,6 @@ bool TextField::ContentView::insertTextAtLocation(const char * text, char * loca
m_currentDraftTextLength += copySize-1; // Do no count the null-termination
reloadRectFromPosition(m_horizontalAlignment == 0.0f ? location : s_draftTextBuffer);
return true;
}
KDSize TextField::ContentView::minimalSizeForOptimalDisplay() const {
@@ -416,6 +424,9 @@ CodePoint TextField::XNTCodePoint(CodePoint defaultXNTCodePoint) {
bool TextField::handleEvent(Ion::Events::Event event) {
assert(m_delegate != nullptr);
if (event != Ion::Events::XNT) {
m_delegate->textFieldDidReceiveNoneXNTEvent();
}
size_t previousTextLength = strlen(text());
bool didHandleEvent = false;
if (privateHandleMoveEvent(event)) {
@@ -486,7 +497,7 @@ bool TextField::privateHandleSelectEvent(Ion::Events::Event event) {
return false;
}
bool TextField::handleEventWithText(const char * eventText, bool indentation, bool forceCursorRightOfText) {
bool TextField::handleEventWithText(const char * eventText, bool indentation, bool forceCursorRightOfText, bool shouldRemoveLastCharacter) {
size_t previousTextLength = strlen(text());
if (!isEditing()) {
@@ -518,7 +529,13 @@ bool TextField::handleEventWithText(const char * eventText, bool indentation, bo
// Replace System parentheses (used to keep layout tree structure) by normal parentheses
Poincare::SerializationHelper::ReplaceSystemParenthesesByUserParentheses(buffer);
if (insertTextAtLocation(buffer, const_cast<char *>(cursorLocation()))) {
int textLength = strlen(buffer);
if (contentView()->isAbleToInsertTextAt(textLength, cursorLocation(), shouldRemoveLastCharacter)) {
if (shouldRemoveLastCharacter) {
removePreviousGlyph();
}
insertTextAtLocation(buffer, const_cast<char *>(cursorLocation()), textLength);
/* The cursor position depends on the text as we sometimes want to position
* the cursor at the end of the text and sometimes after the first
* parenthesis. */

View File

@@ -170,15 +170,13 @@ void TextInput::setAlignment(float horizontalAlignment, float verticalAlignment)
contentView()->setAlignment(horizontalAlignment, verticalAlignment);
}
bool TextInput::insertTextAtLocation(const char * text, char * location) {
if (contentView()->insertTextAtLocation(text, location)) {
/* We layout the scrollable view before scrolling to cursor because the
* content size might have changed. */
layoutSubviews();
scrollToCursor();
return true;
}
return false;
void TextInput::insertTextAtLocation(const char * text, char * location, int textLength) {
assert(contentView()->isAbleToInsertTextAt(textLength, location, false));
contentView()->insertTextAtLocation(text, location, textLength);
/* We layout the scrollable view before scrolling to cursor because the
* content size might have changed. */
layoutSubviews();
scrollToCursor();
}
bool TextInput::removeEndOfLine() {