static void UI_AbstractNodeCallCreateChild (uiNode_t* node, const uiCallContext_t* context) { uiNode_t* child; uiNode_t* component; const char* name; const char* type; if (UI_GetParamNumber(context) != 2) { Com_Printf("UI_AbstractNodeCallCreateChild: Invalid number of parameters\n"); return; } name = UI_GetParam(context, 1); type = UI_GetParam(context, 2); uiNode_t* existingNode = UI_GetNode(node, name); if (existingNode != nullptr) { Com_Printf("UI_AbstractNodeCallCreateChild: Node with name '%s' already exists\n", name); } component = UI_GetComponent(type); if (component) { child = UI_CloneNode(component, node->root, true, name, true); } else { child = UI_AllocNode(name, type, true); } if (child == nullptr) { Com_Printf("UI_AbstractNodeCallCreateChild: Impossible to create the node\n"); return; } UI_AppendNode(node, child); }
/** * @brief Create/remove drag button. */ void UI_Window_SetDragButton (uiNode_t* node, bool value) { if (value) { // create the drag node uiNode_t* control = UI_AllocNode(WINDOW_DRAG_BUTTON_NAME, "controls", node->dynamic); control->root = node; control->box.size[0] = node->box.size[0]; control->box.size[1] = TOP_HEIGHT; control->box.pos[0] = 0; control->box.pos[1] = 0; control->tooltip = _("Drag to move window"); /* if there is a close button already on this windown, then insert the drag button before the close button; this is needed so the drag button is "below" the close button; if we don't do this, the drag button recieves input events for the close button first making the close button no longer working */ uiNode_t* close = UI_FindNode(node, WINDOW_CLOSE_BUTTON_NAME); if (close != nullptr) { // get the previous node of close close = UI_GetPrevNode(close); UI_InsertNode(node, close, control); } else { UI_AppendNode(node, control); } } else { // drop the drag node uiNode_t* control = UI_FindNode(node, WINDOW_DRAG_BUTTON_NAME); if (control) { UI_RemoveNode (node, control); } } EXTRADATA(node).dragButton = value; }
/** * @brief Create/remove close button on window. * @note Creates a onClick event handler, should be refactored. */ void UI_Window_SetCloseButton (uiNode_t* node, bool value) { if (value) { uiNode_t* control = UI_AllocNode(WINDOW_CLOSE_BUTTON_NAME, "button", node->dynamic); const int positionFromRight = CONTROLS_PADDING; static const char* closeCommand = "ui_close <path:root>;"; control->root = node; UI_NodeSetProperty(control, UI_GetPropertyFromBehaviour(control->behaviour, "icon"), "icons/system_close"); /** @todo Once @c image_t is known on the client, use @c image->width resp. @c image->height here */ control->box.size[0] = CONTROLS_IMAGE_DIMENSIONS; control->box.size[1] = CONTROLS_IMAGE_DIMENSIONS; control->box.pos[0] = node->box.size[0] - positionFromRight - control->box.size[0]; control->box.pos[1] = CONTROLS_PADDING; control->tooltip = _("Close the window"); control->onClick = UI_AllocStaticCommandAction(closeCommand); UI_AppendNode(node, control); } else { // drop the close node uiNode_t* control = UI_FindNode(node, WINDOW_CLOSE_BUTTON_NAME); if (control) { UI_RemoveNode (node, control); } } EXTRADATA(node).closeButton = value; }
/** * @brief Called at the end of the load from script */ void uiWindowNode::onLoaded (uiNode_t* node) { /* create a drag zone, if it is requested */ if (EXTRADATA(node).dragButton) { uiNode_t* control = UI_AllocNode("move_window_button", "controls", node->dynamic); control->root = node; control->box.size[0] = node->box.size[0]; control->box.size[1] = TOP_HEIGHT; control->box.pos[0] = 0; control->box.pos[1] = 0; control->tooltip = _("Drag to move window"); UI_AppendNode(node, control); } /* create a close button, if it is requested */ if (EXTRADATA(node).closeButton) { uiNode_t* button = UI_AllocNode("close_window_button", "button", node->dynamic); const int positionFromRight = CONTROLS_PADDING; static const char* closeCommand = "ui_close <path:root>;"; button->root = node; UI_NodeSetProperty(button, UI_GetPropertyFromBehaviour(button->behaviour, "icon"), "icons/system_close"); /** @todo Once @c image_t is known on the client, use @c image->width resp. @c image->height here */ button->box.size[0] = CONTROLS_IMAGE_DIMENSIONS; button->box.size[1] = CONTROLS_IMAGE_DIMENSIONS; button->box.pos[0] = node->box.size[0] - positionFromRight - button->box.size[0]; button->box.pos[1] = CONTROLS_PADDING; button->tooltip = _("Close the window"); button->onClick = UI_AllocStaticCommandAction(closeCommand); UI_AppendNode(node, button); } EXTRADATA(node).isFullScreen = node->box.size[0] == VID_NORM_WIDTH && node->box.size[1] == VID_NORM_HEIGHT; if (EXTRADATA(node).starLayout) UI_Invalidate(node); }
/** * @brief Clone a node * @param[in] node Node to clone * @param[in] recursive True if we also must clone subnodes * @param[in] newWindow Window where the nodes must be add (this function only link node into window, not window into the new node) * @param[in] newName New node name, else nullptr to use the source name * @param[in] isDynamic Allocate a node in static or dynamic memory * @todo exclude rect is not safe cloned. * @todo actions are not cloned. It is be a problem if we use add/remove listener into a cloned node. */ uiNode_t* UI_CloneNode (const uiNode_t* node, uiNode_t* newWindow, bool recursive, const char* newName, bool isDynamic) { uiNode_t* newNode = UI_AllocNodeWithoutNew(nullptr, UI_Node_GetWidgetName(node), isDynamic); /* clone all data */ memcpy(newNode, node, UI_Node_GetMemorySize(node)); newNode->dynamic = isDynamic; /* custom name */ if (newName != nullptr) { Q_strncpyz(newNode->name, newName, sizeof(newNode->name)); if (strlen(newNode->name) != strlen(newName)) Com_Printf("UI_CloneNode: Node name \"%s\" truncated. New name is \"%s\"\n", newName, newNode->name); } /* clean up node navigation */ if (node->root == node && newWindow == nullptr) newWindow = newNode; newNode->root = newWindow; newNode->parent = nullptr; newNode->firstChild = nullptr; newNode->lastChild = nullptr; newNode->next = nullptr; newNode->super = node; /* clone node methods */ if (node->nodeMethods) { newNode->nodeMethods = HASH_CloneTable(node->nodeMethods); } /* allocate memories */ if (newNode->dynamic) { UI_Node_InitNodeDynamic(newNode); } /* typically, cloning makes a copy of all the internals of a source node; override the ::clone(node) method to alter this behaviour */ UI_Node_Clone(node, newNode); /* clone child */ if (recursive) { for (uiNode_t* childNode = node->firstChild; childNode; childNode = childNode->next) { uiNode_t* newChildNode = UI_CloneNode(childNode, newWindow, recursive, nullptr, isDynamic); UI_AppendNode(newNode, newChildNode); } } return newNode; }
/** * @brief Clone a node * @param[in] node Node to clone * @param[in] recursive True if we also must clone subnodes * @param[in] newWindow Window where the nodes must be add (this function only link node into window, not window into the new node) * @param[in] newName New node name, else NULL to use the source name * @param[in] isDynamic Allocate a node in static or dynamic memory * @todo exclude rect is not safe cloned. * @todo actions are not cloned. It is be a problem if we use add/remove listener into a cloned node. */ uiNode_t* UI_CloneNode (const uiNode_t* node, uiNode_t *newWindow, qboolean recursive, const char *newName, qboolean isDynamic) { uiNode_t* newNode = UI_AllocNodeWithoutNew(NULL, node->behaviour->name, isDynamic); /* clone all data */ memcpy(newNode, node, sizeof(*node) + node->behaviour->extraDataSize); newNode->dynamic = isDynamic; /* custom name */ if (newName != NULL) { Q_strncpyz(newNode->name, newName, sizeof(newNode->name)); if (strlen(newNode->name) != strlen(newName)) Com_Printf("UI_CloneNode: Node name \"%s\" truncated. New name is \"%s\"\n", newName, newNode->name); } /* clean up node navigation */ if (node->root == node && newWindow == NULL) newWindow = newNode; newNode->root = newWindow; newNode->parent = NULL; newNode->firstChild = NULL; newNode->lastChild = NULL; newNode->next = NULL; newNode->super = *(uiNode_t**) ((void*)&node); /* clone child */ if (recursive) { uiNode_t* childNode; for (childNode = node->firstChild; childNode; childNode = childNode->next) { uiNode_t* newChildNode = UI_CloneNode(childNode, newWindow, recursive, NULL, isDynamic); UI_AppendNode(newNode, newChildNode); } } /* allocate memories */ if (newNode->dynamic && newNode->behaviour->newNode) newNode->behaviour->newNode(newNode); newNode->behaviour->clone(node, newNode); return newNode; }
/** * @brief Clone a node * @param[in] node Node to clone * @param[in] recursive True if we also must clone subnodes * @param[in] newWindow Window where the nodes must be add (this function only link node into window, not window into the new node) * @param[in] newName New node name, else nullptr to use the source name * @param[in] isDynamic Allocate a node in static or dynamic memory * @todo exclude rect is not safe cloned. * @todo actions are not cloned. It is be a problem if we use add/remove listener into a cloned node. */ uiNode_t* UI_CloneNode (const uiNode_t* node, uiNode_t *newWindow, bool recursive, const char *newName, bool isDynamic) { uiNode_t* newNode = UI_AllocNodeWithoutNew(nullptr, UI_Node_GetWidgetName(node), isDynamic); /* clone all data */ memcpy(newNode, node, UI_Node_GetMemorySize(node)); newNode->dynamic = isDynamic; /* custom name */ if (newName != nullptr) { Q_strncpyz(newNode->name, newName, sizeof(newNode->name)); if (strlen(newNode->name) != strlen(newName)) Com_Printf("UI_CloneNode: Node name \"%s\" truncated. New name is \"%s\"\n", newName, newNode->name); } /* clean up node navigation */ if (node->root == node && newWindow == nullptr) newWindow = newNode; newNode->root = newWindow; newNode->parent = nullptr; newNode->firstChild = nullptr; newNode->lastChild = nullptr; newNode->next = nullptr; newNode->super = node; /* clone child */ if (recursive) { uiNode_t* childNode; for (childNode = node->firstChild; childNode; childNode = childNode->next) { uiNode_t* newChildNode = UI_CloneNode(childNode, newWindow, recursive, nullptr, isDynamic); UI_AppendNode(newNode, newChildNode); } } /* allocate memories */ if (newNode->dynamic) UI_Node_NewNode(newNode); UI_Node_Clone(node, newNode); return newNode; }
/** * @brief parse a node * @sa UI_ParseNodeProperties * @todo we can think about merging UI_ParseNodeProperties here * @note first token already read * @note dont read more than the need token (last right token is '}' of end of node) */ static uiNode_t* UI_ParseNode (uiNode_t* parent, const char** text, const char** token, const char* errhead) { uiNode_t* node = nullptr; uiBehaviour_t* behaviour; uiNode_t* component = nullptr; /* get the behaviour */ behaviour = UI_GetNodeBehaviour(*token); if (!behaviour) { component = UI_GetComponent(*token); } if (behaviour == nullptr && component == nullptr) { Com_Printf("UI_ParseNode: node behaviour/component '%s' doesn't exist (%s)\n", *token, UI_GetPath(parent)); return nullptr; } /* get the name */ *token = Com_EParse(text, errhead, ""); if (!*text) return nullptr; if (!UI_TokenIsName(*token, Com_GetType(text) == TT_QUOTED_WORD)) { Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", *token); return nullptr; } if (UI_TokenIsReserved(*token)) { Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", *token); return nullptr; } /* test if node already exists */ /* Already existing node should only come from inherited node, we should not have 2 definitions of the same node into the same window. */ if (parent) node = UI_GetNode(parent, *token); /* reuse a node */ if (node) { const uiBehaviour_t* test = (behaviour != nullptr) ? behaviour : (component != nullptr) ? component->behaviour : nullptr; if (node->behaviour != test) { Com_Printf("UI_ParseNode: we can't change node type (node \"%s\")\n", UI_GetPath(node)); return nullptr; } Com_DPrintf(DEBUG_CLIENT, "... over-riding node %s\n", UI_GetPath(node)); /* else initialize a component */ } else if (component) { node = UI_CloneNode(component, nullptr, true, *token, false); if (parent) { if (parent->root) UI_UpdateRoot(node, parent->root); UI_AppendNode(parent, node); } /* else initialize a new node */ } else { node = UI_AllocNode(*token, behaviour->name, false); node->parent = parent; if (parent) node->root = parent->root; /** @todo move it into caller */ if (parent) UI_AppendNode(parent, node); } /* get body */ const bool result = UI_ParseNodeBody(node, text, token, errhead); if (!result) return nullptr; /* validate properties */ UI_Node_Loaded(node); return node; }