1

A beginner question. I'm trying to put up a famo.us app. So far, I had nice results on a simple experiment.

Now that my app is growing, I want to allow moving between "screens" : landing screen -> info screen -> back to landing screen -> etc.

My "screens" are View() with surfaces, modifiers, etc. added to the View()

I use RenderController() to switch between screens, with its hide() and show() functions.

The problem is that when going back to a previously used screen, surfaces no longer show up !

Code : var app_render_controller = new RenderController(); app_render_controller.show(landing_screen); // ok app_render_controller.hide(landing_screen); // ok app_render_controller.show(info_screen); // ok app_render_controller.hide(info_screen); // ok app_render_controller.show(landing_screen); // NOK -> I only see my background surface !

When examining the DOM, I'm able to see the outline of my surfaces at the place they should be, but they are hidden. It seems the background surface is now in front of the content surfaces (a ScrollView). If I hide the background, I see the Scrollview again.

I read that the overlap of elements was corresponding to the order they were added to the rendering tree. So since the tree under my View()-based "screen" hasn't changed, the Scrollview should still be drawn on top of the background like the 1st time ?

Is this expected ? Am I doing something wrong ? Am I missinq something ?

------- edit ------

my code (roughly) :

var main_context = Engine.createContext();

var root_render_controller = new RenderController();
main_context.add(root_render_controller);

var landing_screen = new View();
landing_screen.add(new Surface(...background texture...));
var scrollview = new Scrollview();
landing_screen.add(scrollview);
// (add stuff to the scrollview)

var info_screen = new View();
info_screen.add(new Surface(...background texture...));
info_screen.add(new Surface(...some message...));

root_render_controller.show(landing_screen);
// display is fine
root_render_controller.hide(landing_screen);
root_render_controller.show(info_screen);
// display is fine
root_render_controller.hide(info_screen);
root_render_controller.show(landing_screen); // again
// display is wrecked : the background surface is now hiding the scrollview !

When looking at the DOM, we can see the flip :

1st display of the landing screen : background then the scrollview and its 6 elements

1st display of the landing screen : background then the scrollview and its 6 elements

2nd display of the landing screen : scrollview and its 6 elements then the background !

2nd display of the landing screen : scrollview and its 6 elements then the background

Offirmo
  • 18,962
  • 12
  • 76
  • 97

3 Answers3

1

I read that the overlap of elements was corresponding to the order they were added to the rendering tree

From what I've seen, overlap of elements is set by the inverse of the order of creation (e.g., last one on top), not addition to the render tree.

When a Surface is created, it is registered with Entity: https://github.com/Famous/core/blob/master/Surface.js#L60

function Surface(options) {
   ...
    this.id = Entity.register(this);
   ...
}

When changes on a RenderNode are committed, they are applied based on the creation order (as Object.keys returns indices from an array back in numerical order): https://github.com/Famous/core/blob/master/RenderNode.js#L106

function _applyCommit(spec, context, cacheStorage) {
    var result = SpecParser.parse(spec, context);
    var keys = Object.keys(result);
    for (var i = 0; i < keys.length; i++) {
        var id = keys[i];
        var childNode = Entity.get(id);
    ....
}

You can test this out by creating two Surfaces and adding the one you created second to a Context before the first. It will still be on top:

define(function(require, exports, module) {
  var Engine = require("famous/core/Engine");
  var Surface = require("famous/core/Surface");

  var mainContext = Engine.createContext();

  var surface1 = new Surface({
    size: [200, 200],
    content: "Red",
    properties: {
      lineHeight: "200px",
      textAlign: "center",
      backgroundColor: "#f00"
    }
  });

  var surface2 = new Surface({
    size: [200, 200],
    content: "Green",
    properties: {
      lineHeight: "200px",
      textAlign: "center",
      backgroundColor: "#0f0"
    }
  });

  // Doesn't matter what order you add these - surface2 will be on top
  mainContext.add(surface2);
  mainContext.add(surface1);
});

Hopefully that gives you a good place to start looking. It would help if you shared where your background is added to the Context to get a sense of why it is now in front.

aelr
  • 375
  • 1
  • 7
  • ok, last one on top, that's logical. The initial show() is fine. Now why does it flip on the second show() ? – Offirmo May 10 '14 at 22:44
1

It looks like you've found a bug in RenderController. The simple example below is exhibiting the same behavior you described. I've filed it against their repo at https://github.com/Famous/views/issues/42.

Note: If you don't hide the current view before showing the next, you get the same behavior. Interestingly, if you wait between the hide/show operations, you see different incorrect behavior.

define(function(require, exports, module) {
  var Engine = require("famous/core/Engine");
  var Surface = require("famous/core/Surface");
  var View = require("famous/core/View");
  var RenderController = require("famous/views/RenderController");
  var Timer = require("famous/utilities/Timer");

  var mainContext = Engine.createContext();

  var renderController = new RenderController();

  mainContext.add(renderController);

  var view1 = new View();
  var view2 = new View();

  var surface1 = new Surface({
    size: [200, 200],
    content: "Red Background",
    properties: {
      lineHeight: "200px",
      textAlign: "center",
      backgroundColor: "#f00"
    }
  });

  var surface2 = new Surface({
    size: [200, 200],
    content: "Green Foreground",
    properties: {
      lineHeight: "200px",
      textAlign: "center",
      backgroundColor: "#0f0"
    }
  });

  var surface3 = new Surface({
    size: [200, 200],
    content: "Blue Background",
    properties: {
      lineHeight: "200px",
      textAlign: "center",
      backgroundColor: "#00f"
    }
  });

  var surface4 = new Surface({
    size: [200, 200],
    content: "Black Foreground",
    properties: {
      lineHeight: "200px",
      textAlign: "center",
      backgroundColor: "#000",
      color: "white"
    }
  });

  view1.add(surface1);
  view1.add(surface2);

  view2.add(surface3);
  view2.add(surface4);

  // If performing hide/show on same tick (or not hiding before show),
  //  you get green-fg, black-fg, red-bg (wrong)
  renderController.show(view1);
  Timer.setTimeout(function() {
    renderController.hide(view1); // <- commenting this out results in same behavior
    renderController.show(view2);
    Timer.setTimeout(function() {
      renderController.hide(view2); // <- commenting this out results in same behavior
      renderController.show(view1);
    }, 1000);
  }, 1000);

// If waiting between each hide/show
//  you get green-fg, blue-bg (wrong), green-fg
//  renderController.show(view1);
//  Timer.setTimeout(function() {
//    renderController.hide(view1);
//    Timer.setTimeout(function() {
//      renderController.show(view2);
//      Timer.setTimeout(function() {
//        renderController.hide(view2);
//        Timer.setTimeout(function() {
//          renderController.show(view1);
//        }, 1000)
//      }, 1000)
//    }, 1000)
//  }, 1000)


});
aelr
  • 375
  • 1
  • 7
  • Thaks a lot for taking the time to make a smallest-reproduction-code and filing the issue. I see there is a debate on whether it's a bug or not... I'll wait a bit for the outcome. – Offirmo May 12 '14 at 00:12
1

A View has no idea the order in which Surfaces should be layered in. When something is hidden with a RenderController it frees up the document fragment that the Surface is managing. When that View is brought back into the scene graph, the Surfaces get a document fragment to fill with its content from the pool of unused elements if any exist, if the pool is empty, it creates new ones.

Due to the way that browsers implement layering, newly created DOM nodes exist on top of old DOM nodes if they exist at the same transform in Z and have the same CSS zIndex. You should not be relying on the creation order if you need explicit layering. Try getting around this by either adding Modifiers to transform them in z-space or add explicit CSS zIndexing to denote what your layers should be.

MikeOB
  • 106
  • 6