diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index 70e041af5..b208a510a 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -313,7 +313,7 @@ bool ConsoleController::textFieldDidAbortEditing(TextField * textField, const ch return true; } -Toolbox * ConsoleController::toolboxForTextField(TextField * textField) { +Toolbox * ConsoleController::toolboxForTextInput(TextInput * textInput) { Code::App * codeApp = static_cast(app()); codeApp->pythonToolbox()->setAction(codeApp->toolboxActionForTextField()); return codeApp->pythonToolbox(); diff --git a/apps/code/console_controller.h b/apps/code/console_controller.h index 2db1b2333..43cbd364a 100644 --- a/apps/code/console_controller.h +++ b/apps/code/console_controller.h @@ -61,7 +61,7 @@ public: bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; bool textFieldDidAbortEditing(TextField * textField, const char * text) override; - Toolbox * toolboxForTextField(TextField * textField) override; + Toolbox * toolboxForTextInput(TextInput * textInput) override; // MicroPython::ExecutionEnvironment void displaySandbox() override; diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index 7169558ec..e7f0e3251 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -129,7 +129,7 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events: return false; } -Toolbox * EditorController::toolboxForTextArea(TextArea * textArea) { +Toolbox * EditorController::toolboxForTextInput(TextInput * textInput) { Code::App * codeApp = static_cast(app()); codeApp->pythonToolbox()->setAction(codeApp->toolboxActionForTextArea()); return codeApp->pythonToolbox(); diff --git a/apps/code/editor_controller.h b/apps/code/editor_controller.h index 043a6097b..13bb5de75 100644 --- a/apps/code/editor_controller.h +++ b/apps/code/editor_controller.h @@ -25,7 +25,7 @@ public: /* TextAreaDelegate */ bool textAreaShouldFinishEditing(TextArea * textArea, Ion::Events::Event event) override; bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) override; - Toolbox * toolboxForTextArea(TextArea * textArea) override; + Toolbox * toolboxForTextInput(TextInput * textInput) override; private: static constexpr int k_indentationSpacesNumber = 2; diff --git a/apps/code/menu_controller.h b/apps/code/menu_controller.h index 0feff6c98..b10a3d153 100644 --- a/apps/code/menu_controller.h +++ b/apps/code/menu_controller.h @@ -58,7 +58,7 @@ public: bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; bool textFieldDidAbortEditing(TextField * textField, const char * text) override; bool textFieldDidHandleEvent(TextField * textField, bool returnValue, bool textHasChanged) override; - Toolbox * toolboxForTextField(TextField * textField) override { return nullptr; } + Toolbox * toolboxForTextInput(TextInput * textInput) override { return nullptr; } /* ButtonRowDelegate */ int numberOfButtons(ButtonRowController::Position position) const override { return 1; } diff --git a/apps/sequence/list/list_controller.cpp b/apps/sequence/list/list_controller.cpp index 29eefaf32..072fb1688 100644 --- a/apps/sequence/list/list_controller.cpp +++ b/apps/sequence/list/list_controller.cpp @@ -23,7 +23,7 @@ const char * ListController::title() { return I18n::translate(I18n::Message::SequenceTab); } -Toolbox * ListController::toolboxForTextField(TextField * textField) { +Toolbox * ListController::toolboxForTextInput(TextInput * textInput) { int recurrenceDepth = -1; int sequenceDefinition = sequenceDefinitionForRow(selectedRow()); Sequence * sequence = m_sequenceStore->functionAtIndex(functionIndexForRow(selectedRow())); diff --git a/apps/sequence/list/list_controller.h b/apps/sequence/list/list_controller.h index 2fd3f0f35..3e8e9075d 100644 --- a/apps/sequence/list/list_controller.h +++ b/apps/sequence/list/list_controller.h @@ -21,7 +21,7 @@ public: int numberOfRows() override; virtual KDCoordinate rowHeight(int j) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; - Toolbox * toolboxForTextField(TextField * textField) override; + Toolbox * toolboxForTextInput(TextInput * textInput) override; void selectPreviousNewSequenceCell(); private: Shared::TextFieldDelegateApp * textFieldDelegateApp() override; diff --git a/apps/shared/text_field_delegate.cpp b/apps/shared/text_field_delegate.cpp index 907ef9ca6..2f1cb3828 100644 --- a/apps/shared/text_field_delegate.cpp +++ b/apps/shared/text_field_delegate.cpp @@ -12,8 +12,8 @@ bool TextFieldDelegate::textFieldDidReceiveEvent(::TextField * textField, Ion::E return textFieldDelegateApp()->textFieldDidReceiveEvent(textField, event); } -Toolbox * TextFieldDelegate::toolboxForTextField(::TextField * textField) { - return textFieldDelegateApp()->toolboxForTextField(textField); +Toolbox * TextFieldDelegate::toolboxForTextInput(TextInput * textInput) { + return textFieldDelegateApp()->toolboxForTextInput(textInput); } } diff --git a/apps/shared/text_field_delegate.h b/apps/shared/text_field_delegate.h index aeab2045d..d0167c253 100644 --- a/apps/shared/text_field_delegate.h +++ b/apps/shared/text_field_delegate.h @@ -11,7 +11,7 @@ class TextFieldDelegate : public ::TextFieldDelegate { public: bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; - Toolbox * toolboxForTextField(TextField * textField) override; + Toolbox * toolboxForTextInput(TextInput * textInput) override; private: virtual TextFieldDelegateApp * textFieldDelegateApp() = 0; }; diff --git a/apps/shared/text_field_delegate_app.cpp b/apps/shared/text_field_delegate_app.cpp index 6a7072fe1..0d49ddd99 100644 --- a/apps/shared/text_field_delegate_app.cpp +++ b/apps/shared/text_field_delegate_app.cpp @@ -107,7 +107,7 @@ bool TextFieldDelegateApp::textFieldDidReceiveEvent(TextField * textField, Ion:: return false; } -Toolbox * TextFieldDelegateApp::toolboxForTextField(TextField * textField) { +Toolbox * TextFieldDelegateApp::toolboxForTextInput(TextInput * textInput) { return container()->mathToolbox(); } diff --git a/apps/shared/text_field_delegate_app.h b/apps/shared/text_field_delegate_app.h index c5039a1ef..6012ac586 100644 --- a/apps/shared/text_field_delegate_app.h +++ b/apps/shared/text_field_delegate_app.h @@ -17,7 +17,7 @@ public: virtual const char * XNT(); bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; virtual bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; - Toolbox * toolboxForTextField(TextField * textField) override; + Toolbox * toolboxForTextInput(TextInput * textInput) override; protected: TextFieldDelegateApp(Container * container, Snapshot * snapshot, ViewController * rootViewController); private: diff --git a/escher/Makefile b/escher/Makefile index 3d80210a1..6723110b5 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -67,6 +67,7 @@ objs += $(addprefix escher/src/,\ text_cursor_view.o\ text_area.o\ text_field.o\ + text_input.o\ text_input_helpers.o\ text_view.o\ tiled_view.o\ diff --git a/escher/include/escher.h b/escher/include/escher.h index 2658224fb..9a31e8d8c 100644 --- a/escher/include/escher.h +++ b/escher/include/escher.h @@ -63,6 +63,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/escher/include/escher/input_view_controller.h b/escher/include/escher/input_view_controller.h index 52169e9b6..c4f2a2aa9 100644 --- a/escher/include/escher/input_view_controller.h +++ b/escher/include/escher/input_view_controller.h @@ -16,7 +16,7 @@ public: 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, const char * text) override; - Toolbox * toolboxForTextField(TextField * textFied) override; + Toolbox * toolboxForTextInput(TextInput * textInput) override; private: class TextFieldController : public ViewController { public: diff --git a/escher/include/escher/text_area.h b/escher/include/escher/text_area.h index 0ef6aa924..c885b63b8 100644 --- a/escher/include/escher/text_area.h +++ b/escher/include/escher/text_area.h @@ -1,36 +1,30 @@ #ifndef ESCHER_TEXT_AREA_H #define ESCHER_TEXT_AREA_H +#include +#include #include #include -#include -#include -#include -class TextArea : public ScrollableView, public ScrollViewDataSource { +class TextArea : public TextInput { public: - TextArea(Responder * parentResponder, char * textBuffer = nullptr, size_t textBufferSize = 0, - TextAreaDelegate * delegate = nullptr, KDText::FontSize fontSize = KDText::FontSize::Large, + 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) { m_delegate = delegate; } - Toolbox * toolbox() override; bool handleEvent(Ion::Events::Event event) override; - bool handleEventWithText(const char * text, bool indentation = false); + bool handleEventWithText(const char * text, bool indentation = false) override; void setText(char * textBuffer, size_t textBufferSize); - bool insertTextAtLocation(const char * textBuffer, int location) { return m_contentView.insertTextAtLocation(textBuffer, location); } - bool insertTextWithIndentation(const char * textBuffer, int location); - int indentationBeforeCursor() const; - bool removeChar(); - const char * text() const { return m_contentView.text(); } - int cursorLocation() const { return m_contentView.cursorLocation(); } - bool setCursorLocation(int location); private: + int indentationBeforeCursor() const; + bool insertTextWithIndentation(const char * textBuffer, int location); + TextInputDelegate * delegate() override { + return m_delegate; + } class Text { public: Text(char * buffer, size_t bufferSize); void setText(char * buffer, size_t bufferSize); const char * text() const { return const_cast(m_buffer); } - class Line { public: Line(const char * text); @@ -67,7 +61,7 @@ private: Position span() const; - Position positionAtIndex(size_t index); + Position positionAtIndex(size_t index) const; size_t indexAtPosition(Position p); void insertChar(char c, size_t index); @@ -88,37 +82,28 @@ private: size_t m_bufferSize; }; - class ContentView : public View { + class ContentView : public TextInput::ContentView { public: ContentView(char * textBuffer, size_t textBufferSize, KDText::FontSize size, KDColor textColor, KDColor backgroundColor); void drawRect(KDContext * ctx, KDRect rect) const override; KDSize minimalSizeForOptimalDisplay() const override; void setText(char * textBuffer, size_t textBufferSize); - const char * text() const; + const char * text() const override; const Text * getText() const { return &m_text; } - int cursorLocation() const { return m_cursorIndex; } - bool insertTextAtLocation(const char * text, int location); - void setCursorLocation(int cursorLocation); + size_t editedTextLength() const override { + return m_text.textLength(); + } + bool insertTextAtLocation(const char * text, int location) override; void moveCursorGeo(int deltaX, int deltaY); - void removeChar(); - bool removeEndOfLine(); - void removeStartOfLine(); - KDRect cursorRect(); + bool removeChar() override; + bool removeEndOfLine() override; + bool removeStartOfLine(); private: - int numberOfSubviews() const override; - View * subviewAtIndex(int index) override; - void layoutSubviews() override; - KDRect characterFrameAtIndex(size_t index); - KDRect dirtyRectFromCursorPosition(size_t index, bool lineBreak); - TextCursorView m_cursorView; - size_t m_cursorIndex; + KDRect characterFrameAtIndex(size_t index) const override; Text m_text; - KDText::FontSize m_fontSize; - KDColor m_textColor; - KDColor m_backgroundColor; }; - + const ContentView * nonEditableContentView() const override { return &m_contentView; } ContentView m_contentView; TextAreaDelegate * m_delegate; }; diff --git a/escher/include/escher/text_area_delegate.h b/escher/include/escher/text_area_delegate.h index 86cc16bf5..ada8ee7e1 100644 --- a/escher/include/escher/text_area_delegate.h +++ b/escher/include/escher/text_area_delegate.h @@ -1,13 +1,14 @@ #ifndef ESCHER_TEXT_AREA_DELEGATE_H #define ESCHER_TEXT_AREA_DELEGATE_H +#include + class TextArea; -class TextAreaDelegate { +class TextAreaDelegate : public TextInputDelegate { public: virtual bool textAreaShouldFinishEditing(TextArea * textArea, Ion::Events::Event event) = 0; virtual bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) = 0; - virtual Toolbox * toolboxForTextArea(TextArea * textArea) = 0; }; #endif diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index 75dcf0ac2..99c0b6178 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -1,74 +1,53 @@ #ifndef ESCHER_TEXT_FIELD_H #define ESCHER_TEXT_FIELD_H -#include +#include #include -#include #include -class TextField : public ScrollableView, public ScrollViewDataSource { +class TextField : public TextInput { public: TextField(Responder * parentResponder, char * textBuffer, char * draftTextBuffer, size_t textBufferSize, TextFieldDelegate * delegate = nullptr, bool hasTwoBuffers = true, KDText::FontSize size = KDText::FontSize::Large, float horizontalAlignment = 0.0f, float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite); - void setDelegate(TextFieldDelegate * delegate); + void setDelegate(TextFieldDelegate * delegate) { m_delegate = delegate; } void setDraftTextBuffer(char * draftTextBuffer); - Toolbox * toolbox() override; bool isEditing() const; - const char * text() const; - int draftTextLength() const; - int cursorLocation() const; - void setCursorLocation(int location); + size_t draftTextLength() const; void setText(const char * text); - void setBackgroundColor(KDColor backgroundColor); - KDColor backgroundColor() const; - void setTextColor(KDColor textColor); void setAlignment(float horizontalAlignment, float verticalAlignment); virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true); - /* 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, int location); KDSize minimalSizeForOptimalDisplay() const override; - bool handleEventWithText(const char * text); + bool handleEventWithText(const char * text, bool indenting = false) override; bool handleEvent(Ion::Events::Event event) override; - bool textFieldShouldFinishEditing(Ion::Events::Event event); constexpr static int maxBufferSize() { return ContentView::k_maxBufferSize; } - void scrollToCursor(); + void scrollToCursor() override; + bool textFieldShouldFinishEditing(Ion::Events::Event event) { return m_delegate->textFieldShouldFinishEditing(this, event); } protected: - class ContentView : public View { + class ContentView : public TextInput::ContentView { public: - ContentView(char * textBuffer, char * draftTextBuffer, size_t textBufferSize, KDText::FontSize size, float horizontalAlignment = 0.0f, - float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite); + ContentView(char * textBuffer, char * draftTextBuffer, size_t textBufferSize, KDText::FontSize size, float horizontalAlignment = 0.0f, float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite); void setDraftTextBuffer(char * draftTextBuffer); void drawRect(KDContext * ctx, KDRect rect) const override; - void reload(); bool isEditing() const { return m_isEditing; } - const char * text() const; - int draftTextLength() const; - int cursorLocation() const { return m_currentCursorLocation; } + const char * text() const override; + size_t editedTextLength() const override; char * textBuffer() { return m_textBuffer; } char * draftTextBuffer() { return m_draftTextBuffer; } int bufferSize() { return k_maxBufferSize; } void setText(const char * text); - void setBackgroundColor(KDColor backgroundColor); - KDColor backgroundColor() const { return m_backgroundColor; } - void setTextColor(KDColor textColor); void setAlignment(float horizontalAlignment, float verticalAlignment); void setEditing(bool isEditing, bool reinitDraftBuffer); void reinitDraftTextBuffer(); - void setCursorLocation(int location); - bool insertTextAtLocation(const char * text, int location); + /* 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, int location) override; KDSize minimalSizeForOptimalDisplay() const override; - KDCoordinate textHeight() const; - KDCoordinate textWidth() const; - KDCoordinate charWidth() const; - void deleteCharPrecedingCursor(); - bool deleteEndOfLine(); - KDRect cursorRect(); - View * subviewAtIndex(int index) override { return &m_cursorView; } + bool removeChar() override; + bool removeEndOfLine() override; /* In some app (ie Calculation), text fields record expression results whose * lengths can reach 70 (ie * [[1.234567e-123*e^(1.234567e-123*i), 1.234567e-123*e^(1.234567e-123*i)]]). @@ -76,27 +55,23 @@ protected: * over 70. */ constexpr static int k_maxBufferSize = 152; private: - int numberOfSubviews() const override { return 1; } void layoutSubviews() override; - KDPoint textOrigin() const; - TextCursorView m_cursorView; + KDRect characterFrameAtIndex(size_t index) const override; bool m_isEditing; char * m_textBuffer; char * m_draftTextBuffer; size_t m_currentDraftTextLength; - size_t m_currentCursorLocation; size_t m_textBufferSize; float m_horizontalAlignment; float m_verticalAlignment; - KDColor m_textColor; - KDColor m_backgroundColor; - KDText::FontSize m_fontSize; }; + const ContentView * nonEditableContentView() const override { return &m_contentView; } ContentView m_contentView; private: bool privateHandleEvent(Ion::Events::Event event); - void deleteCharPrecedingCursor(); - bool deleteEndOfLine(); + TextInputDelegate * delegate() override { + return m_delegate; + } bool m_hasTwoBuffers; TextFieldDelegate * m_delegate; }; diff --git a/escher/include/escher/text_field_delegate.h b/escher/include/escher/text_field_delegate.h index 900450fc9..e7ea98b1f 100644 --- a/escher/include/escher/text_field_delegate.h +++ b/escher/include/escher/text_field_delegate.h @@ -1,16 +1,17 @@ #ifndef ESCHER_TEXT_FIELD_DELEGATE_H #define ESCHER_TEXT_FIELD_DELEGATE_H +#include + class TextField; -class TextFieldDelegate { +class TextFieldDelegate : public TextInputDelegate { public: 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; }; virtual bool textFieldDidAbortEditing(TextField * textField, const char * text) {return false;}; virtual bool textFieldDidHandleEvent(TextField * textField, bool returnValue, bool textHasChanged) { return returnValue; }; - virtual Toolbox * toolboxForTextField(TextField * textField) = 0; }; #endif diff --git a/escher/include/escher/text_input.h b/escher/include/escher/text_input.h new file mode 100644 index 000000000..441bc33d0 --- /dev/null +++ b/escher/include/escher/text_input.h @@ -0,0 +1,65 @@ +#ifndef ESCHER_TEXT_INPUT_H +#define ESCHER_TEXT_INPUT_H + +#include +#include +#include +#include +#include + +class TextInput : public ScrollableView, public ScrollViewDataSource { +public: + TextInput(Responder * parentResponder, View * contentView); + Toolbox * toolbox() override; + const char * text() const { return nonEditableContentView()->text(); } + void setBackgroundColor(KDColor backgroundColor); + KDColor backgroundColor() const { return nonEditableContentView()->backgroundColor(); } + void setTextColor(KDColor textColor); + bool removeChar(); + size_t cursorLocation() const { return nonEditableContentView()->cursorLocation(); } + bool setCursorLocation(int location); + virtual void scrollToCursor(); + virtual bool handleEventWithText(const char * text, bool indenting = false) = 0; +protected: + class ContentView : public View { + public: + ContentView(KDText::FontSize size, KDColor textColor, KDColor backgroundColor); + void setBackgroundColor(KDColor backgroundColor); + KDColor backgroundColor() const { return m_backgroundColor; } + void setTextColor(KDColor textColor); + size_t cursorLocation() const { return m_cursorIndex; } + void setCursorLocation(int cursorLocation); + virtual const char * text() const = 0; + virtual bool insertTextAtLocation(const char * text, int location) = 0; + virtual bool removeChar() = 0; + virtual bool removeEndOfLine() = 0; + KDRect cursorRect(); + protected: + virtual void layoutSubviews() override; + void reloadRectFromCursorPosition(size_t index, bool lineBreak = false); + virtual KDRect characterFrameAtIndex(size_t index) const = 0; + TextCursorView m_cursorView; + KDText::FontSize m_fontSize; + KDColor m_textColor; + KDColor m_backgroundColor; + size_t m_cursorIndex; + private: + int numberOfSubviews() const override; + View * subviewAtIndex(int index) override; + virtual size_t editedTextLength() const = 0; + }; +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, int location); + virtual bool removeEndOfLine(); + ContentView * contentView() { + return const_cast(nonEditableContentView()); + } + virtual const ContentView * nonEditableContentView() const = 0; +private: + virtual TextInputDelegate * delegate() = 0; +}; + +#endif diff --git a/escher/include/escher/text_input_delegate.h b/escher/include/escher/text_input_delegate.h new file mode 100644 index 000000000..2e2b84605 --- /dev/null +++ b/escher/include/escher/text_input_delegate.h @@ -0,0 +1,11 @@ +#ifndef ESCHER_TEXT_INPUT_DELEGATE_H +#define ESCHER_TEXT_INPUT_DELEGATE_H + +class TextInput; + +class TextInputDelegate { +public: + virtual Toolbox * toolboxForTextInput(TextInput * textInput) = 0; +}; + +#endif diff --git a/escher/src/input_view_controller.cpp b/escher/src/input_view_controller.cpp index 22fd62fa1..df68abf35 100644 --- a/escher/src/input_view_controller.cpp +++ b/escher/src/input_view_controller.cpp @@ -77,8 +77,7 @@ void InputViewController::edit(Responder * caller, Ion::Events::Event event, voi displayModalViewController(&m_textFieldController, 1.0f, 1.0f); m_textFieldController.textField()->handleEvent(event); if (initialText != nullptr) { - m_textFieldController.textField()->insertTextAtLocation(initialText, 0); - m_textFieldController.textField()->setCursorLocation(strlen(initialText)); + m_textFieldController.textField()->handleEventWithText(initialText); } } @@ -107,6 +106,6 @@ bool InputViewController::textFieldDidReceiveEvent(TextField * textField, Ion::E return m_textFieldDelegate->textFieldDidReceiveEvent(textField, event); } -Toolbox * InputViewController::toolboxForTextField(TextField * textField) { - return m_textFieldDelegate->toolboxForTextField(textField); +Toolbox * InputViewController::toolboxForTextInput(TextInput * input) { + return m_textFieldDelegate->toolboxForTextInput(input); } diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index d8f63beaf..61892a169 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -64,7 +64,7 @@ size_t TextArea::Text::indexAtPosition(Position p) { return endOfLastLine - m_buffer; } -TextArea::Text::Position TextArea::Text::positionAtIndex(size_t index) { +TextArea::Text::Position TextArea::Text::positionAtIndex(size_t index) const { assert(m_buffer != nullptr); assert(index < m_bufferSize); const char * target = m_buffer + index; @@ -151,12 +151,8 @@ TextArea::Text::Position TextArea::Text::span() const { /* TextArea::ContentView */ TextArea::ContentView::ContentView(char * textBuffer, size_t textBufferSize, KDText::FontSize fontSize, KDColor textColor, KDColor backgroundColor) : - View(), - m_cursorIndex(0), - m_text(textBuffer, textBufferSize), - m_fontSize(fontSize), - m_textColor(textColor), - m_backgroundColor(backgroundColor) + TextInput::ContentView(fontSize, textColor, backgroundColor), + m_text(textBuffer, textBufferSize) { } @@ -207,18 +203,6 @@ void TextArea::ContentView::drawRect(KDContext * ctx, KDRect rect) const { } } -int TextArea::ContentView::numberOfSubviews() const { - return 1; -} - -View * TextArea::ContentView::subviewAtIndex(int index) { - return &m_cursorView; -} - -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; @@ -239,47 +223,47 @@ bool TextArea::TextArea::ContentView::insertTextAtLocation(const char * text, in lineBreak |= *text == '\n'; m_text.insertChar(*text++, currentLocation++); } - markRectAsDirty(dirtyRectFromCursorPosition(currentLocation-1, lineBreak)); + reloadRectFromCursorPosition(currentLocation-1, lineBreak); return true; } -void TextArea::TextArea::ContentView::removeChar() { - bool lineBreak = false; - if (m_cursorIndex > 0) { - lineBreak = m_text.removeChar(--m_cursorIndex) == '\n'; +bool TextArea::TextArea::ContentView::removeChar() { + if (cursorLocation() <= 0) { + return false; } + bool lineBreak = false; + assert(m_cursorIndex > 0); + lineBreak = m_text.removeChar(--m_cursorIndex) == '\n'; layoutSubviews(); // Reposition the cursor - markRectAsDirty(dirtyRectFromCursorPosition(m_cursorIndex, lineBreak)); + reloadRectFromCursorPosition(cursorLocation(), lineBreak); + return true; } bool TextArea::ContentView::removeEndOfLine() { - int removedLine = m_text.removeRemainingLine(m_cursorIndex, 1); + int removedLine = m_text.removeRemainingLine(cursorLocation(), 1); if (removedLine > 0) { layoutSubviews(); - markRectAsDirty(dirtyRectFromCursorPosition(m_cursorIndex, false)); + reloadRectFromCursorPosition(cursorLocation(), false); return true; } return false; } -void TextArea::ContentView::removeStartOfLine() { - if (m_cursorIndex <= 0) { - return; +bool TextArea::ContentView::removeStartOfLine() { + if (cursorLocation() <= 0) { + return false; } - int removedLine = m_text.removeRemainingLine(m_cursorIndex-1, -1); + int removedLine = m_text.removeRemainingLine(cursorLocation()-1, -1); if (removedLine > 0) { assert(m_cursorIndex >= removedLine); - m_cursorIndex -= removedLine; - layoutSubviews(); - markRectAsDirty(dirtyRectFromCursorPosition(m_cursorIndex, false)); + setCursorLocation(cursorLocation()-removedLine); + reloadRectFromCursorPosition(cursorLocation(), false); + return true; } + return false; } -KDRect TextArea::TextArea::ContentView::cursorRect() { - return characterFrameAtIndex(m_cursorIndex); -} - -KDRect TextArea::TextArea::ContentView::characterFrameAtIndex(size_t index) { +KDRect TextArea::TextArea::ContentView::characterFrameAtIndex(size_t index) const { KDSize charSize = KDText::charSize(m_fontSize); Text::Position p = m_text.positionAtIndex(index); return KDRect( @@ -292,24 +276,7 @@ KDRect TextArea::TextArea::ContentView::characterFrameAtIndex(size_t index) { void TextArea::TextArea::ContentView::moveCursorGeo(int deltaX, int deltaY) { Text::Position p = m_text.positionAtIndex(m_cursorIndex); - m_cursorIndex = m_text.indexAtPosition(Text::Position(p.column() + deltaX, p.line() + deltaY)); - layoutSubviews(); -} - -void TextArea::TextArea::ContentView::setCursorLocation(int location) { - int adjustedLocation = location < 0 ? 0 : location; - adjustedLocation = adjustedLocation > (signed int)m_text.textLength() ? (signed int)m_text.textLength() : adjustedLocation; - m_cursorIndex = adjustedLocation; - layoutSubviews(); -} - -KDRect TextArea::TextArea::ContentView::dirtyRectFromCursorPosition(size_t index, bool lineBreak) { - KDRect charRect = characterFrameAtIndex(index); - KDRect dirtyRect = KDRect(charRect.x(), charRect.y(), bounds().width() - charRect.x(), charRect.height()); - if (lineBreak) { - dirtyRect = dirtyRect.unionedWith(KDRect(0, charRect.bottom()+1, bounds().width(), bounds().height()-charRect.bottom()-1)); - } - return dirtyRect; + setCursorLocation(m_text.indexAtPosition(Text::Position(p.column() + deltaX, p.line() + deltaY))); } /* TextArea */ @@ -317,20 +284,13 @@ KDRect TextArea::TextArea::ContentView::dirtyRectFromCursorPosition(size_t index TextArea::TextArea(Responder * parentResponder, char * textBuffer, size_t textBufferSize, TextAreaDelegate * delegate, KDText::FontSize fontSize, KDColor textColor, KDColor backgroundColor) : - ScrollableView(parentResponder, &m_contentView, this), + TextInput(parentResponder, &m_contentView), m_contentView(textBuffer, textBufferSize, fontSize, textColor, backgroundColor), m_delegate(delegate) { assert(textBufferSize < INT_MAX/2); } -Toolbox * TextArea::toolbox() { - if (m_delegate != nullptr) { - return m_delegate->toolboxForTextArea(this); - } - return nullptr; -} - bool TextArea::handleEventWithText(const char * text, bool indentation) { int nextCursorLocation = cursorLocation(); if ((indentation && insertTextWithIndentation(text, cursorLocation())) || insertTextAtLocation(text, cursorLocation())) { @@ -342,6 +302,7 @@ bool TextArea::handleEventWithText(const char * text, bool indentation) { bool TextArea::handleEvent(Ion::Events::Event event) { if (m_delegate != nullptr && m_delegate->textAreaDidReceiveEvent(this, event)) { + return true; } else if (Responder::handleEvent(event)) { // The only event Responder handles is 'Toolbox' displaying. return true; @@ -372,14 +333,7 @@ bool TextArea::handleEvent(Ion::Events::Event event) { } else { return false; } - /* Technically, we do not need to overscroll in text area. However, - * logically, we should layout the scroll view before calling - * scrollToContentRect in case the size of the scroll view has changed and - * then call scrollToContentRect which call another layout of the scroll view - * if the offset has evolved. In order to avoid requiring two layouts, we - * allow overscrolling in scrollToContentRect and the last layout of the - * scroll view corrects the size of the scroll view only once. */ - scrollToContentRect(m_contentView.cursorRect(), true); + scrollToCursor(); return true; } @@ -431,15 +385,3 @@ int TextArea::indentationBeforeCursor() const { } return indentationSize; } - -bool TextArea::setCursorLocation(int location) { - m_contentView.setCursorLocation(location); - scrollToContentRect(m_contentView.cursorRect(), true); - return true; -} - -bool TextArea::removeChar() { - m_contentView.removeChar(); - scrollToContentRect(m_contentView.cursorRect(), true); - return true; -} diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index 1c0c3d3f9..3b4b2df8e 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -6,18 +6,14 @@ /* TextField::ContentView */ TextField::ContentView::ContentView(char * textBuffer, char * draftTextBuffer, size_t textBufferSize, KDText::FontSize size, float horizontalAlignment, float verticalAlignment, KDColor textColor, KDColor backgroundColor) : - View(), + TextInput::ContentView(size, textColor, backgroundColor), m_isEditing(false), m_textBuffer(textBuffer), m_draftTextBuffer(draftTextBuffer), m_currentDraftTextLength(0), - m_currentCursorLocation(0), m_textBufferSize(textBufferSize), m_horizontalAlignment(horizontalAlignment), - m_verticalAlignment(verticalAlignment), - m_textColor(textColor), - m_backgroundColor(backgroundColor), - m_fontSize(size) + m_verticalAlignment(verticalAlignment) { assert(m_textBufferSize <= k_maxBufferSize); } @@ -32,14 +28,7 @@ void TextField::ContentView::drawRect(KDContext * ctx, KDRect rect) const { bckCol = KDColorWhite; } ctx->fillRect(rect, bckCol); - ctx->drawString(text(), textOrigin(), m_fontSize, m_textColor, bckCol); -} - -void TextField::ContentView::reload() { - KDSize textSize = KDText::stringSize(text(), m_fontSize); - KDSize textAndCursorSize = KDSize(textSize.width()+ m_cursorView.minimalSizeForOptimalDisplay().width(), textSize.height()); - KDRect dirtyZone(textOrigin(), textAndCursorSize); - markRectAsDirty(dirtyZone); + ctx->drawString(text(), characterFrameAtIndex(0).origin(), m_fontSize, m_textColor, bckCol); } const char * TextField::ContentView::text() const { @@ -49,14 +38,12 @@ const char * TextField::ContentView::text() const { return const_cast(m_textBuffer); } -int TextField::ContentView::draftTextLength() const { - assert(isEditing()); - assert(strlen(text()) == m_currentDraftTextLength); +size_t TextField::ContentView::editedTextLength() const { return m_currentDraftTextLength; } void TextField::ContentView::setText(const char * text) { - reload(); + reloadRectFromCursorPosition(0); if (m_isEditing) { strlcpy(m_draftTextBuffer, text, m_textBufferSize); int textLength = strlen(text) >= m_textBufferSize ? m_textBufferSize-1 : strlen(text); @@ -64,17 +51,7 @@ void TextField::ContentView::setText(const char * text) { } else { strlcpy(m_textBuffer, text, m_textBufferSize); } - reload(); -} - -void TextField::ContentView::setBackgroundColor(KDColor backgroundColor) { - m_backgroundColor = backgroundColor; - markRectAsDirty(bounds()); -} - -void TextField::ContentView::setTextColor(KDColor textColor) { - m_textColor = textColor; - markRectAsDirty(bounds()); + reloadRectFromCursorPosition(0); } void TextField::ContentView::setAlignment(float horizontalAlignment, float verticalAlignment) { @@ -101,13 +78,6 @@ void TextField::ContentView::reinitDraftTextBuffer() { m_currentDraftTextLength = 0; } -void TextField::ContentView::setCursorLocation(int location) { - int adjustedLocation = location < 0 ? 0 : location; - adjustedLocation = adjustedLocation > (signed int)m_currentDraftTextLength ? (signed int)m_currentDraftTextLength : adjustedLocation; - m_currentCursorLocation = adjustedLocation; - layoutSubviews(); -} - bool TextField::ContentView::insertTextAtLocation(const char * text, int location) { int textSize = strlen(text); if (m_currentDraftTextLength + textSize >= m_textBufferSize || textSize == 0) { @@ -121,58 +91,47 @@ bool TextField::ContentView::insertTextAtLocation(const char * text, int locatio m_draftTextBuffer[location+textSize-1] = text[textSize-1]; } m_currentDraftTextLength += textSize; - reload(); + reloadRectFromCursorPosition((m_horizontalAlignment == 0.0f ? location : 0)); return true; } KDSize TextField::ContentView::minimalSizeForOptimalDisplay() const { + KDSize charSize = KDText::charSize(m_fontSize); if (m_isEditing) { - return KDSize(textWidth()+m_cursorView.minimalSizeForOptimalDisplay().width(), textHeight()); + return KDSize(charSize.width()*strlen(text())+m_cursorView.minimalSizeForOptimalDisplay().width(), charSize.height()); } - return KDSize(textWidth(), textHeight()); + return KDSize(charSize.width()*strlen(text()), charSize.height()); } -KDCoordinate TextField::ContentView::textHeight() const { - KDSize textSize = KDText::charSize(m_fontSize); - return textSize.height(); -} - -KDCoordinate TextField::ContentView::charWidth() const { - KDSize textSize = KDText::charSize(m_fontSize); - return textSize.width(); -} - -KDCoordinate TextField::ContentView::textWidth() const { - return strlen(text())*charWidth(); -} - -void TextField::ContentView::deleteCharPrecedingCursor() { +bool TextField::ContentView::removeChar() { if (cursorLocation() <= 0) { - return; + return false; } - reload(); m_currentDraftTextLength--; - setCursorLocation(m_currentCursorLocation-1); - for (int k = m_currentCursorLocation; k < (signed char)m_currentDraftTextLength; k ++) { + if (m_horizontalAlignment > 0.0f) { + reloadRectFromCursorPosition(0); + } + setCursorLocation(cursorLocation()-1); + if( m_horizontalAlignment == 0.0f) { + reloadRectFromCursorPosition(cursorLocation()); + } + for (int k = cursorLocation(); k < (signed char)m_currentDraftTextLength; k ++) { m_draftTextBuffer[k] = m_draftTextBuffer[k+1]; } m_draftTextBuffer[m_currentDraftTextLength] = 0; layoutSubviews(); -} - -bool TextField::ContentView::deleteEndOfLine() { - if (m_currentDraftTextLength == m_currentCursorLocation) { - return false; - } - reload(); - m_currentDraftTextLength = m_currentCursorLocation; - m_draftTextBuffer[m_currentCursorLocation] = 0; - layoutSubviews(); return true; } -KDRect TextField::ContentView::cursorRect() { - return KDRect(m_currentCursorLocation * charWidth(), 0, m_cursorView.minimalSizeForOptimalDisplay()); +bool TextField::ContentView::removeEndOfLine() { + if (m_currentDraftTextLength == cursorLocation()) { + return false; + } + reloadRectFromCursorPosition((m_horizontalAlignment == 0.0f ? cursorLocation() : 0)); + m_currentDraftTextLength = cursorLocation(); + m_draftTextBuffer[cursorLocation()] = 0; + layoutSubviews(); + return true; } void TextField::ContentView::layoutSubviews() { @@ -180,62 +139,39 @@ void TextField::ContentView::layoutSubviews() { m_cursorView.setFrame(KDRectZero); return; } + TextInput::ContentView::layoutSubviews(); +} + +KDRect TextField::ContentView::characterFrameAtIndex(size_t index) const { + KDSize charSize = KDText::charSize(m_fontSize); KDSize textSize = KDText::stringSize(text(), m_fontSize); KDCoordinate cursorWidth = m_cursorView.minimalSizeForOptimalDisplay().width(); - KDRect frame(m_horizontalAlignment*(m_frame.width() - textSize.width()-cursorWidth)+ m_currentCursorLocation * charWidth(), m_verticalAlignment*(m_frame.height() - textSize.height()), cursorWidth, textSize.height()); - m_cursorView.setFrame(frame); + return KDRect(m_horizontalAlignment*(m_frame.width() - textSize.width()-cursorWidth)+ index * charSize.width(), m_verticalAlignment*(m_frame.height() - charSize.height()), charSize); } +/* TextField */ + TextField::TextField(Responder * parentResponder, char * textBuffer, char * draftTextBuffer, size_t textBufferSize, TextFieldDelegate * delegate, bool hasTwoBuffers, KDText::FontSize size, float horizontalAlignment, float verticalAlignment, KDColor textColor, KDColor backgroundColor) : - ScrollableView(parentResponder, &m_contentView, this), + TextInput(parentResponder, &m_contentView), m_contentView(textBuffer, draftTextBuffer, textBufferSize, size,horizontalAlignment, verticalAlignment, textColor, backgroundColor), m_hasTwoBuffers(hasTwoBuffers), m_delegate(delegate) { } -KDPoint TextField::ContentView::textOrigin() const { - KDSize textSize = KDText::stringSize(text(), m_fontSize); - return KDPoint(m_horizontalAlignment*(m_frame.width() - textSize.width()-m_cursorView.minimalSizeForOptimalDisplay().width()), m_verticalAlignment*(m_frame.height() - textSize.height())); -} - -void TextField::setDelegate(TextFieldDelegate * delegate) { - m_delegate = delegate; -} - void TextField::setDraftTextBuffer(char * draftTextBuffer) { m_contentView.setDraftTextBuffer(draftTextBuffer); } -Toolbox * TextField::toolbox() { - if (m_delegate) { - return m_delegate->toolboxForTextField(this); - } - return nullptr; -} - bool TextField::isEditing() const { return m_contentView.isEditing(); } -const char * TextField::text() const { - return m_contentView.text(); -} - -int TextField::draftTextLength() const { +size_t TextField::draftTextLength() const { assert(isEditing()); - return m_contentView.draftTextLength(); -} - -int TextField::cursorLocation() const{ - return m_contentView.cursorLocation(); -} - -void TextField::setCursorLocation(int location) { - m_contentView.setCursorLocation(location); - scrollToCursor(); + return m_contentView.editedTextLength(); } void TextField::setText(const char * text) { @@ -246,18 +182,6 @@ void TextField::setText(const char * text) { } } -void TextField::setBackgroundColor(KDColor backgroundColor) { - m_contentView.setBackgroundColor(backgroundColor); -} - -KDColor TextField::backgroundColor() const { - return m_contentView.backgroundColor(); -} - -void TextField::setTextColor(KDColor textColor) { - m_contentView.setTextColor(textColor); -} - void TextField::setAlignment(float horizontalAlignment, float verticalAlignment) { m_contentView.setAlignment(horizontalAlignment, verticalAlignment); } @@ -269,14 +193,6 @@ void TextField::setEditing(bool isEditing, bool reinitDrafBuffer) { } } -bool TextField::insertTextAtLocation(const char * text, int location) { - if (m_contentView.insertTextAtLocation(text, location)) { - scrollToCursor(); - return true; - } - return false; -} - bool TextField::privateHandleEvent(Ion::Events::Event event) { if (Responder::handleEvent(event)) { /* The only event Responder handles is 'Toolbox' displaying. In that case, @@ -287,20 +203,16 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { return true; } if (event == Ion::Events::Left && isEditing() && cursorLocation() > 0) { - setCursorLocation(cursorLocation()-1); - return true; + return setCursorLocation(cursorLocation()-1); } if (event == Ion::Events::ShiftLeft && isEditing()) { - setCursorLocation(0); - return true; + return setCursorLocation(0); } if (event == Ion::Events::Right && isEditing() && cursorLocation() < draftTextLength()) { - setCursorLocation(cursorLocation()+1); - return true; + return setCursorLocation(cursorLocation()+1); } if (event == Ion::Events::ShiftRight && isEditing()) { - setCursorLocation(draftTextLength()); - return true; + return setCursorLocation(draftTextLength()); } if (isEditing() && textFieldShouldFinishEditing(event)) { char bufferText[ContentView::k_maxBufferSize]; @@ -314,7 +226,7 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { reloadScroll(true); return true; } - /* if the text was refused (textFieldDidFinishEditing returned false, we + /* if the text was refused (textInputDidFinishEditing returned false, we * reset the textfield in the same state as before */ char bufferDraft[ContentView::k_maxBufferSize]; strlcpy(bufferDraft, m_contentView.textBuffer(), ContentView::k_maxBufferSize); @@ -324,20 +236,8 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { setCursorLocation(cursorLoc); return true; } - if ((event == Ion::Events::OK || event == Ion::Events::EXE) && !isEditing()) { - setEditing(true); - /* If the text could not be inserted (buffer is full), we set the cursor - * at the end of the text. */ - int nextCursorLocation = draftTextLength(); - if (insertTextAtLocation(m_contentView.textBuffer(), cursorLocation())) { - nextCursorLocation = strlen(m_contentView.draftTextBuffer()); - } - setCursorLocation(nextCursorLocation); - return true; - } if (event == Ion::Events::Backspace && isEditing()) { - deleteCharPrecedingCursor(); - return true; + return removeChar(); } if (event == Ion::Events::Back && isEditing()) { setEditing(false); @@ -346,7 +246,7 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { return true; } if (event == Ion::Events::Clear && isEditing()) { - if (!deleteEndOfLine()) { + if (!removeEndOfLine()) { setEditing(true, true); } return true; @@ -363,27 +263,10 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { return false; } -void TextField::deleteCharPrecedingCursor() { - m_contentView.deleteCharPrecedingCursor(); - scrollToCursor(); -} - -bool TextField::deleteEndOfLine() { - if (m_contentView.deleteEndOfLine()) { - scrollToCursor(); - return true; - } - return false; -} - KDSize TextField::minimalSizeForOptimalDisplay() const { return m_contentView.minimalSizeForOptimalDisplay(); } -bool TextField::textFieldShouldFinishEditing(Ion::Events::Event event) { - return m_delegate->textFieldShouldFinishEditing(this, event); -} - bool TextField::handleEvent(Ion::Events::Event event) { assert(m_delegate != nullptr); if (m_delegate->textFieldDidReceiveEvent(this, event)) { @@ -395,6 +278,9 @@ bool TextField::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Paste) { return handleEventWithText(Clipboard::sharedClipboard()->storedText()); } + if ((event == Ion::Events::OK || event == Ion::Events::EXE) && !isEditing()) { + return handleEventWithText(m_contentView.textBuffer()); + } size_t previousTextLength = strlen(text()); bool didHandleEvent = privateHandleEvent(event); return m_delegate->textFieldDidHandleEvent(this, didHandleEvent, strlen(text()) != previousTextLength); @@ -404,10 +290,10 @@ void TextField::scrollToCursor() { if (!isEditing()) { return; } - scrollToContentRect(m_contentView.cursorRect(), true); + return TextInput::scrollToCursor(); } -bool TextField::handleEventWithText(const char * eventText) { +bool TextField::handleEventWithText(const char * eventText, bool indentation) { size_t previousTextLength = strlen(text()); if (!isEditing()) { setEditing(true); diff --git a/escher/src/text_input.cpp b/escher/src/text_input.cpp new file mode 100644 index 000000000..67ef0061a --- /dev/null +++ b/escher/src/text_input.cpp @@ -0,0 +1,120 @@ +#include +#include + +/* TextInput::ContentView */ + +TextInput::ContentView::ContentView(KDText::FontSize size, KDColor textColor, KDColor backgroundColor) : + View(), + m_cursorView(), + m_fontSize(size), + m_textColor(textColor), + m_backgroundColor(backgroundColor), + m_cursorIndex(0) +{ +} + +void TextInput::ContentView::setBackgroundColor(KDColor backgroundColor) { + m_backgroundColor = backgroundColor; + markRectAsDirty(bounds()); +} + +void TextInput::ContentView::setTextColor(KDColor textColor) { + m_textColor = textColor; + markRectAsDirty(bounds()); +} + +void TextInput::ContentView::setCursorLocation(int location) { + int adjustedLocation = location < 0 ? 0 : location; + adjustedLocation = adjustedLocation > (signed int)editedTextLength() ? (signed int)editedTextLength() : adjustedLocation; + m_cursorIndex = adjustedLocation; + layoutSubviews(); +} + +KDRect TextInput::ContentView::cursorRect() { + return characterFrameAtIndex(m_cursorIndex); +} + +int TextInput::ContentView::numberOfSubviews() const { + return 1; +} + +View * TextInput::ContentView::subviewAtIndex(int index) { + return &m_cursorView; +} + +void TextInput::ContentView::layoutSubviews() { + m_cursorView.setFrame(cursorRect()); +} + +void TextInput::ContentView::reloadRectFromCursorPosition(size_t index, bool lineBreak) { + KDRect charRect = characterFrameAtIndex(index); + KDRect dirtyRect = KDRect(charRect.x(), charRect.y(), bounds().width() - charRect.x(), charRect.height()); + if (lineBreak) { + dirtyRect = dirtyRect.unionedWith(KDRect(0, charRect.bottom()+1, bounds().width(), bounds().height()-charRect.bottom()-1)); + } + markRectAsDirty(dirtyRect); +} + +/* TextInput */ + +TextInput::TextInput(Responder * parentResponder, View * contentView) : + ScrollableView(parentResponder, contentView, this) +{ +} + +Toolbox * TextInput::toolbox() { + if (delegate()) { + return delegate()->toolboxForTextInput(this); + } + return nullptr; +} + +void TextInput::setBackgroundColor(KDColor backgroundColor) { + contentView()->setBackgroundColor(backgroundColor); +} + +void TextInput::setTextColor(KDColor textColor) { + contentView()->setTextColor(textColor); +} + +bool TextInput::removeChar() { + contentView()->removeChar(); + scrollToCursor(); + return true; +} + +void TextInput::scrollToCursor() { + /* Technically, we do not need to overscroll in text input. However, + * logically, we should layout the scroll view before calling + * scrollToContentRect in case the size of the scroll view has changed and + * then call scrollToContentRect which call another layout of the scroll view + * if the offset has evolved. In order to avoid requiring two layouts, we + * allow overscrolling in scrollToContentRect and the last layout of the + * scroll view corrects the size of the scroll view only once. */ + scrollToContentRect(contentView()->cursorRect(), true); +} + +bool TextInput::setCursorLocation(int location) { + contentView()->setCursorLocation(location); + scrollToCursor(); + return true; +} + +bool TextInput::insertTextAtLocation(const char * text, int 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; +} + +bool TextInput::removeEndOfLine() { + if (contentView()->removeEndOfLine()) { + scrollToCursor(); + return true; + } + return false; +}