UFO: Alien Invasion
Loading...
Searching...
No Matches
ui_node_textentry.cpp
Go to the documentation of this file.
1
11
12/*
13Copyright (C) 2002-2025 UFO: Alien Invasion.
14
15This program is free software; you can redistribute it and/or
16modify it under the terms of the GNU General Public License
17as published by the Free Software Foundation; either version 2
18of the License, or (at your option) any later version.
19
20This program is distributed in the hope that it will be useful,
21but WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23
24See the GNU General Public License for more details.
25
26You should have received a copy of the GNU General Public License
27along with this program; if not, write to the Free Software
28Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
30*/
31
32#include "../ui_main.h"
33#include "../ui_nodes.h"
34#include "../ui_font.h"
35#include "../ui_parse.h"
36#include "../ui_behaviour.h"
37#include "../ui_input.h"
38#include "../ui_render.h"
39#include "../ui_sprite.h"
40#include "../ui_lua.h"
41
42#include "ui_node_textentry.h"
44#include "ui_node_panel.h"
45
46#include "../../client.h"
48
50
51#include <SDL.h>
52
53#define EXTRADATA_TYPE textEntryExtraData_t
54#define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
55
56static const char CURSOR_ON = '|';
57static const char CURSOR_OFF = ' ';
58static const char HIDECHAR = '*';
59
60/* limit the input for cvar editing (base name, save slots and so on) */
61#define MAX_CVAR_EDITING_LENGTH 256 /* MAXCMDLINE */
62
63/* global data */
65static cvar_t* editedCvar = nullptr;
66static bool isAborted = false;
67
73 UI_EXTRADATA(node, textEntryExtraData_s).background = sprite;
74}
75
80{
81 /* invalidate cache */
82 editedCvar = nullptr;
83 cvarValueBackup[0] = '\0';
84
85 /* fire change event */
86 if (node->onChange) {
88 }
89 if (node->lua_onChange != LUA_NOREF) {
91 }
92}
93
98{
99 assert(editedCvar);
100
101 /* set the old cvar value */
103
104 /* invalidate cache */
105 editedCvar = nullptr;
106 cvarValueBackup[0] = '\0';
107
108 /* fire abort event */
109 if (EXTRADATA(node).onAbort) {
110 UI_ExecuteEventActions(node, EXTRADATA(node).onAbort);
111 }
112}
113
119{
120 /* remove the focus to show changes */
121 if (!UI_HasFocus(node)) {
122 UI_RequestFocus(node);
123 }
124}
125
131{
132 /* remove the focus to show changes */
133 if (UI_HasFocus(node)) {
135 }
136}
137
141void uiTextEntryNode::onLeftClick (uiNode_t* node, int x, int y)
142{
143 if (node->disabled)
144 return;
145
146 /* no cvar */
147 if (!node->text)
148 return;
149 if (!Q_strstart(node->text, "*cvar:"))
150 return;
151
152 if (!UI_HasFocus(node)) {
153 if (node->onClick) {
154 UI_ExecuteEventActions(node, node->onClick);
155 }
156 if (node->lua_onClick != LUA_NOREF) {
157 UI_ExecuteLuaEventScript_XY(node, node->lua_onClick, x, y);
158 }
159 UI_RequestFocus(node);
160 }
161}
162
167{
168 assert(editedCvar == nullptr);
169 /* skip '*cvar ' */
170 const char* cvarRef = "*cvar:";
171 editedCvar = Cvar_Get(&((const char*)node->text)[strlen(cvarRef)]);
172 assert(editedCvar);
174 isAborted = false;
175 EXTRADATA(node).cursorPosition = UTF8_strlen(editedCvar->string);
176
177 SDL_StartTextInput();
178 vec2_t pos;
179 UI_GetNodeAbsPos(node, pos);
180 SDL_Rect r = {static_cast<int>(pos[0]), static_cast<int>(pos[1]), static_cast<int>(node->box.size[0]), static_cast<int>(node->box.size[1])};
181 SDL_SetTextInputRect(&r);
182}
183
188{
189 /* already aborted/changed with the keyboard */
190 if (editedCvar == nullptr)
191 return;
192
193 /* release the keyboard */
194 if (isAborted || EXTRADATA(node).clickOutAbort) {
196 } else {
198 }
199 SDL_StopTextInput();
200}
201
205static void UI_TextEntryNodeEdit (uiNode_t* node, unsigned int unicode)
206{
207 char buffer[MAX_CVAR_EDITING_LENGTH];
208
209 /* copy the cvar */
210 Q_strncpyz(buffer, editedCvar->string, sizeof(buffer));
211
212 /* compute result */
213 if (unicode == K_BACKSPACE) {
214 if (EXTRADATA(node).cursorPosition > 0){
215 UTF8_delete_char_at(buffer, EXTRADATA(node).cursorPosition - 1);
216 EXTRADATA(node).cursorPosition--;
217 }
218 } else if (unicode == K_DEL) {
219 if (EXTRADATA(node).cursorPosition < UTF8_strlen(editedCvar->string)){
220 UTF8_delete_char_at(buffer, EXTRADATA(node).cursorPosition);
221 }
222 } else {
223 int length = strlen(buffer);
224 int charLength = UTF8_encoded_len(unicode);
225
226 /* is buffer full? */
227 if (length + charLength >= sizeof(buffer))
228 return;
229
230 int insertedLength = UTF8_insert_char_at(buffer, sizeof(buffer), EXTRADATA(node).cursorPosition, unicode);
231 if (insertedLength > 0)
232 EXTRADATA(node).cursorPosition++;
233 }
234
235 /* update the cvar */
236 Cvar_ForceSet(editedCvar->name, buffer);
237}
238
243bool uiTextEntryNode::onKeyPressed (uiNode_t* node, unsigned int key, unsigned short unicode)
244{
245 switch (key) {
246 /* remove the last char. */
247 case K_BACKSPACE:
249 return true;
250 /* cancel the edition */
251 case K_ESCAPE:
252 isAborted = true;
254 return true;
255 /* validate the edition */
256 case K_ENTER:
257 case K_KP_ENTER:
260 return true;
261 case K_LEFTARROW:
262 case K_KP_LEFTARROW:
263 if (EXTRADATA(node).cursorPosition > 0)
264 EXTRADATA(node).cursorPosition--;
265 return true;
266 case K_RIGHTARROW:
267 case K_KP_RIGHTARROW:
268 if (EXTRADATA(node).cursorPosition < UTF8_strlen(editedCvar->string))
269 EXTRADATA(node).cursorPosition++;
270 return true;
271 case K_HOME:
272 case K_KP_HOME:
273 EXTRADATA(node).cursorPosition = 0;
274 return true;
275 case K_END:
276 case K_KP_END:
277 EXTRADATA(node).cursorPosition = UTF8_strlen(editedCvar->string);
278 return true;
279 case K_DEL:
280 case K_KP_DEL:
282 return true;
283 }
284
285 /* non printable */
286 if (unicode < 32 || (unicode >= 127 && unicode < 192))
287 return false;
288
289 /* add a char. */
291 return true;
292}
293
295{
296 const float* textColor;
297 vec2_t pos;
298 const char* font = UI_GetFontFromNode(node);
300
301 if (node->disabled) {
302 textColor = node->disabledColor;
303 iconStatus = SPRITE_STATUS_DISABLED;
304 } else if (node->state) {
305 textColor = node->color;
306 iconStatus = SPRITE_STATUS_HOVER;
307 } else {
308 textColor = node->color;
309 }
310 if (UI_HasFocus(node)) {
311 textColor = node->selectedColor;
312 }
313
314 UI_GetNodeAbsPos(node, pos);
315
316 if (EXTRADATA(node).background) {
317 UI_DrawSpriteInBox(false, EXTRADATA(node).background, iconStatus, pos[0], pos[1], node->box.size[0], node->box.size[1]);
318 }
319
320 if (char const* const text = UI_GetReferenceString(node, node->text)) {
321 char buf[MAX_VAR];
322 if (EXTRADATA(node).isPassword) {
323 size_t size = UTF8_strlen(text);
324
325 if (size > MAX_VAR - 2)
326 size = MAX_VAR - 2;
327
328 memset(buf, HIDECHAR, size);
329 buf[size] = '\0';
330 } else {
331 /* leave one byte empty for the text-based cursor */
332 UTF8_strncpyz(buf, text, sizeof(buf) - 1);
333 }
334
336 if (UI_HasFocus(node)) {
337 if (CL_Milliseconds() % 1000 < 500) {
338 UTF8_insert_char_at(buf, sizeof(buf), EXTRADATA(node).cursorPosition, (int)CURSOR_ON);
339 } else {
340 UTF8_insert_char_at(buf, sizeof(buf), EXTRADATA(node).cursorPosition, (int)CURSOR_OFF);
341 }
342 }
343
344 if (*buf != '\0') {
345 R_Color(textColor);
347 pos[0] + node->padding, pos[1] + node->padding,
348 node->box.size[0] - node->padding - node->padding, node->box.size[1] - node->padding - node->padding,
349 buf);
350 R_Color(nullptr);
351 }
352 }
353}
354
359{
360 node->padding = 8;
361 node->contentAlign = ALIGN_CL;
362 Vector4Set(node->color, 1, 1, 1, 1);
363 Vector4Set(node->selectedColor, 1, 1, 1, 1);
364 Vector4Set(node->disabledColor, 0.5, 0.5, 0.5, 1.0);
365}
366
368{
369 behaviour->name = "textentry";
370 behaviour->manager = UINodePtr(new uiTextEntryNode());
371 behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
372 behaviour->lua_SWIG_typeinfo = UI_SWIG_TypeQuery("uiTextEntryNode_t *");
373
374 /* Call back event called when we click on the node. If the click select the node,
375 * it called before we start the cvar edition.
376 */
377 UI_RegisterOveridedNodeProperty(behaviour, "onClick");
378
379 /* Call back event (like click...) fired when the text is changed, after
380 * validation. An abort of the edition dont fire this event.
381 */
382 UI_RegisterOveridedNodeProperty(behaviour, "onChange");
383
384 /* Custom the draw behaviour by hiding each character of the text with a star (''*''). */
385 UI_RegisterExtradataNodeProperty(behaviour, "isPassword", V_BOOL, textEntryExtraData_t, isPassword);
386 /* ustom the mouse event behaviour. When we are editing the text, if we click out of the node, the edition is aborted. Changes on
387 * the text are canceled, and no change event are fired.
388 */
389 UI_RegisterExtradataNodeProperty(behaviour, "clickOutAbort", V_BOOL, textEntryExtraData_t, clickOutAbort);
390 /* Cursor position (offset of next UTF-8 char to the right) */
391 UI_RegisterExtradataNodeProperty(behaviour, "cursorPosition", V_INT, textEntryExtraData_t, cursorPosition);
392 /* Call it when we abort the edition */
394 /* Call it to force node edition */
396 /* Sprite used to display the background */
397 UI_RegisterExtradataNodeProperty(behaviour, "background", V_UI_SPRITEREF, EXTRADATA_TYPE, background);
398 /* Call it to force exit of node edition */
400}
unsigned int key
Definition cl_input.cpp:64
unsigned short unicode
Definition cl_input.cpp:65
@ K_KP_RIGHTARROW
Definition cl_keys.h:60
@ K_DEL
Definition cl_keys.h:44
@ K_KP_DEL
Definition cl_keys.h:64
@ K_KP_END
Definition cl_keys.h:55
@ K_ENTER
Definition cl_keys.h:40
@ K_ESCAPE
Definition cl_keys.h:42
@ K_BACKSPACE
Definition cl_keys.h:38
@ K_END
Definition cl_keys.h:78
@ K_KP_ENTER
Definition cl_keys.h:69
@ K_KP_LEFTARROW
Definition cl_keys.h:58
@ K_HOME
Definition cl_keys.h:77
@ K_RIGHTARROW
Definition cl_keys.h:74
@ K_KP_HOME
Definition cl_keys.h:61
@ K_LEFTARROW
Definition cl_keys.h:75
int CL_Milliseconds(void)
Definition cl_main.cpp:1207
void R_Color(const vec4_t rgba)
Change the color to given value.
Definition r_state.cpp:1011
void draw(uiNode_t *node) override
void onLeftClick(uiNode_t *node, int x, int y) override
Called when the user click with the right mouse button.
bool onKeyPressed(uiNode_t *node, unsigned int key, unsigned short unicode) override
Called when we press a key when the node got the focus.
void onFocusLost(uiNode_t *node) override
Called when the node lost the focus.
void onFocusGained(uiNode_t *node) override
Called when the node got the focus.
void onLoading(uiNode_t *node) override
Call before the script initialization of the node.
Primary header for client.
cvar_t * Cvar_ForceSet(const char *varName, const char *value)
Will set the variable even if NOSET or LATCH.
Definition cvar.cpp:604
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition cvar.cpp:342
voidpf void uLong size
Definition ioapi.h:42
voidpf void * buf
Definition ioapi.h:42
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition r_gl.h:110
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
align_t
We need this here for checking the boundaries from script values.
Definition scripts.h:89
@ ALIGN_CL
Definition scripts.h:93
@ V_BOOL
Definition scripts.h:50
@ V_INT
Definition scripts.h:52
Header for lua script functions.
#define MAX_VAR
Definition shared.h:36
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition shared.cpp:587
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
extradata for the textentry, to custom draw and behaviour
node behaviour, how a node work
const char * name
void * lua_SWIG_typeinfo
UINodePtr manager
intptr_t extraDataSize
vec2_t size
Definition ui_nodes.h:52
Contain the context of the calling of a function.
Definition ui_actions.h:208
Atomic structure used to define most of the UI.
Definition ui_nodes.h:80
bool disabled
Definition ui_nodes.h:102
char * text
Definition ui_nodes.h:121
int contentAlign
Definition ui_nodes.h:120
vec4_t color
Definition ui_nodes.h:127
LUA_EVENT lua_onChange
Definition ui_nodes.h:162
struct uiAction_s * onChange
Definition ui_nodes.h:143
vec4_t selectedColor
Definition ui_nodes.h:128
LUA_EVENT lua_onClick
Definition ui_nodes.h:148
uiBox_t box
Definition ui_nodes.h:96
int padding
Definition ui_nodes.h:109
struct uiAction_s * onClick
Definition ui_nodes.h:135
vec4_t disabledColor
Definition ui_nodes.h:103
bool state
Definition ui_nodes.h:106
vec_t vec2_t[2]
Definition ufotypes.h:38
void UI_ExecuteEventActions(uiNode_t *source, const uiAction_t *firstAction)
const struct value_s * UI_RegisterNodeMethod(uiBehaviour_t *behaviour, const char *name, uiNodeMethod_t function)
Register a node method to a behaviour.
#define UI_RegisterOveridedNodeProperty(BEHAVIOUR, NAME)
Initialize a property which override an inherited property. It is yet only used for the documentation...
#define UI_RegisterExtradataNodeProperty(BEHAVIOUR, NAME, TYPE, EXTRADATATYPE, ATTRIBUTE)
Initialize a property from extradata of node.
const char * UI_GetFontFromNode(const uiNode_t *const node)
Return the font for a specific node or default font.
Definition ui_font.cpp:145
void UI_RequestFocus(uiNode_t *node)
request the focus for a node
Definition ui_input.cpp:206
void UI_RemoveFocus(void)
Definition ui_input.cpp:241
bool UI_HasFocus(const uiNode_t *node)
check if a node got the focus
Definition ui_input.cpp:230
bool UI_ExecuteLuaEventScript(uiNode_t *node, LUA_EVENT event)
Executes a lua event handler.
Definition ui_lua.cpp:71
bool UI_ExecuteLuaEventScript_XY(uiNode_t *node, LUA_EVENT event, int x, int y)
Executes a lua event handler with (x,y) argument.
Definition ui_lua.cpp:143
Basic lua initialization for the ui.
void * UI_SWIG_TypeQuery(const char *name)
This function queries the SWIG type table for a type information structure. It is used in combination...
void UI_GetNodeAbsPos(const uiNode_t *node, vec2_t pos)
Returns the absolute position of a node.
Definition ui_node.cpp:526
SharedPtr< uiNode > UINodePtr
#define EXTRADATA_TYPE
#define EXTRADATA(node)
static const char HIDECHAR
void UI_TextEntryNodeUnFocus(uiNode_t *node, const uiCallContext_t *context)
force exit the edit mode of a textentry node
static void UI_TextEntryNodeEdit(uiNode_t *node, unsigned int unicode)
edit the current cvar with a char
static bool isAborted
#define MAX_CVAR_EDITING_LENGTH
void UI_TextEntryNodeFocus(uiNode_t *node, const uiCallContext_t *context)
force edition of a textentry node
static void UI_TextEntryNodeValidateEdition(uiNode_t *node)
callback from the keyboard
static const char CURSOR_OFF
#define EXTRADATA(node)
static cvar_t * editedCvar
static const char CURSOR_ON
static void UI_TextEntryNodeAbortEdition(uiNode_t *node)
callback from the keyboard
static char cvarValueBackup[MAX_CVAR_EDITING_LENGTH]
void UI_RegisterTextEntryNode(uiBehaviour_t *behaviour)
void UI_TextEntry_SetBackgroundByName(uiNode_t *node, const char *name)
set background sprite
#define UI_EXTRADATA(NODE, TYPE)
Definition ui_nodes.h:185
const char * UI_GetReferenceString(const uiNode_t *const node, const char *ref)
#define V_UI_SPRITEREF
Definition ui_parse.h:56
#define V_UI_ACTION
Definition ui_parse.h:54
int UI_DrawStringInBox(const char *fontID, align_t align, int x, int y, int width, int height, const char *text, longlines_t method)
draw a line into a bounding box
void UI_DrawSpriteInBox(bool flip, const uiSprite_t *sprite, uiSpriteStatus_t status, int posX, int posY, int sizeX, int sizeY)
uiSprite_t * UI_GetSpriteByName(const char *name)
Return an sprite by is name.
uiSpriteStatus_t
Definition ui_sprite.h:32
@ SPRITE_STATUS_DISABLED
Definition ui_sprite.h:35
@ SPRITE_STATUS_HOVER
Definition ui_sprite.h:34
@ SPRITE_STATUS_NORMAL
Definition ui_sprite.h:33
int UTF8_delete_char_at(char *s, int pos)
Delete a whole (possibly multibyte) character from a string.
Definition utf8.cpp:35
int UTF8_insert_char_at(char *s, int n, int pos, int c)
Insert a (possibly multibyte) UTF-8 character into a string.
Definition utf8.cpp:63
size_t UTF8_strlen(const char *str)
Count the number of character (not the number of bytes) of a zero termination string.
Definition utf8.cpp:207
char * UTF8_strncpyz(char *dest, const char *src, size_t limit)
UTF8 capable string copy function.
Definition utf8.cpp:247
int UTF8_encoded_len(int c)
Definition utf8.cpp:188
#define Vector4Set(v, r, g, b, a)
Definition vector.h:62