What is the good way to work with pointers in Javascript/Qml?
-
I'm originally a c/c++ developer, and in this language it's possible to keep a pointer to an object, which may be e.g. used to call a function on this object from another one, or to compare if 2 pointers represent the same object instance.
But the qml language is built above Javascript, and in this language the things are a little more complicated, and the notion of pointers is not exactly the same as in c/c++, for example it's not safe to compare object instances, because they may be different even if they represent the same object.
However I'm working on a project where I need to retrieve instances of qml components and work with them, for example I need to retrieve the instance of the components hovered by the mouse, and to compare if one of these components matches with another one.
For example, consider the following code:
function getItemsAbovePoint(item, point, result) { // iterate through children for (let i = 0; i < item.children.length; ++i) getItemsAbovePoint(item.children[i], point, result); // item contains point? if (item.contains(item.mapFromGlobal(point.x, point.y))) result.push(item); } ... MouseArea { ... /// Called when the mouse button is released after been pressed (above or outside the control) onReleased: function(mouseEvent) { // not adding a link? if (!m_AddingLinkItem) return; // notify the page that the link is no longer dragging if (m_Box && m_PageContent) m_PageContent.m_DraggingMsg = false; // no document? if (!m_Document) return; let doRemoveMsg = false; let targetConn = null; try { let result = []; // get all controls located above the mouse onto the document JSHelper.getItemsAbovePoint(pageContent, this.mapToGlobal(mouseEvent.x, mouseEvent.y), result); // search for target connector for (let i = 0; i < result.length; ++i) if (result[i].objectName === m_Box.leftConnector.objectName || result[i].objectName === m_Box.topConnector.objectName || result[i].objectName === m_Box.rightConnector.objectName || result[i].objectName === m_Box.bottomConnector.objectName) { // found it? May break the loop if yes, because no connector can overlap another targetConn = result[i]; break; } // found a valid target connector? if (targetConn && targetConn.visible && targetConn.m_Box !== m_Box) { console.log("Connector - link added successfully - name - " + m_AddingLinkItem.objectName); // yes, attach the new link to it m_AddingLinkItem.m_To = targetConn; } else { console.log("Connector - link adding - CANCELED"); // no, remove the currently adding link doRemoveMsg = true; } } catch (exception) { let startBoxName = m_Box ? m_Box.objectName : "<unknown>"; let endBoxName = (targetConn && targetConn.m_Box) ? targetConn.m_Box.objectName : "<unknown>"; let targetConnName = targetConn ? targetConn.objectName : "<unknown>"; // log error console.error("Connector - add link - FAILED - start box name - " + startBoxName + " - end box name - " + endBoxName + " - start connector - " + ctConnector.objectName + " - end connector - " + targetConnName + " - error - " + exception); // remove the incompletely added link doRemoveMsg = true; } // do remove the link? if (doRemoveMsg) { m_AddingLinkItem.unbindMsgFromBox(m_Box) m_AddingLinkItem.destroy(); // emit signal that link adding was canceled m_Page.linkCanceled(); } else // emit signal that link was added m_Page.linkAdded(m_AddingLinkItem); // since now link is no longer adding m_AddingLinkItem = null; } }
The above code is a part of the below visual action on my interface:
As you can see, I try to get a component of type "connector" when the mouse button is released, which may be located below the mouse cursor, and which belongs to a parent component of type "box". The purpose of this code is to link a "box" component with another one. If succeeded, the two "boxes" components will be visually linked on the interface with a line, and this line should react to the "box" events, e.g. if a "box" is moved, the line should follow the "box", as it can be seen on the above gif.
The code works globally well in normal circumstances, however there are several issues, among others:
- Comparisons like
targetConn.m_Box !== m_Box
may fail, because in Javascript, pointers may represent the same object instance but be different. I noticed a such issue if e.g. I try to create dynamically the parent "page" component, which contains my "boxes". In this case the above comparison always succeed even if the instances are the same. - In the same idea, the
getItemsAbovePoint()
function may retrieve atargetConn
target connector which is not the one I expected, as if it was a kind of copy of the connector instance, and in this case the line position isn't updated when the "box" is moved, because attached to the incorrect connector instance
So as using pointers seems unsafe in Javascript/Qml, my question is: What should I use instead, and how can I safely retrieve the instance of a component and use it? Or is my concept completely wrong, and in this case, what is the correct way to perform the tasks I need to complete?
I would be grateful if someone may orient me...
- Comparisons like
-
Javascript abstracts the concept of pointers. Data is only ever passed around by reference. It is usually safer, since memory management is handled automatically and you can be sure an object equals another object if they are compared.
If you need to compare objects to see if they are identical, you may have better luck creating a "compare" method that directly compares primitive attributes (strings can be compared directly, for instance).
Hope this helps, sorry I can't provide a more specific implementation suggestion. Too many details to focus on at once.