UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ui_node_optiontree.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "../ui_main.h"
27 #include "../ui_parse.h"
28 #include "../ui_behaviour.h"
29 #include "../ui_actions.h"
30 #include "../ui_font.h"
31 #include "../ui_data.h"
32 #include "../ui_sprite.h"
33 #include "../ui_render.h"
34 #include "../ui_input.h"
35 #include "../ui_lua.h"
36 
37 #include "ui_node_abstractoption.h"
38 #include "ui_node_abstractnode.h"
39 #include "ui_node_optiontree.h"
40 #include "ui_node_option.h"
41 #include "ui_node_panel.h"
42 
43 #include "../../cl_language.h"
44 #include "../../input/cl_keys.h"
45 
46 #include "../../../common/scripts_lua.h"
47 
48 #define EXTRADATA_TYPE abstractOptionExtraData_t
49 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
50 
51 static const int COLLAPSEBUTTON_WIDTH = 20;
52 static const int DEPTH_WIDTH = 25;
56 
57 /* Used for drag&drop-like scrolling */
58 static int mouseScrollX;
59 static int mouseScrollY;
60 
66 {
67  const char* font;
68  int fontHeight;
69  bool updated;
70  int elements;
71 
72  font = UI_GetFontFromNode(node);
73  fontHeight = EXTRADATA(node).lineHeight;
74  if (fontHeight == 0)
75  fontHeight = UI_FontGetHeight(font);
76 
77  elements = (node->box.size[1] - node->padding - node->padding) / fontHeight;
78  updated = EXTRADATA(node).scrollY.set(-1, elements, EXTRADATA(node).count);
79  if (updated) {
80  if (EXTRADATA(node).onViewChange) {
81  UI_ExecuteEventActions(node, EXTRADATA(node).onViewChange);
82  }
83  else if (EXTRADATA(node).lua_onViewChange != LUA_NOREF) {
84  UI_ExecuteLuaEventScript (node, EXTRADATA(node).lua_onViewChange);
85  }
86  }
87 }
88 
91 
93 {
95  if (option)
96  EXTRADATA(node).count = UI_OptionUpdateCache(option);
97 }
98 
104 {
105  if (node->firstChild) {
107  assert(node->firstChild->behaviour == ui_optionBehaviour);
108  return node->firstChild;
109  } else {
110  const int v = UI_GetDataVersion(EXTRADATA(node).dataId);
111  uiNode_t* option = UI_GetOption(EXTRADATA(node).dataId);
112  if (v != EXTRADATA(node).versionId) {
113  EXTRADATA(node).versionId = v;
115  }
116  return option;
117  }
118 }
119 
121 {
122  uiNode_t* option;
123  const char* ref;
124  const char* font;
125  vec2_t pos;
126  int fontHeight;
127  int currentY;
128  int currentDecY = 0;
129  const float* textColor;
130  int count = 0;
131  uiOptionIterator_t iterator;
132 
133  if (!systemExpand)
134  systemExpand = UI_GetSpriteByName("icons/system_expand");
135  if (!systemCollapse)
136  systemCollapse = UI_GetSpriteByName("icons/system_collapse");
137 
139  if (ref == nullptr)
140  return;
141 
142  UI_GetNodeAbsPos(node, pos);
143 
144  if (EXTRADATA(node).background) {
145  UI_DrawSpriteInBox(false, EXTRADATA(node).background, SPRITE_STATUS_NORMAL, pos[0], pos[1], node->box.size[0], node->box.size[1]);
146  }
147 
148  font = UI_GetFontFromNode(node);
149  fontHeight = EXTRADATA(node).lineHeight;
150  currentY = pos[1] + node->padding;
151  if (fontHeight == 0)
152  fontHeight = UI_FontGetHeight(font);
153  else {
154  const int height = UI_FontGetHeight(font);
155  currentDecY = (fontHeight - height) / 2;
156  }
157 
158  /* skip option over current position */
159  option = UI_OptionTreeNodeGetFirstOption(node);
161  option = UI_InitOptionIteratorAtIndex(EXTRADATA(node).scrollY.viewPos, option, &iterator);
162 
163  /* draw all available options for this selectbox */
164  for (; option; option = UI_OptionIteratorNextOption(&iterator)) {
165  int decX;
166 
167  /* outside the node */
168  if (currentY + fontHeight > pos[1] + node->box.size[1] - node->padding) {
169  count++;
170  break;
171  }
172 
173  /* draw the hover effect */
174  if (OPTIONEXTRADATA(option).hovered)
175  UI_DrawFill(pos[0] + node->padding, currentY, node->box.size[0] - node->padding - node->padding, fontHeight, node->color);
176 
177  /* text color */
178  if (Q_streq(OPTIONEXTRADATA(option).value, ref)) {
179  textColor = node->selectedColor;
180  } else if (node->disabled || option->disabled) {
181  textColor = node->disabledColor;
182  } else if (option->color[3] == 0.0f) {
183  textColor = node->color;
184  } else {
185  textColor = option->color;
186  }
187 
188  /* print the option label */
189  decX = pos[0] + node->padding + iterator.depthPos * DEPTH_WIDTH;
190 
191  R_Color(nullptr);
192  if (option->firstChild) {
193  uiSprite_t* icon = OPTIONEXTRADATA(option).collapsed ? systemExpand : systemCollapse;
194  UI_DrawSpriteInBox(OPTIONEXTRADATA(option).flipIcon, icon, SPRITE_STATUS_NORMAL, decX, currentY, icon->size[0], fontHeight);
195  }
196 
197  decX += COLLAPSEBUTTON_WIDTH;
198 
199  if (OPTIONEXTRADATA(option).icon) {
201  if (option->disabled)
202  iconStatus = SPRITE_STATUS_DISABLED;
203  UI_DrawSpriteInBox(OPTIONEXTRADATA(option).flipIcon, OPTIONEXTRADATA(option).icon, iconStatus, decX, currentY,
204  OPTIONEXTRADATA(option).icon->size[0], fontHeight);
205  decX += OPTIONEXTRADATA(option).icon->size[0] + fontHeight / 4;
206  }
207 
208  const char* label = CL_Translate(OPTIONEXTRADATA(option).label);
209 
210  R_Color(textColor);
211  UI_DrawString(font, ALIGN_UL, decX, currentY + currentDecY,
212  pos[0], node->box.size[0] - node->padding - node->padding,
213  0, label, 0, 0, nullptr, false, LONGLINES_PRETTYCHOP);
214 
215  /* next entries' position */
216  currentY += fontHeight;
217  count++;
218  }
219  R_Color(nullptr);
220 }
221 
222 static uiNode_t* UI_OptionTreeNodeGetOptionAtPosition (uiNode_t* node, int x, int y, int* depth)
223 {
224  uiNode_t* option;
225  const char* font;
226  int fontHeight;
227  int count;
228  uiOptionIterator_t iterator;
229 
230  UI_NodeAbsoluteToRelativePos(node, &x, &y);
231 
232  font = UI_GetFontFromNode(node);
233  fontHeight = EXTRADATA(node).lineHeight;
234  if (fontHeight == 0)
235  fontHeight = UI_FontGetHeight(font);
236 
237  option = UI_OptionTreeNodeGetFirstOption(node);
238  count = EXTRADATA(node).scrollY.viewPos + (y - node->padding) / fontHeight;
239  option = UI_InitOptionIteratorAtIndex(count, option, &iterator);
240  *depth = iterator.depthPos;
241  return option;
242 }
243 
247 void uiOptionTreeNode::onLeftClick (uiNode_t* node, int x, int y)
248 {
249  uiNode_t* option;
250  int depth;
251 
252  if (UI_AbstractOption_GetCurrentValue(node) == nullptr)
253  return;
254 
255  /* select the right option */
256  option = UI_OptionTreeNodeGetOptionAtPosition(node, x, y, &depth);
257 
258  UI_NodeAbsoluteToRelativePos(node, &x, &y);
259 
260  /* extend/collapse*/
261  x -= depth * DEPTH_WIDTH;
262  if (x >= 0 && x < COLLAPSEBUTTON_WIDTH) {
263  if (option && option->firstChild) {
264  OPTIONEXTRADATA(option).collapsed = !OPTIONEXTRADATA(option).collapsed;
266  }
267  return;
268  }
269 
270  /* update the status */
271  if (option)
273 }
274 
278 bool uiOptionTreeNode::onScroll (uiNode_t* node, int deltaX, int deltaY)
279 {
280  bool down = deltaY > 0;
281  bool updated;
282  if (deltaY == 0)
283  return false;
284  updated = EXTRADATA(node).scrollY.move(EXTRADATA(node).scrollY.viewPos + (down ? 1 : -1));
285  if (updated) {
286  if (EXTRADATA(node).onViewChange) {
287  UI_ExecuteEventActions(node, EXTRADATA(node).onViewChange);
288  }
289  else if (EXTRADATA(node).lua_onViewChange != LUA_NOREF) {
290  UI_ExecuteLuaEventScript (node, EXTRADATA(node).lua_onViewChange);
291  }
292  }
293  /* @todo use super behaviour */
294  if (node->onWheelUp && !down) {
295  UI_ExecuteEventActions(node, node->onWheelUp);
296  updated = true;
297  }
298  if (node->onWheelDown && down) {
299  UI_ExecuteEventActions(node, node->onWheelDown);
300  updated = true;
301  }
302  if (node->onWheel) {
303  UI_ExecuteEventActions(node, node->onWheel);
304  updated = true;
305  }
306  return updated;
307 }
308 
313 {
314  Vector4Set(node->color, 1, 1, 1, 1);
315  Vector4Set(node->disabledColor, 0.5, 0.5, 0.5, 1.0);
316  EXTRADATA(node).versionId = -1;
317  node->padding = 3;
318 }
319 
321 {
322 }
323 
324 void UI_OptionTree_SelectValue (uiNode_t* node, const char* value) {
325  /* is the option exists */
326  uiNode_t* firstOption = UI_OptionTreeNodeGetFirstOption(node);
327 
328  uiOptionIterator_t iterator;
329  UI_InitOptionIteratorAtIndex(0, firstOption, &iterator, false, true);
330  uiNode_t* option = UI_FindOptionByValue(&iterator, value);
331 
332  /* update the selection */
333  if (option) {
335  } else {
336  Com_Printf("UI_OptionTree_SelectValue: Option value \"%s\" not found\n", value);
337  return;
338  }
339 
340  /* expend parents */
341  for (int i = 0; i < iterator.depthPos; i++)
342  OPTIONEXTRADATA(iterator.depthCache[i]).collapsed = false;
345 
346  /* fix scroll bar */
347  firstOption = UI_OptionTreeNodeGetFirstOption(node);
348  UI_InitOptionIteratorAtIndex(0, firstOption, &iterator);
349  int pos = UI_FindOptionPosition(&iterator, option);
350  if (pos == -1)
351  return;
352 
353  bool updated;
354  updated = EXTRADATA(node).scrollY.move(pos);
355  if (updated) {
356  if (EXTRADATA(node).onViewChange) {
357  UI_ExecuteEventActions(node, EXTRADATA(node).onViewChange);
358  }
359  else if (EXTRADATA(node).lua_onViewChange != LUA_NOREF) {
360  UI_ExecuteLuaEventScript (node, EXTRADATA(node).lua_onViewChange);
361  }
362  }
363 }
364 
365 static void UI_OptionTreeSetSelectedValue (uiNode_t* node, const uiCallContext_t* context)
366 {
367  if (UI_GetParamNumber(context) != 1) {
368  Com_Printf("UI_OptionTreeSetSelectedValue: Invalide number of param\n");
369  return;
370  }
371 
372  const char* value = UI_GetParam(context, 1);
373 
374  UI_OptionTree_SelectValue(node, value);
375 }
376 
378 {
380  node->invalidated = false;
381 }
382 
387 void uiOptionTreeNode::onMouseDown (uiNode_t* node, int x, int y, int button)
388 {
389  if (!UI_GetMouseCapture() && button == K_MOUSE1 &&
390  EXTRADATA(node).scrollY.fullSize > EXTRADATA(node).scrollY.viewSize) {
391  UI_SetMouseCapture(node);
392  mouseScrollX = x;
393  mouseScrollY = y;
394  }
395 }
396 
397 void uiOptionTreeNode::onMouseUp (uiNode_t* node, int x, int y, int button)
398 {
399  if (UI_GetMouseCapture() == node) /* More checks can never hurt */
400  UI_MouseRelease();
401 }
402 
404 {
405  const int lineHeight = getCellHeight(node);
406  const int deltaY = (mouseScrollY - y) / lineHeight;
407  /* We're doing only vertical scroll, that's enough for the most instances */
408  if (deltaY != 0) {
409  bool updated;
410  updated = EXTRADATA(node).scrollY.moveDelta(deltaY);
411  if (updated) {
412  if (EXTRADATA(node).onViewChange) {
413  UI_ExecuteEventActions(node, EXTRADATA(node).onViewChange);
414  }
415  else if (EXTRADATA(node).lua_onViewChange != LUA_NOREF) {
416  UI_ExecuteLuaEventScript (node, EXTRADATA(node).lua_onViewChange);
417  }
418  }
419  /* @todo not accurate */
420  mouseScrollX = x;
421  mouseScrollY = y;
422  }
423  onMouseMove(node, x, y);
424 }
425 
432 {
433  int lineHeight = EXTRADATA(node).lineHeight;
434  if (lineHeight == 0)
435  lineHeight = UI_FontGetHeight(UI_GetFontFromNode(node));
436  return lineHeight;
437 }
438 
440 {
441  behaviour->name = "optiontree";
442  behaviour->extends = "abstractoption";
443  behaviour->manager = UINodePtr(new uiOptionTreeNode());
444  behaviour->drawItselfChild = true;
445  behaviour->lua_SWIG_typeinfo = UI_SWIG_TypeQuery("uiOptionTreeNode_t *");
446 
447  /* Call this to toggle the node status. */
448  UI_RegisterNodeMethod(behaviour, "setselectedvalue", UI_OptionTreeSetSelectedValue);
449  /* Sprite used to display the background */
450  UI_RegisterExtradataNodeProperty(behaviour, "background", V_UI_SPRITEREF, EXTRADATA_TYPE, background);
451 }
struct uiAction_s * onWheelDown
Definition: ui_nodes.h:142
bool invalidated
Definition: ui_nodes.h:104
vec2_t size
Definition: ui_nodes.h:52
vec2_t size
Definition: ui_sprite.h:43
bool onScroll(uiNode_t *node, int deltaX, int deltaY) override
Auto scroll the list.
void onLoading(uiNode_t *node) override
Called before loading. Used to set default attribute values.
const char * name
Definition: ui_behaviour.h:41
uiNode_t * UI_FindOptionByValue(uiOptionIterator_t *iterator, const char *value)
Find an option (and all his parents) by is value.
Definition: ui_data.cpp:468
static uiNode_t * UI_OptionTreeNodeGetOptionAtPosition(uiNode_t *node, int x, int y, int *depth)
const char * UI_AbstractOption_GetCurrentValue(uiNode_t *node)
uiBehaviour_t * behaviour
Definition: ui_nodes.h:83
UINodePtr manager
Definition: ui_behaviour.h:43
void onMouseDown(uiNode_t *node, int x, int y, int button) override
Track mouse down/up events to implement drag&drop-like scrolling, for touchscreen devices...
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
static int mouseScrollX
virtual void onMouseMove(uiNode_t *node, int x, int y)
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
uiNode_t * UI_GetOption(int dataId)
Definition: ui_data.cpp:324
int getCellHeight(uiNode_t *node) override
Return size of the cell, which is the size (in virtual "pixel") which represent 1 in the scroll value...
void UI_MouseRelease(void)
Release the captured node.
Definition: ui_input.cpp:526
int UI_GetDataVersion(int textId)
Definition: ui_data.cpp:159
#define UI_RegisterExtradataNodeProperty(BEHAVIOUR, NAME, TYPE, EXTRADATATYPE, ATTRIBUTE)
Initialize a property from extradata of node.
Definition: ui_behaviour.h:109
void onLoaded(uiNode_t *node) override
#define EXTRADATA_TYPE
void R_Color(const vec4_t rgba)
Change the color to given value.
Definition: r_state.cpp:1011
uiNode_t * depthCache[MAX_DEPTH_OPTIONITERATORCACHE]
Definition: ui_data.h:61
uiNode_t * UI_OptionIteratorNextOption(uiOptionIterator_t *iterator)
Find the next element from the iterator Iterator skipCollapsed and skipInvisible attribute can contro...
Definition: ui_data.cpp:430
static void UI_OptionTreeSetSelectedValue(uiNode_t *node, const uiCallContext_t *context)
void draw(uiNode_t *node) override
#define OPTIONEXTRADATA(node)
static const int COLLAPSEBUTTON_WIDTH
static uiSprite_t * systemCollapse
static void UI_OptionTreeNodeUpdateCache(uiNode_t *node)
void onLeftClick(uiNode_t *node, int x, int y) override
Handles selectboxes clicks.
#define Vector4Set(v, r, g, b, a)
Definition: vector.h:62
struct uiAction_s * onWheelUp
Definition: ui_nodes.h:141
vec4_t disabledColor
Definition: ui_nodes.h:103
SharedPtr< uiNode > UINodePtr
int UI_FindOptionPosition(uiOptionIterator_t *iterator, const uiNode_t *option)
Find an option position from an option iterator.
Definition: ui_data.cpp:485
int padding
Definition: ui_nodes.h:109
void * lua_SWIG_typeinfo
Definition: ui_behaviour.h:57
void UI_SetMouseCapture(uiNode_t *node)
Captured the mouse into a node.
Definition: ui_input.cpp:516
bool drawItselfChild
Definition: ui_behaviour.h:50
void UI_ExecuteEventActions(uiNode_t *source, const uiAction_t *firstAction)
Definition: ui_actions.cpp:726
void UI_AbstractOption_SetCurrentValue(uiNode_t *node, const char *value)
int UI_OptionUpdateCache(uiNode_t *option)
update option cache about child, according to collapse and visible status
uiNode_t * UI_GetMouseCapture(void)
Return the captured node.
Definition: ui_input.cpp:508
void UI_OptionTree_SelectValue(uiNode_t *node, const char *value)
void onMouseUp(uiNode_t *node, int x, int y, int button) override
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
uiNode_t * UI_InitOptionIteratorAtIndex(int index, uiNode_t *option, uiOptionIterator_t *iterator)
Init an option iterator at an index.
Definition: ui_data.cpp:394
void UI_GetNodeAbsPos(const uiNode_t *node, vec2_t pos)
Returns the absolute position of a node.
Definition: ui_node.cpp:514
Contain the context of the calling of a function.
Definition: ui_actions.h:208
QGL_EXTERN GLuint count
Definition: r_gl.h:99
static uiSprite_t * systemExpand
int UI_GetParamNumber(const uiCallContext_t *context)
Definition: ui_actions.cpp:166
static int mouseScrollY
vec4_t selectedColor
Definition: ui_nodes.h:128
static const int DEPTH_WIDTH
const char * UI_GetParam(const uiCallContext_t *context, int paramID)
Definition: ui_actions.cpp:179
QGL_EXTERN GLint i
Definition: r_gl.h:113
const char * CL_Translate(const char *t)
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 doLayout(uiNode_t *node) override
Call to update the node layout. This common code revalidates the node tree.
void UI_NodeAbsoluteToRelativePos(const uiNode_t *node, int *x, int *y)
Update an absolute position to a relative one.
Definition: ui_node.cpp:592
node behaviour, how a node work
Definition: ui_behaviour.h:39
vec4_t color
Definition: ui_nodes.h:127
struct uiAction_s * onWheel
Definition: ui_nodes.h:138
#define EXTRADATA(node)
const char * extends
Definition: ui_behaviour.h:42
uiSpriteStatus_t
Definition: ui_sprite.h:32
int UI_FontGetHeight(const char *fontID)
Definition: ui_font.cpp:166
vec_t vec2_t[2]
Definition: ufotypes.h:38
int UI_DrawString(const char *fontID, align_t align, int x, int y, int absX, int maxWidth, int lineHeight, const char *c, int boxHeight, int scrollPos, int *curLine, bool increaseLine, longlines_t method)
Definition: ui_render.cpp:371
uiNode_t * firstChild
Definition: ui_nodes.h:89
#define Q_streq(a, b)
Definition: shared.h:136
static uiNode_t * UI_OptionTreeNodeGetFirstOption(uiNode_t *node)
Return the first option of the node.
uiBox_t box
Definition: ui_nodes.h:96
void UI_DrawFill(int x, int y, int w, int h, const vec4_t color)
Fills a box of pixels with a single color.
Definition: ui_render.cpp:37
uiSprite_t * UI_GetSpriteByName(const char *name)
Return an sprite by is name.
Definition: ui_sprite.cpp:115
const uiBehaviour_t * ui_optionBehaviour
void UI_RegisterOptionTreeNode(uiBehaviour_t *behaviour)
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
void onCapturedMouseMove(uiNode_t *node, int x, int y) override
int down
Definition: cl_input.cpp:70
const struct value_s * UI_RegisterNodeMethod(uiBehaviour_t *behaviour, const char *name, uiNodeMethod_t function)
Register a node method to a behaviour.
static void UI_OptionTreeNodeUpdateScroll(uiNode_t *node)
Update the scroll according to the number of items and the size of the node.