#include #include #include #include #include #include #include #include static inline size_t minSize(size_t a, size_t b) { return a < b ? a : b; } static inline size_t maxSize(size_t a, size_t b) { return a > b ? a : b; } /* TextArea */ TextArea::TextArea(Responder * parentResponder, View * contentView, const KDFont * font) : TextInput(parentResponder, contentView), InputEventHandler(nullptr), m_delegate(nullptr) { } bool TextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) { const char * nextCursorLocation = cursorLocation(); const char * cursorPositionInCommand = TextInputHelpers::CursorPositionInCommand(text); constexpr int bufferSize = TextField::maxBufferSize(); char buffer[bufferSize]; // Remove the Empty code points UTF8Helper::CopyAndRemoveCodePoint(buffer, bufferSize, text, UCodePointEmpty, &cursorPositionInCommand); // Insert the text if ((indentation && insertTextWithIndentation(buffer, cursorLocation())) || insertTextAtLocation(buffer, cursorLocation())) { // Set the cursor location if (forceCursorRightOfText) { nextCursorLocation += strlen(buffer); } else { nextCursorLocation += cursorPositionInCommand - text; } } setCursorLocation(nextCursorLocation); return true; } bool TextArea::handleEvent(Ion::Events::Event event) { if (m_delegate != nullptr && m_delegate->textAreaDidReceiveEvent(this, event)) { return true; } else if (handleBoxEvent(app(), event)) { return true; } else if (event == Ion::Events::Left) { if (cursorLocation() <= text()) { assert(cursorLocation() == text()); return false; } UTF8Decoder decoder(text(), cursorLocation()); decoder.previousCodePoint(); return setCursorLocation(decoder.stringPosition()); } else if (event == Ion::Events::Right) { if (*cursorLocation() == 0) { return false; } UTF8Decoder decoder(text(), cursorLocation()); decoder.nextCodePoint(); return setCursorLocation(decoder.stringPosition()); } else if (event == Ion::Events::Up) { contentView()->moveCursorGeo(0, -1); } else if (event == Ion::Events::Down) { contentView()->moveCursorGeo(0, 1); } else if (event == Ion::Events::ShiftLeft) { contentView()->moveCursorGeo(-INT_MAX/2, 0); } else if (event == Ion::Events::ShiftRight) { contentView()->moveCursorGeo(INT_MAX/2, 0); } else if (event == Ion::Events::Backspace) { return removeCodePoint(); } else if (event.hasText()) { return handleEventWithText(event.text()); } else if (event == Ion::Events::EXE) { return handleEventWithText("\n"); } else if (event == Ion::Events::Clear) { if (!contentView()->removeEndOfLine()) { contentView()->removeStartOfLine(); } } else if (event == Ion::Events::Paste) { return handleEventWithText(Clipboard::sharedClipboard()->storedText()); } else { return false; } scrollToCursor(); return true; } void TextArea::setText(char * textBuffer, size_t textBufferSize) { contentView()->setText(textBuffer, textBufferSize); contentView()->moveCursorGeo(0, 0); } bool TextArea::insertTextWithIndentation(const char * textBuffer, const char * location) { // Compute the indentation int indentation = indentationBeforeCursor(); const char * buffer = contentView()->text(); if (cursorLocation() > buffer && UTF8Helper::PreviousCodePointIs(buffer, cursorLocation(), ':')) { indentation += k_indentationSpaces; } // Check the text will not overflow the buffer int totalIndentationSize = UTF8Helper::CountOccurrences(textBuffer, '\n') * indentation; int textSize = strlen(textBuffer); if (contentView()->getText()->textLength() + textSize + totalIndentationSize >= contentView()->getText()->bufferSize() || textSize == 0) { return false; } // Insert the text insertTextAtLocation(textBuffer, location); // Insert the indentation UTF8Helper::PerformAtCodePoints( textBuffer, '\n', [](char * codePointLocation, void * text, int indentation) { ((Text *)text)->insertSpacesAtLocation(indentation, codePointLocation); }, [](char * c1, void * c2, int c3) { }, (void *)(contentView()->getText()), indentation); return true; } int TextArea::indentationBeforeCursor() const { int indentationSize = 0; /* Compute the number of spaces at the beginning of the line. Increase the * indentation size when encountering spaces, reset it to 0 when encountering * another code point, until reaching the beginning of the line. */ UTF8Helper::PerformAtCodePoints(const_cast