UFO: Alien Invasion
ui_node_textentry.cpp
Go to the documentation of this file.
1
12/*
13Copyright (C) 2002-2022 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"
47#include "../../../shared/utf8.h"
48
49#include "../../../common/scripts_lua.h"
50
51#if SDL_VERSION_ATLEAST(2,0,0)
52#include <SDL.h>
53#else
54#ifdef ANDROID
55#include <SDL/SDL_screenkeyboard.h>
56#endif
57#endif
58
59#define EXTRADATA_TYPE textEntryExtraData_t
60#define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
61
62static const char CURSOR_ON = '|';
63static const char CURSOR_OFF = ' ';
64static const char HIDECHAR = '*';
66/* limit the input for cvar editing (base name, save slots and so on) */
67#define MAX_CVAR_EDITING_LENGTH 256 /* MAXCMDLINE */
68
69/* global data */
71static cvar_t* editedCvar = nullptr;
72static bool isAborted = false;
73
79 UI_EXTRADATA(node, textEntryExtraData_s).background = sprite;
80}
81
86{
87 /* invalidate cache */
88 editedCvar = nullptr;
89 cvarValueBackup[0] = '\0';
90
91 /* fire change event */
92 if (node->onChange) {
94 }
95 if (node->lua_onChange != LUA_NOREF) {
97 }
98}
99
104{
105 assert(editedCvar);
106
107 /* set the old cvar value */
109
110 /* invalidate cache */
111 editedCvar = nullptr;
112 cvarValueBackup[0] = '\0';
113
114 /* fire abort event */
115 if (EXTRADATA(node).onAbort) {
116 UI_ExecuteEventActions(node, EXTRADATA(node).onAbort);
117 }
118}
119
125{
126 /* remove the focus to show changes */
127 if (!UI_HasFocus(node)) {
128 UI_RequestFocus(node);
129 }
130}
131
137{
138 /* remove the focus to show changes */
139 if (UI_HasFocus(node)) {
141 }
142}
143
147void uiTextEntryNode::onLeftClick (uiNode_t* node, int x, int y)
148{
149 if (node->disabled)
150 return;
151
152 /* no cvar */
153 if (!node->text)
154 return;
155 if (!Q_strstart(node->text, "*cvar:"))
156 return;
157
158 if (!UI_HasFocus(node)) {
159 if (node->onClick) {
160 UI_ExecuteEventActions(node, node->onClick);
161 }
162 if (node->lua_onClick != LUA_NOREF) {
163 UI_ExecuteLuaEventScript_XY(node, node->lua_onClick, x, y);
164 }
165 UI_RequestFocus(node);
166 }
167}
168
173{
174 assert(editedCvar == nullptr);
175 /* skip '*cvar ' */
176 const char* cvarRef = "*cvar:";
177 editedCvar = Cvar_Get(&((const char*)node->text)[strlen(cvarRef)]);
178 assert(editedCvar);
180 isAborted = false;
181 EXTRADATA(node).cursorPosition = UTF8_strlen(editedCvar->string);
182
183#if SDL_VERSION_ATLEAST(2,0,0)
184 SDL_StartTextInput();
185 vec2_t pos;
186 UI_GetNodeAbsPos(node, pos);
187 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])};
188 SDL_SetTextInputRect(&r);
189#else
190#ifdef ANDROID
192 Q_strncpyz(buf, editedCvar->string, sizeof(buf));
193 SDL_ANDROID_GetScreenKeyboardTextInput(buf, sizeof(buf));
197#endif
198#endif
199}
200
205{
206 /* already aborted/changed with the keyboard */
207 if (editedCvar == nullptr)
208 return;
209
210 /* release the keyboard */
211 if (isAborted || EXTRADATA(node).clickOutAbort) {
213 } else {
215 }
216#if SDL_VERSION_ATLEAST(2,0,0)
217 SDL_StopTextInput();
218#endif
219}
220
224static void UI_TextEntryNodeEdit (uiNode_t* node, unsigned int unicode)
225{
226 char buffer[MAX_CVAR_EDITING_LENGTH];
227
228 /* copy the cvar */
229 Q_strncpyz(buffer, editedCvar->string, sizeof(buffer));
230
231 /* compute result */
232 if (unicode == K_BACKSPACE) {
233 if (EXTRADATA(node).cursorPosition > 0){
234 UTF8_delete_char_at(buffer, EXTRADATA(node).cursorPosition - 1);
235 EXTRADATA(node).cursorPosition--;
236 }
237 } else if (unicode == K_DEL) {
238 if (EXTRADATA(node).cursorPosition < UTF8_strlen(editedCvar->string)){
239 UTF8_delete_char_at(buffer, EXTRADATA(node).cursorPosition);
240 }
241 } else {
242 int length = strlen(buffer);
243 int charLength = UTF8_encoded_len(unicode);
244
245 /* is buffer full? */
246 if (length + charLength >= sizeof(buffer))
247 return;
248
249 int insertedLength = UTF8_insert_char_at(buffer, sizeof(buffer), EXTRADATA(node).cursorPosition, unicode);
250 if (insertedLength > 0)
251 EXTRADATA(node).cursorPosition++;
252 }
253
254 /* update the cvar */
255 Cvar_ForceSet(editedCvar->name, buffer);
256}
257
262bool uiTextEntryNode::onKeyPressed (uiNode_t* node, unsigned int key, unsigned short unicode)
263{
264 switch (key) {
265 /* remove the last char. */
266 case K_BACKSPACE:
268 return true;
269 /* cancel the edition */
270 case K_ESCAPE:
271 isAborted = true;
273 return true;
274 /* validate the edition */
275 case K_ENTER:
276 case K_KP_ENTER:
279 return true;
280 case K_LEFTARROW:
281 case K_KP_LEFTARROW:
282 if (EXTRADATA(node).cursorPosition > 0)
283 EXTRADATA(node).cursorPosition--;
284 return true;
285 case K_RIGHTARROW:
286 case K_KP_RIGHTARROW:
287 if (EXTRADATA(node).cursorPosition < UTF8_strlen(editedCvar->string))
288 EXTRADATA(node).cursorPosition++;
289 return true;
290 case K_HOME:
291 case K_KP_HOME:
292 EXTRADATA(node).cursorPosition = 0;
293 return true;
294 case K_END:
295 case K_KP_END:
296 EXTRADATA(node).cursorPosition = UTF8_strlen(editedCvar->string);
297 return true;
298 case K_DEL:
299 case K_KP_DEL:
301 return true;
302 }
303
304 /* non printable */
305 if (unicode < 32 || (unicode >= 127 && unicode < 192))
306 return false;
307
308 /* add a char. */
310 return true;
311}
312
314{
315 const float* textColor;
316 vec2_t pos;
317 const char* font = UI_GetFontFromNode(node);
319
320 if (node->disabled) {
321 textColor = node->disabledColor;
322 iconStatus = SPRITE_STATUS_DISABLED;
323 } else if (node->state) {
324 textColor = node->color;
325 iconStatus = SPRITE_STATUS_HOVER;
326 } else {
327 textColor = node->color;
328 }
329 if (UI_HasFocus(node)) {
330 textColor = node->selectedColor;
331 }
332
333 UI_GetNodeAbsPos(node, pos);
334
335 if (EXTRADATA(node).background) {
336 UI_DrawSpriteInBox(false, EXTRADATA(node).background, iconStatus, pos[0], pos[1], node->box.size[0], node->box.size[1]);
337 }
338
339 if (char const* const text = UI_GetReferenceString(node, node->text)) {
340 char buf[MAX_VAR];
341 if (EXTRADATA(node).isPassword) {
342 size_t size = UTF8_strlen(text);
343
344 if (size > MAX_VAR - 2)
345 size = MAX_VAR - 2;
346
347 memset(buf, HIDECHAR, size);
348 buf[size] = '\0';
349 } else {
350 /* leave one byte empty for the text-based cursor */
351 UTF8_strncpyz(buf, text, sizeof(buf) - 1);
352 }
353
355 if (UI_HasFocus(node)) {
356 if (CL_Milliseconds() % 1000 < 500) {
357 UTF8_insert_char_at(buf, sizeof(buf), EXTRADATA(node).cursorPosition, (int)CURSOR_ON);
358 } else {
359 UTF8_insert_char_at(buf, sizeof(buf), EXTRADATA(node).cursorPosition, (int)CURSOR_OFF);
360 }
361 }
362
363 if (*buf != '\0') {
364 R_Color(textColor);
366 pos[0] + node->padding, pos[1] + node->padding,
367 node->box.size[0] - node->padding - node->padding, node->box.size[1] - node->padding - node->padding,
368 buf);
369 R_Color(nullptr);
370 }
371 }
372}
373
378{
379 node->padding = 8;
380 node->contentAlign = ALIGN_CL;
381 Vector4Set(node->color, 1, 1, 1, 1);
382 Vector4Set(node->selectedColor, 1, 1, 1, 1);
383 Vector4Set(node->disabledColor, 0.5, 0.5, 0.5, 1.0);
384}
385
387{
388 behaviour->name = "textentry";
389 behaviour->manager = UINodePtr(new uiTextEntryNode());
390 behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
391 behaviour->lua_SWIG_typeinfo = UI_SWIG_TypeQuery("uiTextEntryNode_t *");
392
393 /* Call back event called when we click on the node. If the click select the node,
394 * it called before we start the cvar edition.
395 */
396 UI_RegisterOveridedNodeProperty(behaviour, "onClick");
397
398 /* Call back event (like click...) fired when the text is changed, after
399 * validation. An abort of the edition dont fire this event.
400 */
401 UI_RegisterOveridedNodeProperty(behaviour, "onChange");
402
403 /* Custom the draw behaviour by hiding each character of the text with a star (''*''). */
404 UI_RegisterExtradataNodeProperty(behaviour, "isPassword", V_BOOL, textEntryExtraData_t, isPassword);
405 /* ustom the mouse event behaviour. When we are editing the text, if we click out of the node, the edition is aborted. Changes on
406 * the text are canceled, and no change event are fired.
407 */
408 UI_RegisterExtradataNodeProperty(behaviour, "clickOutAbort", V_BOOL, textEntryExtraData_t, clickOutAbort);
409 /* Cursor position (offset of next UTF-8 char to the right) */
410 UI_RegisterExtradataNodeProperty(behaviour, "cursorPosition", V_INT, textEntryExtraData_t, cursorPosition);
411 /* Call it when we abort the edition */
413 /* Call it to force node edition */
415 /* Sprite used to display the background */
416 UI_RegisterExtradataNodeProperty(behaviour, "background", V_UI_SPRITEREF, EXTRADATA_TYPE, background);
417 /* Call it to force exit of node edition */
419}
unsigned int key
Definition: cl_input.cpp:68
unsigned short unicode
Definition: cl_input.cpp:69
@ 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.
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
#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
char * string
Definition: cvar.h:73
char * name
Definition: cvar.h:72
extradata for the textentry, to custom draw and behaviour
node behaviour, how a node work
Definition: ui_behaviour.h:39
const char * name
Definition: ui_behaviour.h:41
void * lua_SWIG_typeinfo
Definition: ui_behaviour.h:57
UINodePtr manager
Definition: ui_behaviour.h:43
intptr_t extraDataSize
Definition: ui_behaviour.h:54
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)
Definition: ui_actions.cpp:726
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...
Definition: ui_behaviour.h:117
#define UI_RegisterExtradataNodeProperty(BEHAVIOUR, NAME, TYPE, EXTRADATATYPE, ATTRIBUTE)
Initialize a property from extradata of node.
Definition: ui_behaviour.h:109
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
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:514
SharedPtr< uiNode > UINodePtr
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
#define EXTRADATA_TYPE
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)
Definition: ui_parse.cpp:1406
#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
Definition: ui_render.cpp:359
void UI_DrawSpriteInBox(bool flip, const uiSprite_t *sprite, uiSpriteStatus_t status, int posX, int posY, int sizeX, int sizeY)
Definition: ui_sprite.cpp:187
uiSprite_t * UI_GetSpriteByName(const char *name)
Return an sprite by is name.
Definition: ui_sprite.cpp:115
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