Numworks Epsilon  1.4.1
Graphing Calculator Operating System
text_field.cpp
Go to the documentation of this file.
1 #include <escher/text_field.h>
3 #include <escher/clipboard.h>
4 #include <assert.h>
5 
6 /* TextField::ContentView */
7 
8 TextField::ContentView::ContentView(char * textBuffer, char * draftTextBuffer, size_t textBufferSize, KDText::FontSize size, float horizontalAlignment, float verticalAlignment, KDColor textColor, KDColor backgroundColor) :
9  TextInput::ContentView(size, textColor, backgroundColor),
10  m_isEditing(false),
11  m_textBuffer(textBuffer),
12  m_draftTextBuffer(draftTextBuffer),
13  m_currentDraftTextLength(0),
14  m_textBufferSize(textBufferSize),
15  m_horizontalAlignment(horizontalAlignment),
16  m_verticalAlignment(verticalAlignment)
17 {
18  assert(m_textBufferSize <= k_maxBufferSize);
19 }
20 
21 void TextField::ContentView::setDraftTextBuffer(char * draftTextBuffer) {
22  m_draftTextBuffer = draftTextBuffer;
23 }
24 
26  KDColor bckCol = m_backgroundColor;
27  if (m_isEditing) {
28  bckCol = KDColorWhite;
29  }
30  ctx->fillRect(rect, bckCol);
31  ctx->drawString(text(), characterFrameAtIndex(0).origin(), m_fontSize, m_textColor, bckCol);
32 }
33 
34 const char * TextField::ContentView::text() const {
35  if (m_isEditing) {
36  return const_cast<const char *>(m_draftTextBuffer);
37  }
38  return const_cast<const char *>(m_textBuffer);
39 }
40 
42  return m_currentDraftTextLength;
43 }
44 
46  reloadRectFromCursorPosition(0);
47  if (m_isEditing) {
48  strlcpy(m_draftTextBuffer, text, m_textBufferSize);
49  int textLength = strlen(text) >= m_textBufferSize ? m_textBufferSize-1 : strlen(text);
50  m_currentDraftTextLength = textLength;
51  } else {
52  strlcpy(m_textBuffer, text, m_textBufferSize);
53  }
54  reloadRectFromCursorPosition(0);
55 }
56 
57 void TextField::ContentView::setAlignment(float horizontalAlignment, float verticalAlignment) {
58  m_horizontalAlignment = horizontalAlignment;
59  m_verticalAlignment = verticalAlignment;
61 }
62 
63 void TextField::ContentView::setEditing(bool isEditing, bool reinitDrafBuffer) {
64  if (m_isEditing == isEditing && !reinitDrafBuffer) {
65  return;
66  }
67  if (reinitDrafBuffer) {
68  reinitDraftTextBuffer();
69  }
70  m_isEditing = isEditing;
73 }
74 
77  m_draftTextBuffer[0] = 0;
78  m_currentDraftTextLength = 0;
79 }
80 
81 bool TextField::ContentView::insertTextAtLocation(const char * text, int location) {
82  int textSize = strlen(text);
83  if (m_currentDraftTextLength + textSize >= m_textBufferSize || textSize == 0) {
84  return false;
85  }
86  for (int k = m_currentDraftTextLength; k >= location && k >= 0; k--) {
87  m_draftTextBuffer[k+textSize] = m_draftTextBuffer[k];
88  }
89  strlcpy(&m_draftTextBuffer[location], text, textSize);
90  if (location+textSize > 0) {
91  m_draftTextBuffer[location+textSize-1] = text[textSize-1];
92  }
93  m_currentDraftTextLength += textSize;
94  for (size_t i = 0; i < m_currentDraftTextLength; i++) {
95  if (m_draftTextBuffer[i] == '\n') {
96  m_draftTextBuffer[i] = 0;
97  m_currentDraftTextLength = i;
98  break;
99  }
100  }
101  reloadRectFromCursorPosition((m_horizontalAlignment == 0.0f ? location : 0));
102  return true;
103 }
104 
106  KDSize charSize = KDText::charSize(m_fontSize);
107  if (m_isEditing) {
108  return KDSize(charSize.width()*strlen(text())+m_cursorView.minimalSizeForOptimalDisplay().width(), charSize.height());
109  }
110  return KDSize(charSize.width()*strlen(text()), charSize.height());
111 }
112 
114  if (cursorLocation() <= 0) {
115  return false;
116  }
117  m_currentDraftTextLength--;
118  if (m_horizontalAlignment > 0.0f) {
119  reloadRectFromCursorPosition(0);
120  }
122  if( m_horizontalAlignment == 0.0f) {
123  reloadRectFromCursorPosition(cursorLocation());
124  }
125  for (int k = cursorLocation(); k < (signed char)m_currentDraftTextLength; k ++) {
126  m_draftTextBuffer[k] = m_draftTextBuffer[k+1];
127  }
128  m_draftTextBuffer[m_currentDraftTextLength] = 0;
129  layoutSubviews();
130  return true;
131 }
132 
134  if (m_currentDraftTextLength == cursorLocation()) {
135  return false;
136  }
137  reloadRectFromCursorPosition((m_horizontalAlignment == 0.0f ? cursorLocation() : 0));
138  m_currentDraftTextLength = cursorLocation();
139  m_draftTextBuffer[cursorLocation()] = 0;
140  layoutSubviews();
141  return true;
142 }
143 
144 void TextField::ContentView::layoutSubviews() {
145  if (!m_isEditing) {
146  m_cursorView.setFrame(KDRectZero);
147  return;
148  }
150 }
151 
152 KDRect TextField::ContentView::characterFrameAtIndex(size_t index) const {
153  KDSize charSize = KDText::charSize(m_fontSize);
154  KDSize textSize = KDText::stringSize(text(), m_fontSize);
155  KDCoordinate cursorWidth = m_cursorView.minimalSizeForOptimalDisplay().width();
156  return KDRect(m_horizontalAlignment*(m_frame.width() - textSize.width()-cursorWidth)+ index * charSize.width(), m_verticalAlignment*(m_frame.height() - charSize.height()), charSize);
157 }
158 
159 /* TextField */
160 
161 TextField::TextField(Responder * parentResponder, char * textBuffer, char * draftTextBuffer,
162  size_t textBufferSize, TextFieldDelegate * delegate, bool hasTwoBuffers, KDText::FontSize size,
163  float horizontalAlignment, float verticalAlignment, KDColor textColor, KDColor backgroundColor) :
165  m_contentView(textBuffer, draftTextBuffer, textBufferSize, size,horizontalAlignment, verticalAlignment, textColor, backgroundColor),
166  m_hasTwoBuffers(hasTwoBuffers),
167  m_delegate(delegate)
168 {
169 }
170 
171 void TextField::setDraftTextBuffer(char * draftTextBuffer) {
172  m_contentView.setDraftTextBuffer(draftTextBuffer);
173 }
174 
175 bool TextField::isEditing() const {
176  return m_contentView.isEditing();
177 }
178 
180  assert(isEditing());
182 }
183 
184 void TextField::setText(const char * text) {
185  reloadScroll();
187  if (isEditing()) {
189  }
190 }
191 
192 void TextField::setAlignment(float horizontalAlignment, float verticalAlignment) {
193  m_contentView.setAlignment(horizontalAlignment, verticalAlignment);
194 }
195 
196 void TextField::setEditing(bool isEditing, bool reinitDrafBuffer) {
197  m_contentView.setEditing(isEditing, reinitDrafBuffer);
198  if (reinitDrafBuffer) {
199  reloadScroll();
200  }
201 }
202 
203 bool TextField::privateHandleEvent(Ion::Events::Event event) {
204  if (Responder::handleEvent(event)) {
205  /* The only event Responder handles is 'Toolbox' displaying. In that case,
206  * the text field is forced into editing mode. */
207  if (!isEditing()) {
208  setEditing(true);
209  }
210  return true;
211  }
212  if (event == Ion::Events::Left && isEditing() && cursorLocation() > 0) {
213  return setCursorLocation(cursorLocation()-1);
214  }
215  if (event == Ion::Events::ShiftLeft && isEditing()) {
216  return setCursorLocation(0);
217  }
218  if (event == Ion::Events::Right && isEditing() && cursorLocation() < draftTextLength()) {
219  return setCursorLocation(cursorLocation()+1);
220  }
221  if (event == Ion::Events::ShiftRight && isEditing()) {
223  }
224  if (isEditing() && textFieldShouldFinishEditing(event)) {
225  char bufferText[ContentView::k_maxBufferSize];
228  int cursorLoc = cursorLocation();
229  setEditing(false, m_hasTwoBuffers);
230  if (m_delegate->textFieldDidFinishEditing(this, text(), event)) {
231  /* We allow overscroll to avoid calling layoutSubviews twice because the
232  * content might have changed. */
233  reloadScroll(true);
234  return true;
235  }
236  /* if the text was refused (textInputDidFinishEditing returned false, we
237  * reset the textfield in the same state as before */
238  char bufferDraft[ContentView::k_maxBufferSize];
240  setText(bufferText);
241  setEditing(true);
242  setText(bufferDraft);
243  setCursorLocation(cursorLoc);
244  return true;
245  }
246  if (event == Ion::Events::Backspace && isEditing()) {
247  return removeChar();
248  }
249  if (event == Ion::Events::Back && isEditing()) {
250  setEditing(false);
251  reloadScroll();
252  m_delegate->textFieldDidAbortEditing(this, text());
253  return true;
254  }
255  if (event == Ion::Events::Clear && isEditing()) {
256  if (!removeEndOfLine()) {
257  setEditing(true, true);
258  }
259  return true;
260  }
261  if (event == Ion::Events::Copy && !isEditing()) {
263  return true;
264  }
265  if (event == Ion::Events::Cut && !isEditing()) {
267  setEditing(true, true);
268  return true;
269  }
270  return false;
271 }
272 
275 }
276 
278  assert(m_delegate != nullptr);
279  if (m_delegate->textFieldDidReceiveEvent(this, event)) {
280  return true;
281  }
282  if (event.hasText()) {
283  return handleEventWithText(event.text());
284  }
285  if (event == Ion::Events::Paste) {
286  return handleEventWithText(Clipboard::sharedClipboard()->storedText());
287  }
288  if ((event == Ion::Events::OK || event == Ion::Events::EXE) && !isEditing()) {
290  }
291  size_t previousTextLength = strlen(text());
292  bool didHandleEvent = privateHandleEvent(event);
293  return m_delegate->textFieldDidHandleEvent(this, didHandleEvent, strlen(text()) != previousTextLength);
294 }
295 
297  if (!isEditing()) {
298  return;
299  }
300  return TextInput::scrollToCursor();
301 }
302 
303 bool TextField::handleEventWithText(const char * eventText, bool indentation) {
304  size_t previousTextLength = strlen(text());
305  if (!isEditing()) {
306  setEditing(true);
307  }
308  int nextCursorLocation = draftTextLength();
309  if (insertTextAtLocation(eventText, cursorLocation())) {
310  /* The cursor position depends on the text as we sometimes want to
311  * position the cursor at the end of the text and sometimes after the
312  * first parenthesis. */
313  nextCursorLocation = cursorLocation() + TextInputHelpers::CursorIndexInCommand(eventText);
314  }
315  setCursorLocation(nextCursorLocation);
316  return m_delegate->textFieldDidHandleEvent(this, true, strlen(text()) != previousTextLength);
317 }
bool setCursorLocation(int location)
Definition: text_input.cpp:97
bool isEditing() const
Definition: text_field.cpp:175
void setDraftTextBuffer(char *draftTextBuffer)
Definition: text_field.cpp:21
void reloadScroll(bool forceRelayout=false)
#define assert(e)
Definition: assert.h:9
KDPoint drawString(const char *text, KDPoint p, KDText::FontSize size=KDText::FontSize::Large, KDColor textColor=KDColorBlack, KDColor backgroundColor=KDColorWhite, int maxLength=-1)
Definition: context_text.cpp:9
void setText(const char *text)
Definition: text_field.cpp:184
size_t cursorLocation() const
Definition: text_input.h:19
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)
Definition: text_field.cpp:161
constexpr Event EXE
Definition: events.h:114
static constexpr int k_maxBufferSize
Definition: text_field.h:56
int16_t KDCoordinate
Definition: coordinate.h:6
constexpr KDCoordinate width() const
Definition: size.h:10
static KDSize stringSize(const char *text, FontSize size=FontSize::Large)
Definition: text.cpp:6
size_t editedTextLength() const override
Definition: text_field.cpp:41
Responder * parentResponder() const
Definition: responder.cpp:12
bool removeChar() override
Definition: text_field.cpp:113
void markRectAsDirty(KDRect rect)
Definition: view.cpp:39
void setDraftTextBuffer(char *draftTextBuffer)
Definition: text_field.cpp:171
size_t strlcpy(char *dst, const char *src, size_t len)
Definition: strlcpy.c:3
void setAlignment(float horizontalAlignment, float verticalAlignment)
Definition: text_field.cpp:192
void setText(const char *text)
Definition: text_field.cpp:45
void setEditing(bool isEditing, bool reinitDraftBuffer)
Definition: text_field.cpp:63
bool removeEndOfLine() override
Definition: text_field.cpp:133
bool textFieldShouldFinishEditing(Ion::Events::Event event)
Definition: text_field.h:27
virtual bool handleEvent(Ion::Events::Event event)
Definition: responder.cpp:20
virtual void setEditing(bool isEditing, bool reinitDraftBuffer=true)
Definition: text_field.cpp:196
virtual bool textFieldDidReceiveEvent(TextField *textField, Ion::Events::Event event)=0
constexpr Event Back
Definition: events.h:66
constexpr Event Paste
Definition: events.h:124
const char * text() const override
Definition: text_field.cpp:34
void store(const char *storedText)
Definition: clipboard.cpp:9
Definition: size.h:6
virtual void scrollToCursor()
Definition: text_input.cpp:86
bool hasText() const
Definition: events.cpp:122
KDRect m_frame
Definition: view.h:66
constexpr KDColor KDColorWhite
Definition: color.h:42
size_t strlen(const char *s)
Definition: strlen.c:3
char * draftTextBuffer()
Definition: text_field.h:38
virtual bool removeEndOfLine()
Definition: text_input.cpp:114
bool isEditing() const
Definition: text_field.h:34
void drawRect(KDContext *ctx, KDRect rect) const override
Definition: text_field.cpp:25
bool insertTextAtLocation(const char *textBuffer, int location)
Definition: text_input.cpp:103
virtual bool textFieldDidAbortEditing(TextField *textField, const char *text)
ContentView(char *textBuffer, char *draftTextBuffer, size_t textBufferSize, KDText::FontSize size, float horizontalAlignment=0.0f, float verticalAlignment=0.5f, KDColor textColor=KDColorBlack, KDColor=KDColorWhite)
Definition: text_field.cpp:8
const char * text() const
Definition: events.cpp:61
#define false
Definition: stdbool.h:9
KDSize minimalSizeForOptimalDisplay() const override
Definition: text_field.cpp:105
bool removeChar()
Definition: text_input.cpp:80
KDSize minimalSizeForOptimalDisplay() const override
Definition: text_field.cpp:273
constexpr Event Left
Definition: events.h:61
const char * text() const
Definition: text_input.h:14
virtual bool textFieldDidHandleEvent(TextField *textField, bool returnValue, bool textHasChanged)
constexpr Event ShiftLeft
Definition: events.h:118
constexpr KDRect KDRectZero
Definition: rect.h:70
constexpr Event Copy
Definition: events.h:123
Definition: rect.h:26
void fillRect(KDRect rect, KDColor color)
Definition: context_rect.cpp:8
ContentView m_contentView
Definition: text_field.h:69
Definition: color.h:6
bool handleEventWithText(const char *text, bool indenting=false) override
Definition: text_field.cpp:303
constexpr Event ShiftRight
Definition: events.h:119
size_t draftTextLength() const
Definition: text_field.cpp:179
virtual bool textFieldDidFinishEditing(TextField *textField, const char *text, Ion::Events::Event event)
bool handleEvent(Ion::Events::Event event) override
Definition: text_field.cpp:277
constexpr Event Right
Definition: events.h:64
KDCoordinate width() const
Definition: rect.h:39
void layoutSubviews() override
static Clipboard * sharedClipboard()
Definition: clipboard.cpp:5
static constexpr KDSize charSize(FontSize size=FontSize::Large)
Definition: text.h:16
constexpr Event Cut
Definition: events.h:122
KDColor backgroundColor() const
Definition: text_input.h:16
bool insertTextAtLocation(const char *text, int location) override
Definition: text_field.cpp:81
constexpr Event OK
Definition: events.h:65
void setAlignment(float horizontalAlignment, float verticalAlignment)
Definition: text_field.cpp:57
virtual void layoutSubviews() override
Definition: text_input.cpp:45
constexpr Event Clear
Definition: events.h:125
FontSize
Definition: text.h:10
KDCoordinate height() const
Definition: rect.h:40
KDRect bounds() const
Definition: view.cpp:157
void scrollToCursor() override
Definition: text_field.cpp:296
int CursorIndexInCommand(const char *text)
constexpr Event Backspace
Definition: events.h:76
constexpr KDCoordinate height() const
Definition: size.h:11