UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ui_node_textentry.cpp
Go to the documentation of this file.
1 
12 /*
13 Copyright (C) 2002-2020 UFO: Alien Invasion.
14 
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version 2
18 of the License, or (at your option) any later version.
19 
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 
24 See the GNU General Public License for more details.
25 
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, 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"
43 #include "ui_node_abstractnode.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 
62 static const char CURSOR_ON = '|';
63 static const char CURSOR_OFF = ' ';
64 static 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 */
71 static cvar_t* editedCvar = nullptr;
72 static bool isAborted = false;
73 
77 void UI_TextEntry_SetBackgroundByName (uiNode_t* node, const char* name) {
78  uiSprite_t* sprite = UI_GetSpriteByName(name);
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) {
93  UI_ExecuteEventActions(node, node->onChange);
94  }
95  if (node->lua_onChange != LUA_NOREF) {
97  }
98 }
99 
104 {
105  assert(editedCvar);
106 
107  /* set the old cvar value */
108  Cvar_ForceSet(editedCvar->name, cvarValueBackup);
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 
124 void UI_TextEntryNodeFocus (uiNode_t* node, const uiCallContext_t* context)
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)) {
140  UI_RemoveFocus();
141  }
142 }
143 
147 void 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);
179  Q_strncpyz(cvarValueBackup, editedCvar->string, sizeof(cvarValueBackup));
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));
194  Cvar_ForceSet(editedCvar->name, buf);
196  UI_RemoveFocus();
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 
224 static 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 
262 bool 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;
272  UI_RemoveFocus();
273  return true;
274  /* validate the edition */
275  case K_ENTER:
276  case K_KP_ENTER:
278  UI_RemoveFocus();
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. */
309  UI_TextEntryNodeEdit(node, unicode);
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 */
412  UI_RegisterExtradataNodeProperty(behaviour, "onAbort", V_UI_ACTION, textEntryExtraData_t, onAbort);
413  /* Call it to force node edition */
414  UI_RegisterNodeMethod(behaviour, "edit", UI_TextEntryNodeFocus);
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 }
void onLoading(uiNode_t *node) override
Call before the script initialization of the node.
vec2_t size
Definition: ui_nodes.h:52
void UI_RemoveFocus(void)
Definition: ui_input.cpp:241
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
#define EXTRADATA(node)
LUA_EVENT lua_onChange
Definition: ui_nodes.h:162
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
const char * name
Definition: ui_behaviour.h:41
struct uiAction_s * onChange
Definition: ui_nodes.h:143
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
void UI_TextEntry_SetBackgroundByName(uiNode_t *node, const char *name)
set background sprite
char * UTF8_strncpyz(char *dest, const char *src, size_t limit)
UTF8 capable string copy function.
Definition: utf8.cpp:247
#define MAX_CVAR_EDITING_LENGTH
UINodePtr manager
Definition: ui_behaviour.h:43
void UI_RegisterTextEntryNode(uiBehaviour_t *behaviour)
bool UI_ExecuteLuaEventScript(uiNode_t *node, LUA_EVENT event)
Executes a lua event handler.
Definition: ui_lua.cpp:71
#define V_UI_SPRITEREF
Definition: ui_parse.h:56
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
void onFocusGained(uiNode_t *node) override
Called when the node got the focus.
static void UI_TextEntryNodeEdit(uiNode_t *node, unsigned int unicode)
edit the current cvar with a char
struct uiAction_s * onClick
Definition: ui_nodes.h:135
unsigned short unicode
Definition: cl_input.cpp:69
static cvar_t * editedCvar
voidpf void * buf
Definition: ioapi.h:42
extradata for the textentry, to custom draw and behaviour
#define UI_RegisterExtradataNodeProperty(BEHAVIOUR, NAME, TYPE, EXTRADATATYPE, ATTRIBUTE)
Initialize a property from extradata of node.
Definition: ui_behaviour.h:109
void R_Color(const vec4_t rgba)
Change the color to given value.
Definition: r_state.cpp:1011
void draw(uiNode_t *node) override
static const char CURSOR_OFF
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
unsigned int key
Definition: cl_input.cpp:68
int UTF8_delete_char_at(char *s, int pos)
Delete a whole (possibly multibyte) character from a string.
Definition: utf8.cpp:35
align_t
We need this here for checking the boundaries from script values.
Definition: scripts.h:90
bool state
Definition: ui_nodes.h:106
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
GLsizei size
Definition: r_gl.h:152
#define EXTRADATA_TYPE
#define MAX_VAR
Definition: shared.h:36
#define Vector4Set(v, r, g, b, a)
Definition: vector.h:62
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
static const char HIDECHAR
bool UI_HasFocus(const uiNode_t *node)
check if a node got the focus
Definition: ui_input.cpp:230
vec4_t disabledColor
Definition: ui_nodes.h:103
SharedPtr< uiNode > UINodePtr
#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
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
int padding
Definition: ui_nodes.h:109
void * lua_SWIG_typeinfo
Definition: ui_behaviour.h:57
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition: shared.cpp:587
int UTF8_encoded_len(int c)
Definition: utf8.cpp:188
static void UI_TextEntryNodeValidateEdition(uiNode_t *node)
callback from the keyboard
#define UI_EXTRADATA(NODE, TYPE)
Definition: ui_nodes.h:185
void UI_ExecuteEventActions(uiNode_t *source, const uiAction_t *firstAction)
Definition: ui_actions.cpp:726
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
const char * UI_GetReferenceString(const uiNode_t *const node, const char *ref)
Definition: ui_parse.cpp:1406
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
void * UI_SWIG_TypeQuery(const char *name)
This function queries the SWIG type table for a type information structure. It is used in combination...
bool disabled
Definition: ui_nodes.h:102
void UI_TextEntryNodeUnFocus(uiNode_t *node, const uiCallContext_t *context)
force exit the edit mode of a textentry node
void UI_GetNodeAbsPos(const uiNode_t *node, vec2_t pos)
Returns the absolute position of a node.
Definition: ui_node.cpp:514
static const char CURSOR_ON
Contain the context of the calling of a function.
Definition: ui_actions.h:208
vec4_t selectedColor
Definition: ui_nodes.h:128
Definition: cl_keys.h:77
void UI_RequestFocus(uiNode_t *node)
request the focus for a node
Definition: ui_input.cpp:206
intptr_t extraDataSize
Definition: ui_behaviour.h:54
const char * UI_GetFontFromNode(const uiNode_t *const node)
Return the font for a specific node or default font.
Definition: ui_font.cpp:145
Definition: scripts.h:50
char * string
Definition: cvar.h:73
Definition: cl_keys.h:78
node behaviour, how a node work
Definition: ui_behaviour.h:39
void onFocusLost(uiNode_t *node) override
Called when the node lost the focus.
int contentAlign
Definition: ui_nodes.h:120
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
vec4_t color
Definition: ui_nodes.h:127
uiSpriteStatus_t
Definition: ui_sprite.h:32
static bool isAborted
bool onKeyPressed(uiNode_t *node, unsigned int key, unsigned short unicode) override
Called when we press a key when the node got the focus.
vec_t vec2_t[2]
Definition: ufotypes.h:38
Definition: scripts.h:52
Definition: cl_keys.h:44
cvar_t * Cvar_ForceSet(const char *varName, const char *value)
Will set the variable even if NOSET or LATCH.
Definition: cvar.cpp:604
void onLeftClick(uiNode_t *node, int x, int y) override
Called when the user click with the right mouse button.
#define V_UI_ACTION
Definition: ui_parse.h:54
LUA_EVENT lua_onClick
Definition: ui_nodes.h:148
static void UI_TextEntryNodeAbortEdition(uiNode_t *node)
callback from the keyboard
uiBox_t box
Definition: ui_nodes.h:96
static char cvarValueBackup[MAX_CVAR_EDITING_LENGTH]
uiSprite_t * UI_GetSpriteByName(const char *name)
Return an sprite by is name.
Definition: ui_sprite.cpp:115
char * name
Definition: cvar.h:72
const struct value_s * UI_RegisterNodeMethod(uiBehaviour_t *behaviour, const char *name, uiNodeMethod_t function)
Register a node method to a behaviour.
char * text
Definition: ui_nodes.h:121
void UI_TextEntryNodeFocus(uiNode_t *node, const uiCallContext_t *context)
force edition of a textentry node
int CL_Milliseconds(void)
Definition: cl_main.cpp:1208