Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Persisting visibility property in use of EditContext & EditNode

    Hi

    SmartClient v9.1p_2014-03-25/Enterprise Deployment (2014-03-25)

    Chrome Version 33.0.1750.154 m
    Chromium 18.0.1004.0 (Developer Build 117091 Windows)


    I am trying to build up what will eventually become a complex
    collection of instances of SmartClient classes (each wrapped in an
    EditNode) whose state needs to be persistable using a common global
    EditContext to which they are all added.

    In trying to do this for a more complicated structure than i present
    below, i encountered a problem that the visibility property of the
    various objects involved (whose "inherit" vs "hidden" value i rely on
    for certain functionality) is not preserved across a "reincarnation"
    of the object collection.

    The code i present below is a very stripped-down version of the sort
    of system i need to work with, which illustrates:

    - in microcosm how i'm using the EditContext and EditNode
    functionality,
    - what specifically i'm trying but currently failing to accomplish.
    - how i'm implementing reincarnation.
    - how reincarnation interferes with the state of the system and causes
    the functionality to break.

    The code is a qunit test which interleaves a sequence of actions and
    state checks. Between actions, certain things about the state should
    be true. A reincarnate is just a special kind of action which is
    supposed in all circumstances to preserve the state of the system.

    We can see that if all the reincarnate actions are commented out, that
    all the tests pass. If we uncomment either or both of the reincarnate
    actions that could be performed when the system is in the "shown"
    state (i.e., the first and third), then all the tests continue to
    pass. But if we uncomment the reincarnate action that could be
    performed when the system is in the "hidden" state (the second), then
    the test immediately following it fails. And this is because the
    second reincarnation fails to preserve the fact that the visibility
    property of the Canvas should be "hidden", instead of being
    reincarnated with this property reverted to "inherit".

    What can you suggest i fix about how i'm using your
    EditContext/EditNode functionality to allow the current state of a
    built-in property like visibility to be persisted in a manner that
    will allow these tests to pass with the second reincarnate action
    uncommented?

    What other general recommendations can you make about how i should
    change the way i am trying to use this functionality?

    SCP_ForumATest.html:
    Code:
    <!DOCTYPE html>
    <html>
      <head>
        <title>ForumATest</title>
        <script>var isomorphicDir = "isomorphic/";</script>
        <script src="isomorphic/system/modules-debug/ISC_Core.js"></script>
        <script src="isomorphic/system/modules-debug/ISC_Foundation.js"></script>
        <script src="isomorphic/system/modules-debug/ISC_Containers.js"></script>
        <script src="isomorphic/system/modules-debug/ISC_Grids.js"></script>
        <script src="isomorphic/system/modules-debug/ISC_Forms.js"></script>
        <script src="isomorphic/system/modules-debug/ISC_DataBinding.js"></script>
        <script src="isomorphic/system/modules-debug/ISC_Drawing.js"></script>
        <script src="isomorphic/system/modules-debug/ISC_Charts.js"></script>
        <script src="isomorphic/system/development/ISC_Tools.js"></script>
      </head>
      <body>
        <div id="qunit" style="display:block"></div>
        <div id="qunit-fixture" style="display:block"></div>
        <script src="js/qunit.js"></script>
        <link rel="stylesheet" href="js/qunit.css">
        <script src="SCP_ForumATest.js"></script>
      </body>
    </html>
    SCP_ForumATest.js:
    Code:
    test("persistingVisibility", function(assert){
        
        Fixture = {
    	editPane : isc.EditPane.create({}),
    	getLiveObjectArray : function() {
    	    return Fixture.editPane.getEditNodeTree().getChildren().map(function(x){return x.liveObject;});
    	},
    	serialize : function() {
    	    return Fixture.editPane.serializeAllEditNodesAsJSON(false);
    	}
        };
        
        ClassFactory.defineClass("SCP_CanvasA",isc.Canvas).addProperties({
    	initWidget : function(args) {
    	    this.Super("initWidget", arguments);
    	}
        });
        
        ClassFactory.defineClass("SCP_LayoutA",isc.Layout).addProperties({
    	initWidget : function(args) {
    	    this.Super("initWidget", arguments);
    	    var canvasNode = Fixture.editPane.makeEditNode({type:"SCP_CanvasA",defaults:{backgroundColor:"#070",width:90,height:90}});
    	    this.canvas = canvasNode.liveObject;
    	    this.addMember(this.canvas);
    	    Fixture.editPane.addNode(canvasNode,undefined,undefined,undefined,true);
    	    this.show();
    	},
    	showCanvas : function() {
    	    this.canvas.show();
    	},
    	hideCanvas : function() {
    	    this.canvas.hide();
    	}
        });
        
        TestSequenceA = {
    	
    	actionCreate : function(liveObjectArray) {
    	    var layoutNode = Fixture.editPane.makeEditNode({type:"SCP_LayoutA",defaults:{backgroundColor:"#700",width:100,height:100}});
    	    Fixture.editPane.addNode(layoutNode,undefined,undefined,undefined,true);
    	},
    	
    	actionShow : function(liveObjectArray) {
    	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
    	    layout[0].showCanvas();
    	},
    	
    	actionHide : function(liveObjectArray) {
    	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
    	    layout[0].hideCanvas();
    	},
    	
    	checkShownState : function(liveObjectArray) {
    	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
    	    equal(layout[0].canvas.visibility,isc.Canvas.INHERIT,"canvas should be visible");
    	},
    	
    	checkHiddenState : function(liveObjectArray) {
    	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
    	    equal(layout[0].canvas.visibility,isc.Canvas.HIDDEN,"canvas should be invisible");
    	}
        };
        
        var reincarnate = function(s11n) {
    	ok(true,"before reincarnation, serialization=="+Fixture.serialize() );
    	Fixture.editPane.destroyAll();
    	ok(true,"during reincarnation, serialization=="+Fixture.serialize() );
    	Fixture.editPane.addPaletteNodesFromJSON(s11n);
    	ok(true," after reincarnation, serialization=="+Fixture.serialize() );
        };
        
        TestSequenceA.actionCreate	(Fixture.getLiveObjectArray());
        TestSequenceA.actionShow	(Fixture.getLiveObjectArray());
        
        TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
        // reincarnate			(Fixture.serialize()); // first
        TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
        
        TestSequenceA.actionHide	(Fixture.getLiveObjectArray());
        
        TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
        // reincarnate			(Fixture.serialize()); // second
        TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
        
        TestSequenceA.actionShow	(Fixture.getLiveObjectArray());
        
        TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
        // reincarnate			(Fixture.serialize()); // third
        TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
        
    });

    #2
    In the code you've shown, we see no attempt to make the visibility property a part of the saved state at all - no calls to updateEditNode, no attempt to add "visibility" to the editNode or paletteNode.defaults.

    Let us know if we're missing something.

    Comment


      #3
      Originally posted by Isomorphic View Post
      In the code you've shown, we see no attempt to make the visibility property a part of the saved state at all - no calls to updateEditNode, no attempt to add "visibility" to the editNode or paletteNode.defaults.

      Let us know if we're missing something.
      (I'd just like it to be clear that we were the commissioners of the feature sponsorship that led to the development of this functionality, and so have a vested interest in figuring out how to use what you have done for us. We would appreciate an appropriate level of your help in this endeavour.)

      I have done some experimentation based on your hints:

      EXPERIMENT 1.

      Add a visibility property to the paletteNode.defaults in the argument to makeEditNode in the canvasNode assignment in SCP_LayoutA.initWidget

      - Control: makes no attempt at adding visibility property
      - INHERIT: sets the visibility property to isc.Canvas.INHERIT
      - HIDDEN: sets the visibility property to isc.Canvas.HIDDEN

      Results of EXPERIMENT 1:
      Code:
      | reincarnates  | Control                | INHERIT                | HIDDEN                  |
      |---------------+------------------------+------------------------+-------------------------|
      | all disabled  | (6/6) all tests pass   | (6/6) all tests pass   | (6/6) all tests pass    |
      | 1 enabled     | (9/9) all tests pass   | (9/9) all tests pass   | (8/9) test 5 FAILS      |
      | 1,3 enabled   | (12/12) all tests pass | (12/12) all tests pass | (10/12) tests 5,12 FAIL |
      | 1,2,3 enabled | (14/15) test 10 FAILS  | (14/15) test 10 FAILS  | (13/15) tests 5,15 FAIL |
      These results imply:

      - that putting visibility:isc.Canvas.INHERIT into the picture has no effect on the outcome (which is to be expected, since "inherit" is the default value for Canvas.visibility).
      - that putting visibility:isc.Canvas.HIDDEN into the picture DOES have an effect on the outcome (so we know something is happening), but it is NOT the desired effect overall.

      Together, these two scenarios imply that if we go the route of adding a visibility property to paletteNodes.default (given for now that the rest of the code usage remains as it is), then we need to add it with a variable value that is somehow determined when SCP_LayoutA.initWidget is called during initial construction and during reconstruction at the end of a reincarnation.

      This probably requires passing the value in as an argument to the constructor of SCP_LayoutA, or storing what should be an attribute of SCP_CanvasA as an attribute of SCP_LayoutA. Neither of these options seem particularly satisfactory, but i could pursue them if i get more hints that that is the right way to go.

      EXPERIMENT 2:

      Right now, attempting to experiment with Canvas.updateEditNode (whose existence i was not aware of) looks more attractive:

      So, that looks like a callback which needs to be defined (overridden) for each liveObject, and that it should make sure that the properties in the liveObject that we wish to have persisted be used to update the corresponding objects in the editNode.

      So, mimicking the way this has been done for the Portlet, PortalRow and PortalColumn classes in ISC_DataBinding.js, we use editContext.setNodeProperties(editNode,{...},true).

      This appears to amount to creating an updateEditNode override for SCP_CanvasA, and to set the visibility node property in it to be equal to the current value of the canvas's visibility property. So, i try that, again looking at the above Control, INHERIT, HIDDEN scenarios, in order to see whether setting a default is also necessary.

      Results of EXPERIMENT 2:
      Code:
      | reincarnates  | Control                | INHERIT                | HIDDEN                  |
      |---------------+------------------------+------------------------+-------------------------|
      | all disabled  | (6/6) all tests pass   | (6/6) all tests pass   | (6/6) all tests pass    |
      | 1 enabled     | (9/9) all tests pass   | (9/9) all tests pass   | (8/9) test 5 FAILS      |
      | 1,3 enabled   | (12/12) all tests pass | (12/12) all tests pass | (10/12) tests 5,12 FAIL |
      | 1,2,3 enabled | (14/15) test 10 FAILS  | (14/15) test 10 FAILS  | (13/15) tests 5,15 FAIL |
      Unfortunately, it looks like defining an override for SCP_CanvasA.updateEditNode doesn't make any difference to the behaviour of the system.

      I'm not sure what else to try. Please advise me further.

      Here is the modified version of the code on which i conducted the experiments mentioned above. The bits of the code that i was changing are those which are depicted as commented out with // in the extreme left margin.

      SCP_ForumATest.js (edited)
      Code:
      test("persistingVisibility", function(assert){
          
          Fixture = {
      	editPane : isc.EditPane.create({}),
      	getLiveObjectArray : function() {
      	    return Fixture.editPane.getEditNodeTree().getChildren().map(function(x){return x.liveObject;});
      	},
      	serialize : function() {
      	    return Fixture.editPane.serializeAllEditNodesAsJSON(false);
      	}
          };
          
          ClassFactory.defineClass("SCP_CanvasA",isc.Canvas).addProperties({
      	initWidget : function(args) {
      	    this.Super("initWidget", arguments);
      	}
      //	,
      //	updateEditNode : function(editContext, editNode) {
      //	    editContext.setNodeProperties(editNode,{ visibility: this.visibility },true);
      //	}
          });
          
          ClassFactory.defineClass("SCP_LayoutA",isc.Layout).addProperties({
      	initWidget : function(args) {
      	    this.Super("initWidget", arguments);
      	    var canvasNode = Fixture.editPane.makeEditNode({type:"SCP_CanvasA",defaults:{
      //		visibility:isc.Canvas.INHERIT,
      //		visibility:isc.Canvas.HIDDEN,
      		backgroundColor:"#070",width:90,height:90}});
      	    this.canvas = canvasNode.liveObject;
      	    this.addMember(this.canvas);
      	    Fixture.editPane.addNode(canvasNode,undefined,undefined,undefined,true);
      	    this.show();
      	},
      	showCanvas : function() {
      	    this.canvas.show();
      	},
      	hideCanvas : function() {
      	    this.canvas.hide();
      	}
          });
          
          TestSequenceA = {
      	
      	actionCreate : function(liveObjectArray) {
      	    var layoutNode = Fixture.editPane.makeEditNode({type:"SCP_LayoutA",defaults:{backgroundColor:"#700",width:100,height:100}});
      	    Fixture.editPane.addNode(layoutNode,undefined,undefined,undefined,true);
      	},
      	
      	actionShow : function(liveObjectArray) {
      	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
      	    layout[0].showCanvas();
      	},
      	
      	actionHide : function(liveObjectArray) {
      	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
      	    layout[0].hideCanvas();
      	},
      	
      	checkShownState : function(liveObjectArray) {
      	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
      	    equal(layout[0].canvas.visibility,isc.Canvas.INHERIT,"canvas should be visible");
      	},
      	
      	checkHiddenState : function(liveObjectArray) {
      	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
      	    equal(layout[0].canvas.visibility,isc.Canvas.HIDDEN,"canvas should be invisible");
      	}
          };
          
          var reincarnate = function(s11n) {
      	ok(true,"before reincarnation, serialization=="+Fixture.serialize() );
      	Fixture.editPane.destroyAll();
      	ok(true,"during reincarnation, serialization=="+Fixture.serialize() );
      	Fixture.editPane.addPaletteNodesFromJSON(s11n);
      	ok(true," after reincarnation, serialization=="+Fixture.serialize() );
          };
          
          TestSequenceA.actionCreate		(Fixture.getLiveObjectArray());
          TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
          
          TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
      //  reincarnate				(Fixture.serialize()); // first
          TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
          
          TestSequenceA.actionHide		(Fixture.getLiveObjectArray());
          
          TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
      //  reincarnate				(Fixture.serialize()); // second
          TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
          
          TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
          
          TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
      //  reincarnate				(Fixture.serialize()); // third
          TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
          
      });

      Comment


        #4
        We would appreciate an appropriate level of your help in this endeavour
        We're all set to provide that help - please understand that, when you post code that would not be expected to work, we need to figure out if you have misconceptions vs just posted the wrong code or some similar trivial problem.

        Now we can see that, yes, you do have some misconceptions, so to clear those up:

        The information in editNode.defaults is updated by:

        1. a component in EditMode (or EditContext) that documents that it will automatically persist certain kinds of information (such as EditContext.persistCoordinates)

        2. an explicit call to setNodeProperties()

        .. and by nothing else.

        So all these experiments about what might happen if paletteNode.defaults contains a setting - this wouldn't be expected to do anything.

        updateEditNode() is a *notification* you receive during serialization, and is an opportunity to call setNodeProperties() to make last-second changes. This is for use cases where it is too expensive to call setNodeProperties() right when the change occurs, to allow you to do so lazily only when data is actually being serialized.

        So if you want the visibility setting to be persisted, you need a call to setNodeProperties() persisting it. You could either do that by using the visibilityChanged() notification on each Layout member, or by making call lazily when updateEditNode() is called.

        We've already been greatly expanding and clarifying the docs for this whole subsystem as part of 5.0, and we'll make sure the information above is suitably emphasized.

        Comment


          #5
          For this round of tests, all reincarnates are enabled.

          I think it could make a difference whether there is a default value in
          the picture or not. In order to see where it shows up during testing
          and whether it has any impact on the final outcome, i either give it a
          fake value ("FAKE") or i don't set it at all ("NONE").

          Then, i focus my attention on ways of using setNodeProperties:
          - setNodeProperties is not used at all ("CONTROL")
          - setNodeProperties is used in SCP_CanvasA.updateEditNode ("LAZY")
          - setNodeProperties is used in SCP_LayoutA.{show,hide}Canvas ("EAGER")

          I have written a new function Fixture.neaten which slightly digests
          the serialization for ease of viewing (especially viewing
          differences), but this digested version is never passed to
          EditContext.addPaletteNodesFromJSON.

          SCP_ForumATest.js: edited yet again:

          (Again, the bits of the code that i was changing are those which are
          depicted as commented out with // in the extreme left margin.)

          Code:
          test("persistingVisibility", function(assert){
          
              Fixture = {
          	editPane : isc.EditPane.create({}),
          	getLiveObjectArray : function() {
          	    return Fixture.editPane.getEditNodeTree().getChildren().map(function(x){return x.liveObject;});
          	},
          	serialize : function() {
          	    return Fixture.editPane.serializeAllEditNodesAsJSON(false);
          	},
          	neaten : function(string) {
          	    return JSON.stringify(JSON.parse(string)).replace(/{/g,"\n{").replace(/\"/g,"'");
          	}
              };
          
              ClassFactory.defineClass("SCP_CanvasA",isc.Canvas).addProperties({
          	initWidget : function(args) {
          	    this.Super("initWidget", arguments);
          	}
          //	,
          //	updateEditNode : function(editContext, editNode) {
          //	    editContext.setNodeProperties(editNode,{ visibility: this.visibility },true);
          //	}
              });
          
              ClassFactory.defineClass("SCP_LayoutA",isc.Layout).addProperties({
          	initWidget : function(args) {
          	    this.Super("initWidget", arguments);
          	    var canvasNode = Fixture.editPane.makeEditNode({type:"SCP_CanvasA",defaults:{
          //		visibility:"fake",
          		backgroundColor:"#070",width:90,height:90}});
          	    this.canvas = canvasNode.liveObject;
          	    this.addMember(this.canvas);
          	    Fixture.editPane.addNode(canvasNode,undefined,undefined,undefined,true);
          	    this.show();
          	},
          	showCanvas : function() {
          	    this.canvas.show();
          //	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.INHERIT},true);
          	},
          	hideCanvas : function() {
          	    this.canvas.hide();
          //	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.HIDDEN},true);
          	}
              });
          
              TestSequenceA = {
          
          	actionCreate : function(liveObjectArray) {
          	    var layoutNode = Fixture.editPane.makeEditNode({type:"SCP_LayoutA",defaults:{backgroundColor:"#700",width:100,height:100}});
          	    Fixture.editPane.addNode(layoutNode,undefined,undefined,undefined,true);
          	},
          
          	actionShow : function(liveObjectArray) {
          	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
          	    layout[0].showCanvas();
          	},
          
          	actionHide : function(liveObjectArray) {
          	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
          	    layout[0].hideCanvas();
          	},
          
          	checkShownState : function(liveObjectArray) {
          	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
          	    equal(layout[0].canvas.visibility,isc.Canvas.INHERIT,"canvas should be visible");
          	},
          
          	checkHiddenState : function(liveObjectArray) {
          	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
          	    equal(layout[0].canvas.visibility,isc.Canvas.HIDDEN,"canvas should be invisible");
          	}
              };
          
              var reincarnate = function(s11n) {
          	var s11nBefore = Fixture.neaten(Fixture.serialize());
          	equal(s11nBefore,"serialization before reincarnation" );
          	Fixture.editPane.destroyAll();
          	equal("[]",Fixture.neaten(Fixture.serialize()),"serialization during reincarnation" );
          	Fixture.editPane.addPaletteNodesFromJSON(s11n);
          	equal(s11nBefore,Fixture.neaten(Fixture.serialize()),"serialization after reincarnation should be as before" );
              };
          
              TestSequenceA.actionCreate		(Fixture.getLiveObjectArray());
              TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
          
              TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
              reincarnate				(Fixture.serialize()); // first
              TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
          
              TestSequenceA.actionHide		(Fixture.getLiveObjectArray());
          
              TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
              reincarnate				(Fixture.serialize()); // second
              TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
          
              TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
          
              TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
              reincarnate				(Fixture.serialize()); // third
              TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
          
          });
          Also, please find the following output files attached:

          - results.CONTROL.NONE.txt
          - results.LAZY.NONE.txt
          - results.EAGER.NONE.txt

          - results.CONTROL.FAKE.txt
          - results.LAZY.FAKE.txt
          - results.EAGER.FAKE.txt

          OBSERVATIONS and CONCLUSIONS:

          1. None of the possibilities actually resolve the failure of
          reincarnate() to preserve the state tested by checkHiddenState().

          2. I have convinced myself that having a default value for the
          visibility in the picture doesn't make any significant difference
          to the serializations produced, since all the differences that
          occur in the following diffs only differ by having or not having
          the fake value in the picture. So i'm happy to let that one rest.

          diff -u0 results.CONTROL.NONE.txt results.CONTROL.FAKE.txt
          diff -u0 results.LAZY.NONE.txt results.LAZY.FAKE.txt
          diff -u0 results.EAGER.NONE.txt results.EAGER.FAKE.txt

          3. The serialization that is produced after a reincarnation always
          contains one more copy of the SCP_CanvasA object than that produced
          before the reincarnation. I'm not sure if this is significant for
          the problem at hand, but it is plausible that the reconstructed
          system ends up being reconstructed from the wrong instance of
          SCP_CanvasA, and so the updated value of visibility gets lost in
          the process.

          I have a suspicion that perhaps this is because i haven't set up
          the objects i create to destroy themselves properly. So perhaps
          the EditContext.destroyAll() call triggers the destruction of the
          live SCP_LayoutA object, but this in turn fails to trigger the
          destruction of the SCP_CanvasA object that it contains.

          Could you confirm whether this is in fact a plausible cause of the
          problem, and, if so, how i should be setting up the destroy() or
          similar methods of the two classes to make sure a reincarnation
          triggers a proper destruction of all old liveObjects?
          Attached Files

          Comment


            #6
            And the last results file that wouldn't fit in your maximum 5 file upload limit :(
            Attached Files

            Comment


              #7
              In case you're not aware: we can't run this code, it seems to need some kind of test framework.

              It's also very synthetic code, totally unlike an application, and that makes it very difficult to understand what you're trying to accomplish and what you expect to happen, especially when we can't even run the code..

              In an attempt to somehow help despite this situation, let's instead take this sample:

              http://www.smartclient.com/index.jsp#automaticPersistence2

              Drag something out and then run this in the Developer Console:

              editPane.setNodeProperties(Canvas0.editNode, { visibility:"hidden" });
              echoFull(editPane.getSaveData());

              You'll that the "visibility" attribute appears in the data as expected, and "Destroy and Re-create" works as expected.

              To try to stretch to answer one of your other questions, again without the ability to run the code: yes, destroy() is recursive so destroying a Layout destroys all members.

              Comment


                #8
                Originally posted by Isomorphic View Post
                In case you're not aware: we can't run this code, it seems to need some kind of test framework.

                It's also very synthetic code, totally unlike an application, and that makes it very difficult to understand what you're trying to accomplish and what you expect to happen, especially when we can't even run the code..
                My apologies. I tried to make this a standalone runnable example, but i guess i dropped the ball with QUnit. Instead of using the SCP_ForumATest.html i posted previously, try using the one below in conjunction with the last SCP_ForumATest.js i posted. Instead of referring to local copies of the two files required for QUnit, this SCP_ForumATest.html refers to ones available on the net. So, provided you can arrange simultaneous access to the net and access to your own library files of the version i'm using (as mentioned in my first post), this should now work standalone.

                I agree that not being to able to run this code is a significant stumbling block to understanding it, and i hope this solves that problem, so that we can concentrate better on the problems at hand.

                SCP_ForumATest.html:
                Code:
                <!DOCTYPE html>
                <html>
                  <head>
                    <title>ForumATest</title>
                    <script>var isomorphicDir = "isomorphic/";</script>
                    <script src="isomorphic/system/modules-debug/ISC_Core.js"></script>
                    <script src="isomorphic/system/modules-debug/ISC_Foundation.js"></script>
                    <script src="isomorphic/system/modules-debug/ISC_Containers.js"></script>
                    <script src="isomorphic/system/modules-debug/ISC_Grids.js"></script>
                    <script src="isomorphic/system/modules-debug/ISC_Forms.js"></script>
                    <script src="isomorphic/system/modules-debug/ISC_DataBinding.js"></script>
                    <script src="isomorphic/system/modules-debug/ISC_Drawing.js"></script>
                    <script src="isomorphic/system/modules-debug/ISC_Charts.js"></script>
                    <script src="isomorphic/system/development/ISC_Tools.js"></script>
                  </head>
                  <body>
                    <div id="qunit" style="display:block"></div>
                    <div id="qunit-fixture" style="display:block"></div>
                    <script src="http://code.jquery.com/qunit/qunit-1.12.0.js"></script>
                    <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.12.0.css">
                    <script src="SCP_ForumATest.js"></script>
                  </body>
                </html>
                As far as your comment about the code being "very synthetic and totally unlike an application" is concerned, please indulge me in this. The code being written as it is illustrates some things i need to be able to do build the larger structure i'm building (for example, that some objects need to be created when others are initialized, or potentially as a result of method calls on others); it is not good enough to build a static structure up front, and work with that. Not only will our final system need to be able to create and destroy portlets dynamically, but also larger structures ("Dashboards") which each contain a PortalLayout as *part* of its widget hierarchy. I also illustrate the sort of testing i need to do to ensure that all the functionality we will need to have working is in fact working, and to ensure that arbitrary insertions of reincarnations always faithfully preserve all state on which this functionality depends. If you think it will be useful for me to post the current prototype i'm working on, in which i'm experiencing these problems with visibility, then i can do so, and you can see what i'm doing in a somewhat more realistic setting. But note that even this (being a prototype for now of a single Dashboard) is a small fraction of the final functionality that we will need to have working with your persistence mechanism.

                Comment


                  #9
                  Can you clarify what's going on in the real code - are you trying to manage visibility *of Portlets* (because this is automatically handled by PortalLayout, and so the code would be totally different), or of something *contained within a Portlet*?

                  Also, there's kind of a blizzard of information here - can you confirm that the only apparent misbehavior you're seeing is a seeming extra Canvas in the serialized data, and show exactly which lines are commented vs uncommented in the scenario that's misbehaving?

                  Comment


                    #10
                    Originally posted by Isomorphic View Post
                    Can you clarify what's going on in the real
                    code - are you trying to manage visibility *of Portlets* (because this
                    is automatically handled by PortalLayout, and so the code would be
                    totally different), or of something *contained within a
                    Portlet*?
                    The actual problem with the current prototype that provoked this post
                    is that the switching between the "panel" and "maximized" views of our
                    "dashboard" is broken by a reincarnation.

                    Our "dashboard" is a means of viewing portlets in two different views
                    between which the user can toggle. Our "panel view" consists of a
                    SmartClient PortalLayout, which contains SmartClient Portlets ("panel
                    view placeholders"). Our "maximized view" consists of a custom nested
                    SmartClient Layout structure (the "maximized layout") which also
                    contains SmartClient Portlets ("maximized view placeholders") and
                    another HLayout strip (the "thumbnail layout") which contains simple
                    SmartClient Canvases ("thumbnails").

                    When the user toggles the dashboard into panel view, the panel view
                    should become visible and the maximized and thumbnail layouts should
                    become invisible. When the user toggles the dashboard into maximized
                    view, the panel view should become invisible and the maximized and
                    thumbnail layouts should become visible. Each of our abstract
                    "portlets" has representatives in each of the three layouts (a "panel
                    view placeholder" somewhere in the "panel view", a "maximized view
                    placeholder" somewhere in the "maximized layout", and a "thumbnail"
                    somewhere in the "thumbnail layout").

                    This functionality works when there are no reincarnations in the
                    picture, but when a reincarnation is inserted, the visibility=hidden
                    status of either "panel view" or both "maximized layout" and
                    "thumbnail layout", is lost during a reincarnation, and all three
                    views end up being visible afterwards.

                    When the dashboard is toggled into maximized view (respectively, panel
                    view), each of the portlets also toggles view by moving the contents
                    of the abstract portlet from panel view (respectively, maximized view)
                    into maximized view (respectively, panel view) by moving the contents
                    of the panel view placeholder (respectively, maximized view
                    placeholder) SmartClient Portlet into the maximized view placeholder
                    (respectively, panel view placeholder) SmartClient Portlet.

                    The contents of SmartClient Portlets are switched like this because we
                    want the persistence mechanism to be able to preserve and reconstruct
                    *everything* about the grand dashboard (and eventually *collection of
                    dashboards*) layout including portlet contents (which will themselves
                    become very complicated and varied), and correctly preserve the
                    containment geometry of a portlet's placeholders in both panel view
                    and maximized layouts, irrespective of which state the (eventually,
                    each) dashboard is in when the reincarnation occurs.

                    I can post the code for this prototype, but it will take a bit of time
                    to make it self contained, and i'm hoping that this description will
                    suffice to convince you that it is indeed appropriate to work on the
                    specific problem highlighted by the example i have already posted.

                    Originally posted by Isomorphic View Post
                    Also, there's kind of a blizzard of
                    information here - can you confirm that the only apparent misbehavior
                    you're seeing is a seeming extra Canvas in the serialized data, and
                    show exactly which lines are commented vs uncommented in the scenario
                    that's misbehaving?
                    Well, *all* the scenarios are misbehaving, because in *none* of the
                    cases i tried did a reincarnation in hidden state preserve the hidden
                    state. But i'm not sure why this is the case. I also noticed that in
                    all the cases, any reincarnation adds an anomalous extra canvas into
                    the serialization, and i wonder whether this correlation is hinting at
                    some form of causation. Maybe it is (in which case understanding how
                    i should be insuring that a call of destroy on the layout also causes
                    a call of destroy on the canvas and fixing that might make the
                    anomalous extra canvas and the original problem go away). Or maybe it
                    isn't, the anomalous extra canvas is indeed an innocuous red herring,
                    and the original problem remains and is caused by something else.

                    PS: i am working in the UTC+2 timezone, but i have arranged to work
                    1900Wed-0200Thu, 1900Thu-0200Fri, 1900Fri-0200Sat my time, so that
                    i can be online interacting with you better during Wed, Thu, Fri
                    1000-1700 your time (i understand UTC-7 to be your timezone). i
                    think if we can be better synchronized like this, then i don't
                    have to resort to writing essays to get some mileage out of our
                    interactions, and we can get to the bottom of issues and
                    misunderstandings quicker. i hope you will be suitably available
                    to take advantage of this arrangement.

                    Comment


                      #11
                      i'm available...

                      Comment


                        #12
                        i'm still available...until 17:00 your time

                        Comment


                          #13
                          From what you've shared, in the real system, there are no extra elements being introduced as in your test harness (which may well turn out to just be a bug in the harness).

                          Have you tried simply calling setNodeProperties() in the real system? As we previously indicated, the fact that the visibility setting is not persisted is exactly what would be expected if you were not making this call.

                          Comment


                            #14
                            Originally posted by Isomorphic View Post
                            From what you've shared, in the real system, there are no extra elements being introduced as in your test harness (which may well turn out to just be a bug in the harness).

                            Have you tried simply calling setNodeProperties() in the real system? As we previously indicated, the fact that the visibility setting is not persisted is exactly what would be expected if you were not making this call.
                            I'm not sure i understand what you mean by "there are no extra elements being introduced as in your test harness". The test example that i have posted is very similar in nature to the prototype i'm building (which i assume is what you're referring to as "the real system"), except that the prototype is the more complicated structure i described previously. Specifically, it is tested in the same way, using the same sort of testing harness, but with different actions appropriate to its structure and more elaborate state checks.

                            I have not tested the idea of using setNodeProperties() in the prototype yet because i figured that if the attempts to use it in the simpler test system, which i *have* made, don't work, then we can't expect by some miracle that they will work on a more complex system of the same nature. What i have tried to demonstrate is a minimal (or close to minimal) example which manifests a problem. I have tried two variations on your suggested remedy to that problem (the LAZY and EAGER tests), and neither of them eliminate the problem. They do affect the serialization, in the sense of putting visibility properties into it where they weren't before, but when that property takes on the value of "hidden", the fact of it having done so is somehow destroyed during the reincarnation process.

                            Just to be quite clear:

                            If you run the NONE test, with the code exactly as quoted below, then the reincarnate in hidden state breaks the functionality and there are no visibility properties in the serializations:
                            Code:
                            test("persistingVisibility", function(assert){
                                
                                Fixture = {
                            	editPane : isc.EditPane.create({}),
                            	getLiveObjectArray : function() {
                            	    return Fixture.editPane.getEditNodeTree().getChildren().map(function(x){return x.liveObject;});
                            	},
                            	serialize : function() {
                            	    return Fixture.editPane.serializeAllEditNodesAsJSON(false);
                            	},
                            	neaten : function(string) {
                            	    return JSON.stringify(JSON.parse(string)).replace(/{/g,"\n{").replace(/\"/g,"'");
                            	}
                                };
                                
                                ClassFactory.defineClass("SCP_CanvasA",isc.Canvas).addProperties({
                            	initWidget : function(args) {
                            	    this.Super("initWidget", arguments);
                            	}
                            //	,
                            //	updateEditNode : function(editContext, editNode) {
                            //	    editContext.setNodeProperties(editNode,{ visibility: this.visibility },true);
                            //	}
                                });
                                
                                ClassFactory.defineClass("SCP_LayoutA",isc.Layout).addProperties({
                            	initWidget : function(args) {
                            	    this.Super("initWidget", arguments);
                            	    var canvasNode = Fixture.editPane.makeEditNode({type:"SCP_CanvasA",defaults:{
                            //		visibility:"fake",
                            		backgroundColor:"#070",width:90,height:90}});
                            	    this.canvas = canvasNode.liveObject;
                            	    this.addMember(this.canvas);
                            	    Fixture.editPane.addNode(canvasNode,undefined,undefined,undefined,true);
                            	    this.show();
                            	},
                            	showCanvas : function() {
                            	    this.canvas.show();
                            //	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.INHERIT},true);
                            	},
                            	hideCanvas : function() {
                            	    this.canvas.hide();
                            //	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.HIDDEN},true);
                            	}
                                });
                                
                                TestSequenceA = {
                            	
                            	actionCreate : function(liveObjectArray) {
                            	    var layoutNode = Fixture.editPane.makeEditNode({type:"SCP_LayoutA",defaults:{backgroundColor:"#700",width:100,height:100}});
                            	    Fixture.editPane.addNode(layoutNode,undefined,undefined,undefined,true);
                            	},
                            	
                            	actionShow : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    layout[0].showCanvas();
                            	},
                            	
                            	actionHide : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    layout[0].hideCanvas();
                            	},
                            	
                            	checkShownState : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    equal(layout[0].canvas.visibility,isc.Canvas.INHERIT,"canvas should be visible");
                            	},
                            	
                            	checkHiddenState : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    equal(layout[0].canvas.visibility,isc.Canvas.HIDDEN,"canvas should be invisible");
                            	}
                                };
                                
                                var reincarnate = function(s11n) {
                            	var s11nBefore = Fixture.neaten(Fixture.serialize());
                            	equal(s11nBefore,"serialization before reincarnation" );
                            	Fixture.editPane.destroyAll();
                            	equal("[]",Fixture.neaten(Fixture.serialize()),"serialization during reincarnation" );
                            	Fixture.editPane.addPaletteNodesFromJSON(s11n);
                            	equal(s11nBefore,Fixture.neaten(Fixture.serialize()),"serialization after reincarnation should be as before" );
                                };
                                
                                TestSequenceA.actionCreate		(Fixture.getLiveObjectArray());
                                TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // first
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.actionHide		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // second
                                TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // third
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                
                            });
                            If you run the LAZY test, with the code exactly as quoted below (uncommenting SCP_CanvasA.updateEditNode from the NONE scenario), then visibility properties appear in the serializations, *but their appearance doesn't fix the problem*; a reincarnate in hidden state still breaks the functionality.

                            Code:
                            test("persistingVisibility", function(assert){
                                
                                Fixture = {
                            	editPane : isc.EditPane.create({}),
                            	getLiveObjectArray : function() {
                            	    return Fixture.editPane.getEditNodeTree().getChildren().map(function(x){return x.liveObject;});
                            	},
                            	serialize : function() {
                            	    return Fixture.editPane.serializeAllEditNodesAsJSON(false);
                            	},
                            	neaten : function(string) {
                            	    return JSON.stringify(JSON.parse(string)).replace(/{/g,"\n{").replace(/\"/g,"'");
                            	}
                                };
                                
                                ClassFactory.defineClass("SCP_CanvasA",isc.Canvas).addProperties({
                            	initWidget : function(args) {
                            	    this.Super("initWidget", arguments);
                            	}
                            	,
                            	updateEditNode : function(editContext, editNode) {
                            	    editContext.setNodeProperties(editNode,{ visibility: this.visibility },true);
                            	}
                                });
                                
                                ClassFactory.defineClass("SCP_LayoutA",isc.Layout).addProperties({
                            	initWidget : function(args) {
                            	    this.Super("initWidget", arguments);
                            	    var canvasNode = Fixture.editPane.makeEditNode({type:"SCP_CanvasA",defaults:{
                            //		visibility:"fake",
                            		backgroundColor:"#070",width:90,height:90}});
                            	    this.canvas = canvasNode.liveObject;
                            	    this.addMember(this.canvas);
                            	    Fixture.editPane.addNode(canvasNode,undefined,undefined,undefined,true);
                            	    this.show();
                            	},
                            	showCanvas : function() {
                            	    this.canvas.show();
                            //	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.INHERIT},true);
                            	},
                            	hideCanvas : function() {
                            	    this.canvas.hide();
                            //	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.HIDDEN},true);
                            	}
                                });
                                
                                TestSequenceA = {
                            	
                            	actionCreate : function(liveObjectArray) {
                            	    var layoutNode = Fixture.editPane.makeEditNode({type:"SCP_LayoutA",defaults:{backgroundColor:"#700",width:100,height:100}});
                            	    Fixture.editPane.addNode(layoutNode,undefined,undefined,undefined,true);
                            	},
                            	
                            	actionShow : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    layout[0].showCanvas();
                            	},
                            	
                            	actionHide : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    layout[0].hideCanvas();
                            	},
                            	
                            	checkShownState : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    equal(layout[0].canvas.visibility,isc.Canvas.INHERIT,"canvas should be visible");
                            	},
                            	
                            	checkHiddenState : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    equal(layout[0].canvas.visibility,isc.Canvas.HIDDEN,"canvas should be invisible");
                            	}
                                };
                                
                                var reincarnate = function(s11n) {
                            	var s11nBefore = Fixture.neaten(Fixture.serialize());
                            	equal(s11nBefore,"serialization before reincarnation" );
                            	Fixture.editPane.destroyAll();
                            	equal("[]",Fixture.neaten(Fixture.serialize()),"serialization during reincarnation" );
                            	Fixture.editPane.addPaletteNodesFromJSON(s11n);
                            	equal(s11nBefore,Fixture.neaten(Fixture.serialize()),"serialization after reincarnation should be as before" );
                                };
                                
                                TestSequenceA.actionCreate		(Fixture.getLiveObjectArray());
                                TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // first
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.actionHide		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // second
                                TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // third
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                
                            });
                            If you run the EAGER test, with the code exactly as quoted below (uncommenting setNodeProperties calls in SCP_LayoutA.hideCanvas and SCP_LayoutA.showCanvas from the NONE scenario), then visibility properties again appear in the serializations, *but their appearance again doesn't fix the problem*; a reincarnate in hidden state still breaks the functionality.

                            Code:
                            test("persistingVisibility", function(assert){
                                
                                Fixture = {
                            	editPane : isc.EditPane.create({}),
                            	getLiveObjectArray : function() {
                            	    return Fixture.editPane.getEditNodeTree().getChildren().map(function(x){return x.liveObject;});
                            	},
                            	serialize : function() {
                            	    return Fixture.editPane.serializeAllEditNodesAsJSON(false);
                            	},
                            	neaten : function(string) {
                            	    return JSON.stringify(JSON.parse(string)).replace(/{/g,"\n{").replace(/\"/g,"'");
                            	}
                                };
                                
                                ClassFactory.defineClass("SCP_CanvasA",isc.Canvas).addProperties({
                            	initWidget : function(args) {
                            	    this.Super("initWidget", arguments);
                            	}
                            //	,
                            //	updateEditNode : function(editContext, editNode) {
                            //	    editContext.setNodeProperties(editNode,{ visibility: this.visibility },true);
                            //	}
                                });
                                
                                ClassFactory.defineClass("SCP_LayoutA",isc.Layout).addProperties({
                            	initWidget : function(args) {
                            	    this.Super("initWidget", arguments);
                            	    var canvasNode = Fixture.editPane.makeEditNode({type:"SCP_CanvasA",defaults:{
                            //		visibility:"fake",
                            		backgroundColor:"#070",width:90,height:90}});
                            	    this.canvas = canvasNode.liveObject;
                            	    this.addMember(this.canvas);
                            	    Fixture.editPane.addNode(canvasNode,undefined,undefined,undefined,true);
                            	    this.show();
                            	},
                            	showCanvas : function() {
                            	    this.canvas.show();
                            	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.INHERIT},true);
                            	},
                            	hideCanvas : function() {
                            	    this.canvas.hide();
                            	    Fixture.editPane.setNodeProperties(this.canvas.editNode,{visibility:isc.Canvas.HIDDEN},true);
                            	}
                                });
                                
                                TestSequenceA = {
                            	
                            	actionCreate : function(liveObjectArray) {
                            	    var layoutNode = Fixture.editPane.makeEditNode({type:"SCP_LayoutA",defaults:{backgroundColor:"#700",width:100,height:100}});
                            	    Fixture.editPane.addNode(layoutNode,undefined,undefined,undefined,true);
                            	},
                            	
                            	actionShow : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    layout[0].showCanvas();
                            	},
                            	
                            	actionHide : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    layout[0].hideCanvas();
                            	},
                            	
                            	checkShownState : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    equal(layout[0].canvas.visibility,isc.Canvas.INHERIT,"canvas should be visible");
                            	},
                            	
                            	checkHiddenState : function(liveObjectArray) {
                            	    var layout = liveObjectArray.filter(function(x){ return x.isA("SCP_LayoutA"); });
                            	    equal(layout[0].canvas.visibility,isc.Canvas.HIDDEN,"canvas should be invisible");
                            	}
                                };
                                
                                var reincarnate = function(s11n) {
                            	var s11nBefore = Fixture.neaten(Fixture.serialize());
                            	equal(s11nBefore,"serialization before reincarnation" );
                            	Fixture.editPane.destroyAll();
                            	equal("[]",Fixture.neaten(Fixture.serialize()),"serialization during reincarnation" );
                            	Fixture.editPane.addPaletteNodesFromJSON(s11n);
                            	equal(s11nBefore,Fixture.neaten(Fixture.serialize()),"serialization after reincarnation should be as before" );
                                };
                                
                                TestSequenceA.actionCreate		(Fixture.getLiveObjectArray());
                                TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // first
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.actionHide		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // second
                                TestSequenceA.checkHiddenState	(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.actionShow		(Fixture.getLiveObjectArray());
                                
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                reincarnate				(Fixture.serialize()); // third
                                TestSequenceA.checkShownState	(Fixture.getLiveObjectArray());
                                
                            });

                            Comment


                              #15
                              Here's your duplicate canvas issue:

                              1. Create Layout
                              2. Layout creates Canvas
                              3. Serialize
                              4. Reincarnate
                              5. Reincarnate creates Canvas
                              6. Reincarnate creates Layout
                              7. Layout creates a new Canvas

                              Thus you get 2 canvases.

                              Note also that your Layout child canvas is added into the editContext at the same level as the Layout and therefore it will not automatically be reincarnated (your terminology) into the Layout members. To do that you will have to specify a parent to your addNode but that's not possible within initWidget because the editNode is not yet created at that point for the layout.

                              So there are a few issues to be worked out with the test cases before dealing with the visible property.

                              Comment

                              Working...
                              X