R-Type
Distributed multiplayer game engine in C++
Loading...
Searching...
No Matches
RaylibTextInput.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** r-type
4** File description:
5** RaylibTextInput - Raylib implementation of ITextInput
6*/
7
9
10#include <raylib.h>
11#include <algorithm>
12
13namespace UI {
14 RaylibTextInput::RaylibTextInput(Graphics::IGraphics &graphics) : _graphics(graphics) {}
15
17 if (!_enabled) {
18 return;
19 }
20
22
23 if (_focused) {
28 }
29 }
30
32 bool mousePressed = _graphics.IsMouseButtonPressed(0);
33 if (mousePressed) {
34 bool wasOver = IsMouseOver();
35 SetFocused(wasOver);
36 }
37 }
38
46
48 // Handle paste (Ctrl+V or Cmd+V) - cross-platform
49 bool ctrlDown = _graphics.IsKeyDown(KEY_LEFT_CONTROL) || _graphics.IsKeyDown(KEY_RIGHT_CONTROL);
50 bool cmdDown = _graphics.IsKeyDown(KEY_LEFT_SUPER) || _graphics.IsKeyDown(KEY_RIGHT_SUPER);
51
52 if (!_graphics.IsKeyPressed(KEY_V) || !(ctrlDown || cmdDown)) {
53 return;
54 }
55
56 const char *clipboardText = GetClipboardText();
57 if (clipboardText == nullptr) {
58 return;
59 }
60
61 std::string pasteText(clipboardText);
62 bool textChanged = false;
63
64 // Validate and add each character from clipboard
65 for (char c : pasteText) {
66 // Only paste printable characters
67 if (c >= 32 && c <= 126) {
68 if (TryAddCharacter(c)) {
69 textChanged = true;
70 } else {
71 // Stop pasting if we hit an invalid character or max length
72 break;
73 }
74 }
75 }
76
77 if (textChanged && _onTextChanged) {
79 }
80
81 if (textChanged) {
83 }
84 }
85
87 if (!_graphics.IsKeyDown(KEY_BACKSPACE) || _text.empty()) {
88 // Reset backspace repeat counter when key is released
89 _backspaceTimer = 0.0F;
91 return;
92 }
93
94 bool shouldDelete = false;
95
96 if (_graphics.IsKeyPressed(KEY_BACKSPACE)) {
97 // Initial press - delete immediately and start timer
98 shouldDelete = true;
99 _backspaceTimer = 0.0F;
100 } else {
101 // Key is held down - check repeat timing
103
104 // Initial delay before repeat starts
106 // Calculate how many characters to delete based on repeat rate
107 float timeSinceDelay = _backspaceTimer - BACKSPACE_REPEAT_DELAY;
108 int repeatCount = static_cast<int>(timeSinceDelay / BACKSPACE_REPEAT_RATE);
109
110 if (repeatCount > _lastBackspaceRepeat) {
111 shouldDelete = true;
112 _lastBackspaceRepeat = repeatCount;
113 }
114 }
115 }
116
117 if (shouldDelete) {
118 _text.pop_back();
119 if (_onTextChanged) {
121 }
122 ResetCursor();
123 }
124 }
125
127 int key = _graphics.GetCharPressed();
128 while (key > 0) {
129 // Check if character is printable and within limits
130 if (key >= 32 && key <= 126) {
131 char c = static_cast<char>(key);
132 if (TryAddCharacter(c)) {
133 if (_onTextChanged) {
135 }
136 ResetCursor();
137 }
138 }
140 }
141 }
142
144 // Check max length
145 if (_maxLength != 0 && _text.length() >= _maxLength) {
146 return false;
147 }
148
149 // Check regex validation
150 if (!IsCharValid(c)) {
151 return false;
152 }
153
154 _text += c;
155 return true;
156 }
157
159 _cursorVisible = true;
160 _cursorBlinkTimer = 0.0F;
161 }
162
164 // Draw background
165 _graphics.DrawRectangle(static_cast<int>(_x), static_cast<int>(_y), static_cast<int>(_width),
166 static_cast<int>(_height), _backgroundColor);
167
168 // Draw border (different color when focused)
169 unsigned int borderColor = _focused ? _activeBorderColor : _borderColor;
170 _graphics.DrawRectangleLines(static_cast<int>(_x), static_cast<int>(_y), static_cast<int>(_width),
171 static_cast<int>(_height), borderColor);
172
173 // Draw thicker border when focused
174 if (_focused) {
175 _graphics.DrawRectangleLines(static_cast<int>(_x + 1), static_cast<int>(_y + 1),
176 static_cast<int>(_width - 2), static_cast<int>(_height - 2),
177 borderColor);
178 }
179
180 // Calculate text position (left-aligned with padding)
181 float textX = _x + TEXT_PADDING;
182 float textY = _y + (_height / 2.0F) - (_textSize / 2.0F);
183
184 // Draw text or placeholder
185 if (_text.empty() && !_placeholder.empty()) {
186 // Draw placeholder
187 _graphics.DrawText(_placeholder.c_str(), static_cast<int>(textX), static_cast<int>(textY),
189 } else if (!_text.empty()) {
190 // Draw actual text or masked text (password mode)
191 if (_passwordMode) {
192 // Mask password with asterisks
193 std::string maskedText(_text.length(), '*');
194 _graphics.DrawText(maskedText.c_str(), static_cast<int>(textX), static_cast<int>(textY),
196 } else {
197 _graphics.DrawText(_text.c_str(), static_cast<int>(textX), static_cast<int>(textY), _textSize,
198 _textColor);
199 }
200 }
201
202 // Draw blinking cursor when focused
203 if (_focused && _cursorVisible && _enabled) {
204 // Use Raylib's MeasureText for accurate positioning
205 int textWidth = 0;
206 if (!_text.empty()) {
207 if (_passwordMode) {
208 // Measure masked text width
209 std::string maskedText(_text.length(), '*');
210 textWidth = MeasureText(maskedText.c_str(), _textSize);
211 } else {
212 textWidth = MeasureText(_text.c_str(), _textSize);
213 }
214 }
215 float cursorX = textX + static_cast<float>(textWidth);
216 float cursorY = textY;
217 float cursorHeight = static_cast<float>(_textSize);
218
219 _graphics.DrawRectangle(static_cast<int>(cursorX), static_cast<int>(cursorY), 2,
220 static_cast<int>(cursorHeight), _textColor);
221 }
222 }
223
224 void RaylibTextInput::SetOnTextChanged(std::function<void(const std::string &)> callback) {
225 _onTextChanged = std::move(callback);
226 }
227
228 void RaylibTextInput::SetPosition(float x, float y) {
229 _x = x;
230 _y = y;
231 }
232
233 void RaylibTextInput::GetPosition(float &x, float &y) const {
234 x = _x;
235 y = _y;
236 }
237
238 void RaylibTextInput::SetSize(float width, float height) {
239 _width = width;
240 _height = height;
241 }
242
243 void RaylibTextInput::GetSize(float &width, float &height) const {
244 width = _width;
245 height = _height;
246 }
247
248 void RaylibTextInput::SetBackgroundColor(unsigned int color) {
249 _backgroundColor = color;
250 }
251
252 void RaylibTextInput::SetBorderColor(unsigned int color) {
253 _borderColor = color;
254 }
255
256 void RaylibTextInput::SetActiveBorderColor(unsigned int color) {
257 _activeBorderColor = color;
258 }
259
260 void RaylibTextInput::SetTextColor(unsigned int color) {
261 _textColor = color;
262 }
263
264 void RaylibTextInput::SetPlaceholderColor(unsigned int color) {
265 _placeholderColor = color;
266 }
267
269 _textSize = size;
270 }
271
272 void RaylibTextInput::SetPlaceholder(const std::string &placeholder) {
273 _placeholder = placeholder;
274 }
275
276 void RaylibTextInput::SetMaxLength(size_t maxLength) {
277 _maxLength = maxLength;
278 // Truncate existing text if needed
279 if (_maxLength > 0 && _text.length() > _maxLength) {
280 _text = _text.substr(0, _maxLength);
281 }
282 }
283
284 void RaylibTextInput::SetValidationRegex(const std::string &regexPattern) {
285 _regexPattern = regexPattern;
286 if (!regexPattern.empty()) {
287 try {
288 _validationRegex = std::make_unique<std::regex>(regexPattern);
289 } catch (const std::regex_error &) {
290 _validationRegex.reset();
291 }
292 } else {
293 _validationRegex.reset();
294 }
295 }
296
297 const std::string &RaylibTextInput::GetText() const {
298 return _text;
299 }
300
301 void RaylibTextInput::SetText(const std::string &text) {
302 _text = text;
303 // Apply max length constraint
304 if (_maxLength > 0 && _text.length() > _maxLength) {
305 _text = _text.substr(0, _maxLength);
306 }
307 }
308
310 _text.clear();
311 }
312
314 return _focused;
315 }
316
317 void RaylibTextInput::SetFocused(bool focused) {
318 _focused = focused;
319 if (_focused) {
320 _cursorVisible = true;
321 _cursorBlinkTimer = 0.0F;
322 }
323 }
324
325 void RaylibTextInput::SetFont(int fontHandle) {
326 _fontHandle = fontHandle;
327 }
328
330 _align = align;
331 }
332
334 return _align;
335 }
336
338 int screenWidth = _graphics.GetScreenWidth();
339 int screenHeight = _graphics.GetScreenHeight();
340
341 switch (_align) {
343 _x = (static_cast<float>(screenWidth) - _width) / 2.0F;
344 break;
346 _y = (static_cast<float>(screenHeight) - _height) / 2.0F;
347 break;
349 _x = (static_cast<float>(screenWidth) - _width) / 2.0F;
350 _y = (static_cast<float>(screenHeight) - _height) / 2.0F;
351 break;
352 case Align::NONE:
353 default:
354 break;
355 }
356 }
357
358 void RaylibTextInput::SetEnabled(bool enabled) {
359 _enabled = enabled;
360 if (!_enabled) {
361 _focused = false;
362 }
363 }
364
366 return _enabled;
367 }
368
369 void RaylibTextInput::SetPasswordMode(bool passwordMode) {
370 _passwordMode = passwordMode;
371 }
372
374 return _passwordMode;
375 }
376
378 int mouseX = _graphics.GetMouseX();
379 int mouseY = _graphics.GetMouseY();
380
381 return static_cast<float>(mouseX) >= _x && static_cast<float>(mouseX) <= _x + _width &&
382 static_cast<float>(mouseY) >= _y && static_cast<float>(mouseY) <= _y + _height;
383 }
384
385 bool RaylibTextInput::IsCharValid(char c) const {
386 if (!_validationRegex) {
387 return true; // No validation = all chars allowed
388 }
389
390 std::string singleChar(1, c);
391 return std::regex_match(singleChar, *_validationRegex);
392 }
393} // namespace UI
Abstract interface for graphics rendering operations.
Definition IGraphics.hpp:32
virtual bool IsKeyPressed(int key) const =0
Check if a key was pressed (triggered once when key goes down)
virtual void DrawText(int fontHandle, const char *text, int x, int y, int fontSize, unsigned int color)=0
Draw text using a loaded font.
virtual int GetMouseX() const =0
Get the current X position of the mouse cursor.
virtual int GetScreenHeight() const =0
Get the screen height (same as window height)
virtual int GetMouseY() const =0
Get the current Y position of the mouse cursor.
virtual int GetCharPressed() const =0
Get the next character from the keyboard input queue.
virtual bool IsMouseButtonPressed(int button) const =0
Check if a mouse button was pressed (triggered once when button goes down)
virtual void DrawRectangleLines(int x, int y, int width, int height, unsigned int color)=0
Draw a rectangle outline (alias for DrawRect)
virtual float GetDeltaTime() const =0
Get the time elapsed for the last frame.
virtual bool IsKeyDown(int key) const =0
Check if a key is currently being held down.
virtual int GetScreenWidth() const =0
Get the screen width (same as window width)
virtual void DrawRectangle(int x, int y, int width, int height, unsigned int color)=0
Draw a filled rectangle (alias for DrawRectFilled)
void GetSize(float &width, float &height) const override
Get the current size of the text input.
const std::string & GetText() const override
Get the current text content.
void GetPosition(float &x, float &y) const override
Get the current top-left position of the text input.
bool IsPasswordMode() const override
Check if password mode is enabled.
RaylibTextInput(Graphics::IGraphics &graphics)
Construct a new RaylibTextInput.
void SetTextSize(int size) override
Set the text size in pixels.
void SetFocused(bool focused) override
Set focus state programmatically.
static constexpr float BACKSPACE_REPEAT_DELAY
static constexpr float CURSOR_BLINK_RATE
void HandleBackspace()
Handle backspace key with repeat support.
void ResetCursor()
Show cursor and reset blink timer.
bool TryAddCharacter(char c)
Add a character to the text if valid.
void SetOnTextChanged(std::function< void(const std::string &)> callback) override
Set callback invoked when text changes.
bool IsEnabled() const override
Check if the text input is enabled.
unsigned int _activeBorderColor
void SetValidationRegex(const std::string &regexPattern) override
Set a regex pattern to restrict allowed characters.
bool IsMouseOver() const
Check whether the mouse cursor is currently over the input rectangle.
unsigned int _backgroundColor
void SetActiveBorderColor(unsigned int color) override
Set the border color when the input is focused/active.
void SetAlign(Align align) override
Set alignment mode relative to the window.
bool IsFocused() const override
Check if the text input is currently focused/active.
void SetBorderColor(unsigned int color) override
Set the border color when the input is not focused.
void SetText(const std::string &text) override
Set the text content programmatically.
void HandleFocusClick()
Handle mouse click to set focus state.
void SetTextColor(unsigned int color) override
Set the text color.
void ApplyAlignment() override
Apply the current alignment mode to the position.
void SetPasswordMode(bool passwordMode) override
Enable or disable password mode (masks characters with asterisks).
void Clear() override
Clear the text content.
void SetFont(int fontHandle) override
Set the font handle for rendering text.
Graphics::IGraphics & _graphics
void SetPlaceholderColor(unsigned int color) override
Set the placeholder text color (when input is empty).
void SetPosition(float x, float y) override
Set the top-left position of the text input.
std::function< void(const std::string &)> _onTextChanged
void Render() override
Render the text input box.
void SetSize(float width, float height) override
Set the text input size.
void SetBackgroundColor(unsigned int color) override
Set the background color of the text input box.
void SetEnabled(bool enabled) override
Enable or disable the text input.
void HandlePaste()
Handle paste operation (Ctrl+V or Cmd+V).
Align GetAlign() const override
Get the current alignment mode.
void UpdateCursorBlink()
Update cursor blink animation.
void HandleCharacterInput()
Handle regular character input.
void SetMaxLength(size_t maxLength) override
Set the maximum number of characters allowed.
void Update() override
Update the text input internal state (focus, cursor, input).
bool IsCharValid(char c) const
Validate a character against the current regex pattern.
static constexpr float BACKSPACE_REPEAT_RATE
void SetPlaceholder(const std::string &placeholder) override
Set the placeholder text displayed when the input is empty.
static constexpr float TEXT_PADDING
std::unique_ptr< std::regex > _validationRegex
unsigned int _placeholderColor
Definition IButton.hpp:13
Align
Alignment modes relative to the current window.
Definition IButton.hpp:33
@ CENTER_VERTICAL
Center on the Y axis.
@ CENTER_BOTH
Center on both axes.
@ CENTER_HORIZONTAL
Center on the X axis.
@ NONE
No alignment.