mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[escher] Create a class TextInput (from which derived TextField,
TextArea)
This commit is contained in:
committed by
EmilieNumworks
parent
c50e57029b
commit
aade7cb2fe
@@ -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<Code::App *>(app());
|
||||
codeApp->pythonToolbox()->setAction(codeApp->toolboxActionForTextField());
|
||||
return codeApp->pythonToolbox();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Code::App *>(app());
|
||||
codeApp->pythonToolbox()->setAction(codeApp->toolboxActionForTextArea());
|
||||
return codeApp->pythonToolbox();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
#include <escher/text_area_delegate.h>
|
||||
#include <escher/text_field.h>
|
||||
#include <escher/text_field_delegate.h>
|
||||
#include <escher/text_input.h>
|
||||
#include <escher/text_input_delegate.h>
|
||||
#include <escher/text_input_helpers.h>
|
||||
#include <escher/text_view.h>
|
||||
#include <escher/tab_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:
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
#ifndef ESCHER_TEXT_AREA_H
|
||||
#define ESCHER_TEXT_AREA_H
|
||||
|
||||
#include <escher/text_input.h>
|
||||
#include <escher/text_area_delegate.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <escher/scrollable_view.h>
|
||||
#include <escher/text_cursor_view.h>
|
||||
#include <escher/text_area_delegate.h>
|
||||
|
||||
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<const char *>(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;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#ifndef ESCHER_TEXT_AREA_DELEGATE_H
|
||||
#define ESCHER_TEXT_AREA_DELEGATE_H
|
||||
|
||||
#include <escher/text_input_delegate.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -1,74 +1,53 @@
|
||||
#ifndef ESCHER_TEXT_FIELD_H
|
||||
#define ESCHER_TEXT_FIELD_H
|
||||
|
||||
#include <escher/scrollable_view.h>
|
||||
#include <escher/text_input.h>
|
||||
#include <escher/text_field_delegate.h>
|
||||
#include <escher/text_cursor_view.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
#ifndef ESCHER_TEXT_FIELD_DELEGATE_H
|
||||
#define ESCHER_TEXT_FIELD_DELEGATE_H
|
||||
|
||||
#include <escher/text_input_delegate.h>
|
||||
|
||||
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
|
||||
|
||||
65
escher/include/escher/text_input.h
Normal file
65
escher/include/escher/text_input.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef ESCHER_TEXT_INPUT_H
|
||||
#define ESCHER_TEXT_INPUT_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <escher/scrollable_view.h>
|
||||
#include <escher/text_cursor_view.h>
|
||||
#include <escher/text_input_delegate.h>
|
||||
|
||||
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<ContentView *>(nonEditableContentView());
|
||||
}
|
||||
virtual const ContentView * nonEditableContentView() const = 0;
|
||||
private:
|
||||
virtual TextInputDelegate * delegate() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
11
escher/include/escher/text_input_delegate.h
Normal file
11
escher/include/escher/text_input_delegate.h
Normal file
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<const char *>(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);
|
||||
|
||||
120
escher/src/text_input.cpp
Normal file
120
escher/src/text_input.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <escher/text_input.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user