diff --git a/apps/code/python_text_area.cpp b/apps/code/python_text_area.cpp index 07ced50cd..5a6a4d1d8 100644 --- a/apps/code/python_text_area.cpp +++ b/apps/code/python_text_area.cpp @@ -18,6 +18,7 @@ constexpr KDColor KeywordColor = KDColor::RGB24(0xFF000C); constexpr KDColor OperatorColor = KDColor::RGB24(0xd73a49); constexpr KDColor StringColor = KDColor::RGB24(0x032f62); constexpr KDColor BackgroundColor = KDColorWhite; +constexpr KDColor HighlightColor = KDColorRed; //TODO LEA static inline const char * minPointer(const char * x, const char * y) { return x < y ? x : y; } @@ -71,7 +72,7 @@ void PythonTextArea::ContentView::clearRect(KDContext * ctx, KDRect rect) const #define LOG_DRAW(...) #endif -void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t byteLength, int fromColumn, int toColumn) const { +void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t byteLength, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const { LOG_DRAW("Drawing \"%.*s\"\n", byteLength, text); if (!m_pythonDelegate->isPythonUser(this)) { @@ -84,7 +85,10 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char lineStart, minPointer(text + byteLength, lineEnd) - lineStart, StringColor, - BackgroundColor + BackgroundColor, + selectionStart, + selectionEnd, + HighlightColor ); return; } @@ -116,7 +120,10 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char tokenFrom, tokenLength, TokenColor(lex->tok_kind), - BackgroundColor + BackgroundColor, + selectionStart, + selectionEnd, + HighlightColor ); mp_lexer_to_next(lex); @@ -131,7 +138,11 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char tokenFrom, text + byteLength - tokenFrom, CommentColor, - BackgroundColor); + BackgroundColor, + selectionStart, + selectionEnd, + HighlightColor + ); } mp_lexer_free(lex); diff --git a/apps/code/python_text_area.h b/apps/code/python_text_area.h index a16b5006b..277d0fba3 100644 --- a/apps/code/python_text_area.h +++ b/apps/code/python_text_area.h @@ -27,7 +27,7 @@ protected: void loadSyntaxHighlighter(); void unloadSyntaxHighlighter(); void clearRect(KDContext * ctx, KDRect rect) const override; - void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn) const override; + void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const override; KDRect dirtyRectFromPosition(const char * position, bool lineBreak) const override; private: App * m_pythonDelegate; diff --git a/escher/include/escher/clipboard.h b/escher/include/escher/clipboard.h index 674e5e5f4..46a105e9a 100644 --- a/escher/include/escher/clipboard.h +++ b/escher/include/escher/clipboard.h @@ -7,7 +7,7 @@ class Clipboard { public: static Clipboard * sharedClipboard(); - void store(const char * storedText); + void store(const char * storedText, int length = -1); const char * storedText() const { return m_textBuffer; } void reset(); private: diff --git a/escher/include/escher/solid_text_area.h b/escher/include/escher/solid_text_area.h index e38ffe498..aa4b7c003 100644 --- a/escher/include/escher/solid_text_area.h +++ b/escher/include/escher/solid_text_area.h @@ -18,14 +18,16 @@ protected: ContentView(const KDFont * font, KDColor textColor, KDColor backgroundColor) : TextArea::ContentView(font), m_textColor(textColor), - m_backgroundColor(backgroundColor) + m_backgroundColor(backgroundColor), + m_backgroundHighlightColor(KDColorRed) //TODO LEA { } void clearRect(KDContext * ctx, KDRect rect) const override; - void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn) const override; + void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const override; private: KDColor m_textColor; KDColor m_backgroundColor; + KDColor m_backgroundHighlightColor; }; private: diff --git a/escher/include/escher/text_area.h b/escher/include/escher/text_area.h index f4bc36f48..237d603d9 100644 --- a/escher/include/escher/text_area.h +++ b/escher/include/escher/text_area.h @@ -105,8 +105,8 @@ protected: m_cursorLocation = m_text.text(); } void drawRect(KDContext * ctx, KDRect rect) const override; - void drawStringAt(KDContext * ctx, int line, int column, const char * text, size_t length, KDColor textColor, KDColor backgroundColor) const; - virtual void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn) const = 0; + void drawStringAt(KDContext * ctx, int line, int column, const char * text, size_t length, KDColor textColor, KDColor backgroundColor, const char * selectionStart, const char * selectionEnd, KDColor backgroundHighlightColor) const; + virtual void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const = 0; virtual void clearRect(KDContext * ctx, KDRect rect) const = 0; KDSize minimalSizeForOptimalDisplay() const override; void setText(char * textBuffer, size_t textBufferSize); diff --git a/escher/include/escher/text_input.h b/escher/include/escher/text_input.h index 90cd79a1e..32436b996 100644 --- a/escher/include/escher/text_input.h +++ b/escher/include/escher/text_input.h @@ -20,6 +20,8 @@ protected: public: ContentView(const KDFont * font) : View(), + m_selectionStart(nullptr), + m_selectionEnd(nullptr), m_cursorView(), m_font(font), m_cursorLocation(nullptr) @@ -33,14 +35,21 @@ protected: virtual bool removePreviousGlyph() = 0; virtual bool removeEndOfLine() = 0; KDRect cursorRect(); + const char * selectionStart() const { return m_selectionStart; } + const char * selectionEnd() const { return m_selectionEnd; } + void addSelection(const char * left, const char * right); + bool resetSelection(); // returns true if the selection was indeed reset + bool currentSelectionIsEmpty() const; + const char * m_selectionStart; + const char * m_selectionEnd; protected: virtual void layoutSubviews(bool force = false) override; void reloadRectFromPosition(const char * position, bool lineBreak = false); virtual KDRect glyphFrameAtPosition(const char * buffer, const char * position) const = 0; + virtual KDRect dirtyRectFromPosition(const char * position, bool lineBreak) const; TextCursorView m_cursorView; const KDFont * m_font; const char * m_cursorLocation; - virtual KDRect dirtyRectFromPosition(const char * position, bool lineBreak) const; private: int numberOfSubviews() const override { return 1; } View * subviewAtIndex(int index) override { @@ -62,6 +71,7 @@ protected: virtual const ContentView * nonEditableContentView() const = 0; bool moveCursorLeft(); bool moveCursorRight(); + bool selectLeftRight(bool left); private: virtual void willSetCursorLocation(const char * * location) {} virtual bool privateRemoveEndOfLine(); diff --git a/escher/src/clipboard.cpp b/escher/src/clipboard.cpp index 3996a9ffd..446211c4b 100644 --- a/escher/src/clipboard.cpp +++ b/escher/src/clipboard.cpp @@ -2,12 +2,14 @@ static Clipboard s_clipboard; +static inline int minInt(int x, int y) { return x < y ? x : y; } + Clipboard * Clipboard::sharedClipboard() { return &s_clipboard; } -void Clipboard::store(const char * storedText) { - strlcpy(m_textBuffer, storedText, TextField::maxBufferSize()); +void Clipboard::store(const char * storedText, int length) { + strlcpy(m_textBuffer, storedText, length == -1 ? TextField::maxBufferSize() : minInt(TextField::maxBufferSize(), length + 1)); } void Clipboard::reset() { diff --git a/escher/src/solid_text_area.cpp b/escher/src/solid_text_area.cpp index b115c9f99..c035618b1 100644 --- a/escher/src/solid_text_area.cpp +++ b/escher/src/solid_text_area.cpp @@ -6,7 +6,7 @@ void SolidTextArea::ContentView::clearRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, m_backgroundColor); } -void SolidTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn) const { +void SolidTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const { drawStringAt( ctx, line, @@ -14,6 +14,8 @@ void SolidTextArea::ContentView::drawLine(KDContext * ctx, int line, const char text + fromColumn, minInt(length - fromColumn, toColumn - fromColumn), m_textColor, - m_backgroundColor - ); + m_backgroundColor, + selectionStart, + selectionEnd, + m_backgroundHighlightColor); } diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index 0b61fa3cf..3baed8f3e 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -8,7 +8,9 @@ #include #include +static inline const char * maxPointer(const char * x, const char * y) { return x > y ? x : y; } static inline const char * minPointer(const char * x, const char * y) { return x < y ? x : y; } +static inline size_t minSizeT(size_t x, size_t y) { return x < y ? x : y; } /* TextArea */ @@ -96,32 +98,67 @@ bool TextArea::handleEventWithText(const char * text, bool indentation, bool for bool TextArea::handleEvent(Ion::Events::Event event) { if (m_delegate != nullptr && m_delegate->textAreaDidReceiveEvent(this, event)) { return true; - } else if (handleBoxEvent(event)) { + } + if (handleBoxEvent(event)) { return true; - } else if (event == Ion::Events::Left) { + } + if (event == Ion::Events::ShiftLeft || event == Ion::Events::ShiftRight) { + if (event == Ion::Events::ShiftLeft) { + selectLeftRight(true); + } else if (event == Ion::Events::ShiftRight) { + selectLeftRight(false); + } + scrollToCursor(); //TODO LEA remove? + return true; + } + + if (event == Ion::Events::Left) { + if (contentView()->resetSelection()) { + return true; + } return TextInput::moveCursorLeft(); - } else if (event == Ion::Events::Right) { + } + if (event == Ion::Events::Right) { + if (contentView()->resetSelection()) { + return true; + } return TextInput::moveCursorRight(); - } else if (event == Ion::Events::Up) { + } + if (event == Ion::Events::Backspace) { + contentView()->resetSelection(); + return removePreviousGlyph(); + } + if (event.hasText()) { + contentView()->resetSelection(); + return handleEventWithText(event.text()); + } + if (event == Ion::Events::EXE) { + contentView()->resetSelection(); + return handleEventWithText("\n"); + } + if (event == Ion::Events::Copy) { + if (contentView()->currentSelectionIsEmpty()) { + return false; + } + const char * start = contentView()->selectionStart(); + Clipboard::sharedClipboard()->store(start, contentView()->selectionEnd() - start); + return true; + } + if (event == Ion::Events::Paste) { + //TODO LEA + return handleEventWithText(Clipboard::sharedClipboard()->storedText()); + } + if (event == Ion::Events::Up) { + contentView()->resetSelection(); contentView()->moveCursorGeo(0, -1); } else if (event == Ion::Events::Down) { + contentView()->resetSelection(); 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 removePreviousGlyph(); - } else if (event.hasText()) { - return handleEventWithText(event.text()); - } else if (event == Ion::Events::EXE) { - return handleEventWithText("\n"); } else if (event == Ion::Events::Clear) { + //TODO LEA if (!contentView()->removeEndOfLine()) { contentView()->removeStartOfLine(); } - } else if (event == Ion::Events::Paste) { - return handleEventWithText(Clipboard::sharedClipboard()->storedText()); } else { return false; } @@ -141,11 +178,11 @@ int TextArea::indentationBeforeCursor() const { * another code point, until reaching the beginning of the line. */ UTF8Helper::PerformAtCodePoints(const_cast