Skip to content

Commit 0f80ca6

Browse files
committed
Added cursor to the text input
1 parent 5f9ac71 commit 0f80ca6

2 files changed

Lines changed: 238 additions & 39 deletions

File tree

src/Engine/UI/textinput.cpp

Lines changed: 198 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
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

Comments
 (0)