33#include " textinput.h"
44
55#include " sentencesystem.h"
6+ #include " ttftext.h"
67
78#include " logger.h"
89
@@ -23,18 +24,17 @@ namespace pg
2324 {
2425 auto text = entity->get <TextInputComponent>();
2526
26- text->text += event.text ;
27-
28- // if (entity->entity->has<SentenceText>())
29- // {
30- // entity->entity->get<SentenceText>()->setText(text->text);
31- // }
27+ // Insert text at cursor position instead of appending
28+ text->text .insert (text->cursorPos , event.text );
29+ text->cursorPos += event.text .size ();
3230
3331 if (entity->entity ->has <TTFText>())
3432 {
3533 entity->entity ->get <TTFText>()->setText (text->text );
3634 }
3735
36+ updateCursorVisual (entity->entity , text);
37+
3838 ecsRef->sendEvent (CurrentTextInputTextChanged{text->text , entity->entityId });
3939
4040 LOG_INFO (DOM , " Current Text: " << text->text );
@@ -44,7 +44,10 @@ namespace pg
4444
4545 void TextInputSystem::onEvent (const OnSDLScanCode& event)
4646 {
47- if (event.key != SDL_SCANCODE_RETURN and event.key != SDL_SCANCODE_BACKSPACE )
47+ if (event.key != SDL_SCANCODE_RETURN and event.key != SDL_SCANCODE_BACKSPACE
48+ and event.key != SDL_SCANCODE_LEFT and event.key != SDL_SCANCODE_RIGHT
49+ and event.key != SDL_SCANCODE_HOME and event.key != SDL_SCANCODE_END
50+ and event.key != SDL_SCANCODE_DELETE )
4851 return ;
4952
5053 for (const auto & entity : viewGroup<TextInputComponent, FocusableComponent>())
@@ -53,65 +56,170 @@ namespace pg
5356
5457 if (focus->focused )
5558 {
59+ auto text = entity->get <TextInputComponent>();
60+
5661 switch (event.key )
5762 {
5863 case SDL_SCANCODE_RETURN :
5964 {
60- auto text = entity->get <TextInputComponent>();
61-
6265 text->returnText = text->text ;
6366
6467 if (text->clearTextAfterEnter )
68+ {
6569 text->text = " " ;
70+ text->cursorPos = 0 ;
71+ }
6672
67- auto event = text->event ;
68-
69- event.values [" return" ] = text->returnText ;
73+ auto ev = text->event ;
7074
71- ecsRef-> sendEvent (event) ;
75+ ev. values [ " return " ] = text-> returnText ;
7276
73- // if (entity->entity->has<SentenceText>())
74- // {
75- // entity->entity->get<SentenceText>()->setText(text->text);
76- // }
77+ ecsRef->sendEvent (ev);
7778
7879 if (entity->entity ->has <TTFText>())
7980 {
8081 entity->entity ->get <TTFText>()->setText (text->text );
8182 }
8283
84+ updateCursorVisual (entity->entity , text);
85+
8386 ecsRef->sendEvent (CurrentTextInputTextChanged{text->text , entity->entityId });
8487 }
8588 break ;
8689
8790 case SDL_SCANCODE_BACKSPACE :
8891 {
89- auto text = entity->get <TextInputComponent>();
92+ if (text->cursorPos > 0 )
93+ {
94+ // If control is held, remove from cursor back to previous word boundary
95+ if (inputHandler->isKeyPressed (SDL_SCANCODE_LCTRL ) or inputHandler->isKeyPressed (SDL_SCANCODE_RCTRL ))
96+ {
97+ size_t eraseEnd = text->cursorPos ;
98+
99+ // Skip back past current position
100+ text->cursorPos --;
90101
91- // Remove the last character
92- if (not text->text .empty ())
93- text->text .pop_back ();
102+ // Skip back to word boundary
103+ while (text->cursorPos > 0 and text->text [text->cursorPos - 1 ] != ' ' )
104+ {
105+ text->cursorPos --;
106+ }
107+
108+ text->text .erase (text->cursorPos , eraseEnd - text->cursorPos );
109+ }
110+ else
111+ {
112+ text->cursorPos --;
113+ text->text .erase (text->cursorPos , 1 );
114+ }
115+
116+ if (entity->entity ->has <TTFText>())
117+ {
118+ entity->entity ->get <TTFText>()->setText (text->text );
119+ }
120+
121+ updateCursorVisual (entity->entity , text);
122+
123+ ecsRef->sendEvent (CurrentTextInputTextChanged{text->text , entity->entityId });
124+ }
125+ }
126+ break ;
94127
95- // If control is held try to remove everything till the beginning or till another space is found
96- if (inputHandler->isKeyPressed (SDL_SCANCODE_LCTRL ) or inputHandler->isKeyPressed (SDL_SCANCODE_RCTRL ))
128+ case SDL_SCANCODE_DELETE :
129+ {
130+ if (text->cursorPos < text->text .size ())
97131 {
98- while (not text->text .empty () and not (text->text .back () == ' ' ))
132+ if (inputHandler->isKeyPressed (SDL_SCANCODE_LCTRL ) or inputHandler->isKeyPressed (SDL_SCANCODE_RCTRL ))
133+ {
134+ size_t eraseStart = text->cursorPos ;
135+
136+ // Skip forward to next word boundary
137+ size_t eraseEnd = text->cursorPos ;
138+ while (eraseEnd < text->text .size () and text->text [eraseEnd] != ' ' )
139+ {
140+ eraseEnd++;
141+ }
142+
143+ // Also consume the space after the word
144+ if (eraseEnd < text->text .size () and text->text [eraseEnd] == ' ' )
145+ eraseEnd++;
146+
147+ text->text .erase (eraseStart, eraseEnd - eraseStart);
148+ }
149+ else
99150 {
100- text->text .pop_back ( );
151+ text->text .erase (text-> cursorPos , 1 );
101152 }
153+
154+ if (entity->entity ->has <TTFText>())
155+ {
156+ entity->entity ->get <TTFText>()->setText (text->text );
157+ }
158+
159+ updateCursorVisual (entity->entity , text);
160+
161+ ecsRef->sendEvent (CurrentTextInputTextChanged{text->text , entity->entityId });
102162 }
163+ }
164+ break ;
103165
104- // if (entity->entity->has<SentenceText>())
105- // {
106- // entity->entity->get<SentenceText>()->setText(text->text);
107- // }
166+ case SDL_SCANCODE_LEFT :
167+ {
168+ if (text->cursorPos > 0 )
169+ {
170+ if (inputHandler->isKeyPressed (SDL_SCANCODE_LCTRL ) or inputHandler->isKeyPressed (SDL_SCANCODE_RCTRL ))
171+ {
172+ // Jump to previous word boundary
173+ text->cursorPos --;
174+ while (text->cursorPos > 0 and text->text [text->cursorPos - 1 ] != ' ' )
175+ {
176+ text->cursorPos --;
177+ }
178+ }
179+ else
180+ {
181+ text->cursorPos --;
182+ }
108183
109- if (entity->entity ->has <TTFText>())
184+ updateCursorVisual (entity->entity , text);
185+ }
186+ }
187+ break ;
188+
189+ case SDL_SCANCODE_RIGHT :
190+ {
191+ if (text->cursorPos < text->text .size ())
110192 {
111- entity->entity ->get <TTFText>()->setText (text->text );
193+ if (inputHandler->isKeyPressed (SDL_SCANCODE_LCTRL ) or inputHandler->isKeyPressed (SDL_SCANCODE_RCTRL ))
194+ {
195+ // Jump to next word boundary
196+ text->cursorPos ++;
197+ while (text->cursorPos < text->text .size () and text->text [text->cursorPos ] != ' ' )
198+ {
199+ text->cursorPos ++;
200+ }
201+ }
202+ else
203+ {
204+ text->cursorPos ++;
205+ }
206+
207+ updateCursorVisual (entity->entity , text);
112208 }
209+ }
210+ break ;
113211
114- ecsRef->sendEvent (CurrentTextInputTextChanged{text->text , entity->entityId });
212+ case SDL_SCANCODE_HOME :
213+ {
214+ text->cursorPos = 0 ;
215+ updateCursorVisual (entity->entity , text);
216+ }
217+ break ;
218+
219+ case SDL_SCANCODE_END :
220+ {
221+ text->cursorPos = text->text .size ();
222+ updateCursorVisual (entity->entity , text);
115223 }
116224 break ;
117225
@@ -120,14 +228,71 @@ namespace pg
120228 LOG_ERROR (DOM , " Received unknown scan code: " << event.key );
121229 }
122230 }
231+ }
232+ }
233+ }
234+
235+ void TextInputSystem::onEvent (const OnFocus& event)
236+ {
237+ for (const auto & entity : viewGroup<TextInputComponent, FocusableComponent>())
238+ {
239+ auto text = entity->get <TextInputComponent>();
123240
241+ if (not text->cursorEntity )
242+ continue ;
243+
244+ auto cursorUi = text->cursorEntity ->get <PositionComponent>();
245+
246+ if (not cursorUi)
247+ continue ;
248+
249+ bool isFocused = (entity->entityId == event.id );
250+
251+ cursorUi->setVisible (isFocused);
252+ }
253+ }
254+
255+ void TextInputSystem::updateCursorVisual (EntityRef entity, CompRef<TextInputComponent> textComp)
256+ {
257+ if (not textComp->cursorEntity )
258+ return ;
259+
260+ auto cursorAnchor = textComp->cursorEntity ->get <UiAnchor>();
261+
262+ if (not cursorAnchor)
263+ return ;
264+
265+ // Compute the X offset of the cursor by summing glyph advances up to cursorPos
266+ float cursorX = 0 .0f ;
267+
268+ if (entity->has <TTFText>())
269+ {
270+ auto ttf = entity->get <TTFText>();
271+ auto * ttfSystem = ecsRef->getSystem <TTFTextSystem>();
272+
273+ if (ttfSystem)
274+ {
275+ const auto & fontChars = ttfSystem->charactersMap [ttf->fontPath ];
276+ float scale = ttf->scale ;
277+
278+ for (size_t i = 0 ; i < textComp->cursorPos and i < textComp->text .size (); i++)
279+ {
280+ char c = textComp->text [i];
281+ auto it = fontChars.find (c);
282+ if (it != fontChars.end ())
283+ {
284+ cursorX += (it->second .advance >> 6 ) * scale;
285+ }
286+ }
124287 }
125288 }
289+
290+ cursorAnchor->setLeftMargin (cursorX);
126291 }
127292
128293 void TextInputSystem::execute ()
129294 {
130295
131296 }
132297
133- }
298+ }
0 commit comments