UFO: Alien Invasion
Loading...
Searching...
No Matches
ui_node_optiontree.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, 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
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
47
48#define EXTRADATA_TYPE abstractOptionExtraData_t
49#define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
50
51static const int COLLAPSEBUTTON_WIDTH = 20;
52static const int DEPTH_WIDTH = 25;
53
56
57/* Used for drag&drop-like scrolling */
58static int mouseScrollX;
59static 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
222static 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
247void 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
278bool 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) {
296 updated = true;
297 }
298 if (node->onWheelDown && down) {
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
323
324void 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
365static 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
387void 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
397void uiOptionTreeNode::onMouseUp (uiNode_t* node, int x, int y, int button)
398{
399 if (UI_GetMouseCapture() == node) /* More checks can never hurt */
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}
int down
Definition cl_input.cpp:66
Header file for keyboard handler.
@ K_MOUSE1
Definition cl_keys.h:46
const char * CL_Translate(const char *t)
@ LONGLINES_PRETTYCHOP
void R_Color(const vec4_t rgba)
Change the color to given value.
Definition r_state.cpp:1011
virtual void onMouseMove(uiNode_t *node, int x, int y)
bool onScroll(uiNode_t *node, int deltaX, int deltaY) override
Auto scroll the list.
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.
void doLayout(uiNode_t *node) override
Call to update the node layout. This common code revalidates the node tree.
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 onLoading(uiNode_t *node) override
Called before loading. Used to set default attribute values.
void onMouseUp(uiNode_t *node, int x, int y, int button) override
void onLeftClick(uiNode_t *node, int x, int y) override
Handles selectboxes clicks.
void onLoaded(uiNode_t *node) override
void draw(uiNode_t *node) override
void onCapturedMouseMove(uiNode_t *node, int x, int y) override
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
QGL_EXTERN GLuint count
Definition r_gl.h:99
QGL_EXTERN int GLboolean GLfloat * v
Definition r_gl.h:120
QGL_EXTERN GLint i
Definition r_gl.h:113
@ ALIGN_UL
Definition scripts.h:90
Header for lua script functions.
#define Q_streq(a, b)
Definition shared.h:136
node behaviour, how a node work
const char * name
void * lua_SWIG_typeinfo
UINodePtr manager
const char * extends
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
struct uiAction_s * onWheelDown
Definition ui_nodes.h:142
uiNode_t * firstChild
Definition ui_nodes.h:89
vec4_t color
Definition ui_nodes.h:127
bool invalidated
Definition ui_nodes.h:104
uiBehaviour_t * behaviour
Definition ui_nodes.h:83
struct uiAction_s * onWheel
Definition ui_nodes.h:138
vec4_t selectedColor
Definition ui_nodes.h:128
uiBox_t box
Definition ui_nodes.h:96
int padding
Definition ui_nodes.h:109
vec4_t disabledColor
Definition ui_nodes.h:103
struct uiAction_s * onWheelUp
Definition ui_nodes.h:141
uiNode_t * depthCache[MAX_DEPTH_OPTIONITERATORCACHE]
Definition ui_data.h:61
vec2_t size
Definition ui_sprite.h:43
vec_t vec2_t[2]
Definition ufotypes.h:38
void UI_ExecuteEventActions(uiNode_t *source, const uiAction_t *firstAction)
const char * UI_GetParam(const uiCallContext_t *context, int paramID)
int UI_GetParamNumber(const uiCallContext_t *context)
const struct value_s * UI_RegisterNodeMethod(uiBehaviour_t *behaviour, const char *name, uiNodeMethod_t function)
Register a node method to a behaviour.
#define UI_RegisterExtradataNodeProperty(BEHAVIOUR, NAME, TYPE, EXTRADATATYPE, ATTRIBUTE)
Initialize a property from extradata of node.
uiNode_t * UI_InitOptionIteratorAtIndex(int index, uiNode_t *option, uiOptionIterator_t *iterator)
Init an option iterator at an index.
Definition ui_data.cpp:394
uiNode_t * UI_GetOption(int dataId)
Definition ui_data.cpp:324
int UI_FindOptionPosition(uiOptionIterator_t *iterator, const uiNode_t *option)
Find an option position from an option iterator.
Definition ui_data.cpp:485
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
int UI_GetDataVersion(int textId)
Definition ui_data.cpp:159
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
Data and interface to share data.
int UI_FontGetHeight(const char *fontID)
Definition ui_font.cpp:166
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_SetMouseCapture(uiNode_t *node)
Captured the mouse into a node.
Definition ui_input.cpp:516
uiNode_t * UI_GetMouseCapture(void)
Return the captured node.
Definition ui_input.cpp:508
void UI_MouseRelease(void)
Release the captured node.
Definition ui_input.cpp:526
bool UI_ExecuteLuaEventScript(uiNode_t *node, LUA_EVENT event)
Executes a lua event handler.
Definition ui_lua.cpp:71
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_NodeAbsoluteToRelativePos(const uiNode_t *node, int *x, int *y)
Update an absolute position to a relative one.
Definition ui_node.cpp:604
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
const char * UI_AbstractOption_GetCurrentValue(uiNode_t *node)
#define EXTRADATA(node)
void UI_AbstractOption_SetCurrentValue(uiNode_t *node, const char *value)
static int mouseScrollY
static int mouseScrollX
const uiBehaviour_t * ui_optionBehaviour
int UI_OptionUpdateCache(uiNode_t *option)
update option cache about child, according to collapse and visible status
#define OPTIONEXTRADATA(node)
static const int DEPTH_WIDTH
static void UI_OptionTreeNodeUpdateScroll(uiNode_t *node)
Update the scroll according to the number of items and the size of the node.
static void UI_OptionTreeNodeUpdateCache(uiNode_t *node)
static uiSprite_t * systemCollapse
#define EXTRADATA(node)
static uiSprite_t * systemExpand
static uiNode_t * UI_OptionTreeNodeGetOptionAtPosition(uiNode_t *node, int x, int y, int *depth)
void UI_RegisterOptionTreeNode(uiBehaviour_t *behaviour)
void UI_OptionTree_SelectValue(uiNode_t *node, const char *value)
static const int COLLAPSEBUTTON_WIDTH
static void UI_OptionTreeSetSelectedValue(uiNode_t *node, const uiCallContext_t *context)
static uiNode_t * UI_OptionTreeNodeGetFirstOption(uiNode_t *node)
Return the first option of the node.
#define V_UI_SPRITEREF
Definition ui_parse.h:56
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)
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
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_NORMAL
Definition ui_sprite.h:33
#define Vector4Set(v, r, g, b, a)
Definition vector.h:62