I used remove() method to delete a rectangle from the scene, how can i draw it back?
The documentation said: "remove a node from parent, but don't destroy. You can reuse the node later."
Link to documentation
I couldn't find any clue
Thanks
Thank you for the responses, i actually found a simple workaround.
I use .hide() and .show() methods because i want to keep intact the object for later use and when i do not need it anymore i'll just .destroy() the shapes.
The downside is you need more memory to keep all, but with few shapes on the scene is negligible.
Just keep a reference to the node via a variable. For example, in the code below I add a node to layer1, remove it, and add it to layer2.
var layer1 = new Konva.Layer();
stage.add(layer1);
Var node = new Konva.WhateverShape({....});
layer1.add(node);
layer1.draw();
...
...
var layer2 = new Konva.Layer();
stage.add(layer2);
node.remove(); // at this time the node exists but is not on the stage
layer2.add(node);
layer2.draw(); // now the node is visible again.
Related
I'm using cytoscape.js and cytoscape.js-expand-collapse to create dynamic hierarchies in my graph structure. I would like to be able to dynamically create a collapsed(merged) node that could potentially be expanded out, removed or possibly re-merged with additional nodes. I am having trouble whenever I call nodes.move({parent:null}). They become detached from the graph and I cannot re-attach them to a new parentNode. If I call restore on them I will see errors saying they already exist in the graph.
merge and unmerge by itself works fine in a simple case of no existing collapsed nodes. However calling merge on something that already contains a compound collapsed node breaks things. I would get the resulting merged node without the children of the previously collapsed merge candidate
Update #1 I've realized my problem was returning the wrong moved nodes. Calling .move on a collection returns a new set of nodes. so the unmerge should return those instead.
Update #2 cytoscape-expand-collapse utility does some internal book-keeping during the parent node collapse/expand by tucking away node data in 'originalEnds' data prop on the meta edges. so, if I'm now altering the graph dynamically by moving nodes in/out of parents these originalEnds pointers get out of sync causing infinite loops. I am doing some additional node tracking of my own as a workaround.
function merge(nodes){
//if one of the nodes is a compound itself, first uncollapse, then merge
var collapsed = nodes.filter(function(c){return typeof c.data('collapsedChildren')!=='undefined';});
if (collapsed.length>0) {
// for now just assume collapsed is a length of 1
var unmerged = unmerge(collapsed); //unmerged should be now the former collapsed children
nodes = nodes.subtract(collapsed).union(unmerged.nodes());
}
var parentNode = cy.add({group:'nodes', data: {id: parentID}});
nodes.move({parent: parentID});
collapseApi.collapse(parentNode);
return parentNode;
}
function unmerge(parentNode){
collapseApi.expand(parentNode);
var children = parentNode.children();
var moved = children.move({parent:null});
//at this point children become "detached" from the graph
// .removed() returns true, however, calling restore() logs an error and they are still technically in the graph
parentNode.remove();
return moved;
}
ele.move() has a more convenient implementation in cytoscape>=3.4: The elements are modified in-place instead of creating replacement elements.
Old code that uses the returned collection from ele.move() will still work. New code can be simplified by not having to use the returned collections at all.
I try to loop a complete header animation endlessly, but I just cannot get it to work:
https://codepen.io/Sixl/pen/jwXGqd
I tried to loop the master like this:
var master = new TimelineMax({repeat:-1});
Then I tried the same solution on the single scene I'm using:
function sceneOne() {
var tl = new TimelineMax({repeat:-1});
I also tried several solutions with oncomplete callbacks.
I finally had some experiments with
timeline.seek(0).pause();
timeline.pause(0); //shortcut for the line above
timeline.progress(0).pause();
timeline.totalProgress(0).pause();
timeline.restart(0).pause();
inside my scene. Didn't help.
Can somebody point me in the right direction? I cannot find any error in my code.
It is the entry_heroes section that is causing the problem as it has itself a repeat: -1 that means that the section never ends and so the whole timeline never finishes and thus does not repeat.
I'm using Dojo dnd version 1.7.2 and it's generally working really well. I'm happy.
My app maintains many arrays of items, and as the user drags and drops items around, I need to ensure that my arrays are updated to reflect the contents the user is seeing.
In order to accomplish this, I think I need to run some code around the time of Source.onDndDrop
If I use dojo.connect to set up a handler on my Source for onDndDrop or onDrop, my code seems to get called too late. That is, the source that's passed to the handler doesn't actually have the item in it any more.
This is a problem because I want to call source.getItem(nodes[0].id) to get at the actual data that's being dragged around so I can find it in my arrays and update those arrays to reflect the change the user is making.
Perhaps I'm going about this wrong; and there's a better way?
Ok, I found a good way to do this. A hint was found in this answer to a different question:
https://stackoverflow.com/a/1635554/573110
My successful sequence of calls is basically:
var source = new dojo.dnd.Source( element, creationParams );
var dropHandler = function(source,nodes,copy){
var o = source.getItem(nodes[0].id); // 0 is cool here because singular:true.
// party on o.data ...
this.oldDrop(source,nodes,copy);
}
source.oldDrop = source.onDrop;
source.onDrop = dropHandler;
This ensures that the new implementation of onDrop (dropHandler) is called right before the previously installed one.
Kind'a shooting a blank i guess, there are a few different implementations of the dndSource. But there are a some things one needs to know about the events / checkfunctions that are called during the mouseover / dnddrop.
One approach would be to setup checkAcceptance(source, nodes) for any target you may have. Then keep a reference of the nodes currently dragged. Gets tricky though, with multiple containers that has dynamic contents.
Setup your Source, whilst overriding the checkAcceptance and use a known, (perhaps global) variable to keep track.
var lastReference = null;
var target = dojo.dnd.Source(node, {
checkAcceptance(source, nodes) : function() {
// this is called when 'nodes' are attempted dropped - on mouseover
lastReference = source.getItem(nodes[0].id)
// returning boolean here will either green-light or deny your drop
// use fallback (default) behavior like so:
return this.inhertied(arguments);
}
});
Best approach might just be like this - you get both target and source plus nodes at hand, however you need to find out which is the right stack to look for the node in. I believe it is published at same time as the event (onDrop) youre allready using:
dojo.subscribe("/dnd/drop", function(source, nodes, copy, target) {
// figure out your source container id and target dropzone id
// do stuff with nodes
var itemId = nodes[0].id
}
Available mechanics/topics through dojo.subscribe and events are listed here
http://dojotoolkit.org/reference-guide/1.7/dojo/dnd.html#manager
Here is my code:
cLicensePanel::cLicensePanel( wxWindow * parent )
: wxPanel(parent,-1,wxPoint(0,0),wxSize(500,500))
{
wxBoxSizer * szrUserKey = new wxBoxSizer(wxHORIZONTAL);
myUserKeyCtrl = new wxTextCtrl(this,-1,L"");
szrUserKey->Add( new wxStaticText(this,-1,wxString(L"User's ComputerKey:")),
0,wxALL,10);
szrUserKey->Add( myUserKeyCtrl,0,wxALL,10);
szrUserKey->Add( new wxButton(this,IDC_Generate,L"Generate"),0,wxALL,10);
SetSizer( szrUserKey );
}
This is intended to produce this display, with the 3 widgets neatly arranged in a row.
However, what I actually get is all the widgets piled one on top of the other
Why?
I can force the correct display by adding an explicit call to Sizer::Layout()
cLicensePanel::cLicensePanel( wxWindow * parent )
: wxPanel(parent,-1,wxPoint(0,0),wxSize(500,500))
{
wxBoxSizer * szrUserKey = new wxBoxSizer(wxHORIZONTAL);
myUserKeyCtrl = new wxTextCtrl(this,-1,L"");
szrUserKey->Add( new wxStaticText(this,-1,wxString(L"User's Computer Key:")),0,wxALL,10);
szrUserKey->Add( myUserKeyCtrl,0,wxALL,10);
szrUserKey->Add( new wxButton(this,IDC_Generate,L"Generate"),0,wxALL,10);
szrUserKey->Layout();
SetSizer( szrUserKey );
}
I can live with this, though it seems to me that it should be unnecessary to call Layout. I also suspect that the reason the Layout call is neccessary might be an important clue to the problem that has me stumped.
The above is the behaviour when using wxWidgets v2.8.12. I am upgrading to v2.9.3.
In the new version the initial code, without the call to Layout(), shows the same problem with the widgets all piled on top of each other. If I add the call to layout() in v2.9.3 then I cannot see any of the widgets at all - the panel shows completely blank!
SetSizer() intentionally doesn't do the layout, it only happens when the window is resized. But it also so happens that a newly created window always gets a EVT_SIZE, soon after being shown, so by the time the user sees it, it's already laid out (and this is the reason SetSizer() doesn't do it: this would be wasteful as it would be redone very soon anyhow in 99% of cases).
In your case the panel doesn't get this size event for whatever reason. Perhaps -- looking at the rest of your code -- you can see what it is and avoid it. But if not, calling Layout() is a fine solution too. Except that you should call the same method on the panel itself, not on the sizer, i.e. do
cLicensePanel::cLicensePanel( wxWindow * parent )
{
...
SetSizer(szrUserKey);
Layout();
}
I am trying to add children tree nodes to a non-Root node dynamically when it's selected.
This is how I implemented it:
First, I populate my tree with a TreeStore then to dynamically add children tree nodes, I handle it on the itemexpand event in tree Controller.
//pThis is a NodeInterface obj of the selected node
itemexpand: function (pThis, pEOpts) {
//type is an attribute that I added so that I know what type of node that got selected
if (pThis.raw.type === 'Staff' && !pThis.hasChildNodes()) {
var staffNodeAry = [{text:"Teachers", type:"ChildStaff", leaf: false},
{text:"Principals", type:"ChildStaff", leaf: false}];
pThis.appendChild(staffNodeAry);
}
}
This worked quite good because I am able to see both "Teachers" and "Principals" node under "Staff" type but there are 2 issues that I found so far:
1) There's this error on the console:
Uncaught TypeError: Cannot read property 'internalId' of undefined
I don't know why but if anyone could enlighten that would be great.
2) When I expand either "Teachers" and "Principals", the NodeInterface obj that's returned by itemexpand has an undefined raw object.
So I couldn't do this: pThis.raw.type
From my understanding, I don't need to create a new TreeStore to add any new children nodes, I thought that by creating an array of objects that adhere to NodeInterface should be enough but I might be wrong.
If anyone could point me to the right direction, that would be great.
Handi
More info:
What I did was actually following the ExtJs 4 documentation so I believe this is a bug on ExtJs 4 framework.
I also tested this with Extjs-4.1.0-rc3 and it also produced the same error.
I have resolved this issue changing the event itemexpand with the event afteritemexpand. I have no documentation to justify this. The debugger says to me that the view listen for the itemexpand event and do something that goes wrong if add a node manually...
For the question number 2: you have no raw object because it is created by the reader of the store after the load operation. So if you add a node by hand the raw object does not exists.
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Model-property-raw
The documentation for .appendChild() functions says:
node : Ext.data.NodeInterface/Ext.data.NodeInterface[]
The node or Array of nodes to append
And you are trying to append an array of 2 objects, but they are not "NodeInterface" instances. So you should create them properly like so:
var nodeToAppend1 = pThis.createNode({text:"Teachers", type:"ChildStaff", leaf: false});
pThis.appendChild(nodeToAppend1);
I remember making the same mistake a year ago, so I hope this helps.
I think it happens because your Ext.TreePanel working asyncroniously. So add process is:
Append Child, add virtual child leaf to your tree, with red mark in corner
Call store's create api
Receive response, if it success=true, change with virtual
The goal of changing is set internakId property, so you can add new child only after this change happens. I think better is write some recursive function and bind it to store's added event
tree.getStore().on('update', addChild);
or
tree.getStore().load({
success: function() {
addChild()
});