It suddenly occurred to me this evening, well after I had given up and erased my code, that there is a simple and elegant solution to my problems with using a single qx.ui.list.List and swapping the model to avoid all the headaches with incorrect selection:

qx.ui.container.Stack

So, I whipped up a new proof-of-concept widget that creates 3 qx.ui.list.List objects, 1 for composers, 1 for pieces, and 1 for movements.  As of right now, it literally worked on the first try after I fixed the expected typo bugs and minor omissions.

So for anyone struggling with using a qx.ui.list.List with changing model and selection issues, use more than one of the things with a stack and your problem disappears.  You do lose some memory efficiency, but it's a pretty small footprint since the data will be re-used.  Here is the actual code (minus the guess box stuff, which is irrelevant to this discussion)

/**
 * programinfo widget for complex program handling
 */
qx.Class.define("qooxdoo.Widgets.SimpleProgram", {
  extend: qx.ui.core.Widget,
  include: [qx.ui.form.MModelProperty],

  properties :
  {
    appearance :
    {
      refine : true,
      init : "simpleprogram"
    }
  },

  events :
  {
    removeProgram : "qx.event.type.Data",
  },

  construct: function()
  {
    this.base(arguments);
    this._setLayout(new qx.ui.layout.VBox(5));
    this._add(this._createChildControl("erasebutton"));
    this._add(this._createChildControl("guessbox"));
    this._add(this._createChildControl("composerpiece"));
  },
  members: {
    _stack : null,
    _composerList: null,
    _pieceList: null,
    _movementsList: null,
    _makeEraseButton: function(id)
    {
      var control;
      control = new qx.ui.form.Button;
      control.addListener("execute", function() {
        this.fireDataEvent("removeProgram", this);
      }, this);
      control.setAppearance("programinfo/erasebutton");
      return control;
    },
    _makeComposerPiece: function(id)
    {
      var control;
      control = new qx.ui.container.Stack();
      this._stack = control;
      control.add(this.getChildControl("composer"));
      control.add(this.getChildControl("piece"));
      control.add(this.getChildControl("movements"));
      return control;
    },
    _makeComposer: function(id)
    {
      var control = new qx.ui.list.List();
      control.setLabelPath("title");
      control.setModel(qx.core.Init.getApplication().getRepModel().getKids());
      this._composerList = control;
      return control;
    },
    _makePiece: function(id)
    {
      var control = new qx.ui.list.List();
      control.setLabelPath("title");
      this._pieceList = control;
      return control;
    },
    _makeMovements: function(id)
    {
      var control = new qx.ui.list.List().set({
        labelPath: "title",
        selectionMode: "additive"
      });
      control.setLabelPath("title");
      this._movementList = control;
      return control;
    },
    _loadPieces: function() {
      this._pieceList.setModel(this._composerList.getSelection().getItem(0).getKids());
      this._stack.setSelection([this._pieceList]);
      for (var i = 0; i < this._composerList.getSelection().getItem(0).getKids().getLength(); i++) {
        var piece = this._composerList.getSelection().getItem(0).getKids().getItem(i);
        // try to head off some of the bullshit at the pass
        piece.lazyLoadKids();
      }
    },
    _switchMovements: function() {
      this._movementList.setModel(this._pieceList.getSelection().getItem(0).getKids());
      for (var i = 0; i < this._pieceList.getSelection().getItem(0).getKids().getLength(); i++) {
        var movement = this._pieceList.getSelection().getItem(0).getKids().getItem(i);
        this.getModel().getMovements().push(movement.getId());
        this._movementList.getSelection().push(movement);
      }
      this._stack.setSelection([this._movementList]);
    },
    _createChildControlImpl: function(id)
    {
      var control;
      if (id == "erasebutton") {
        control = this._makeEraseButton(id);
        this._add(control);
      } else if (id == "guessbox") {
        control = new qx.ui.form.TextField();
      } else if (id == "composerpiece") {
        control = this._makeComposerPiece(id);
        this._add(control);
      } else if (id == "composer") {
        control = this._makeComposer(id);
        control.getSelection().addListener("change", function(e) {
          this.getModel().setMovements(new qx.data.Array());
          this.getModel().setPiece(0);
          this.getModel().getComposer().setId(e.getData());
          if (this._composerList.getSelection().getItem(0).getTitle() == "Loading...") {
            var composer = this._composerList.getSelection().getItem(0);
            composer.lazyLoadKids();
            composer.addListenerOnce("changeKids", this._loadPieces, this);
          } else {
            this._loadPieces();
          }
        }, this);
      } else if (id == "piece") {
        control = this._makePiece(id);
        control.getSelection().addListener("change", function(e) {
          this.getModel().setMovements(new qx.data.Array());
          this.getModel().setPiece(e.getData());
          this._switchMovements();
        }, this);
      } else if (id == "movements") {
        control = this._makeMovements(id);
      }
      return control || this.base(arguments, id);
    }
  }
});
 


August 29, 2013 8:19 AM
Just a thought for when you get back to it then – is there a chance that two bits of code were trying to change the model(s) at the same time?  I have had something similar where recursive binding can cause conflicts and the wrong things get added/removed - e.g. An event changes the model which causes an event which indirectly causes a change to the model etc.  Have you tried setting a long timeout (e.g. a second or two) before adjusting the model?

John

From: Gregory Beaver <[email protected]>
Reply-To: qooxdoo Development <[email protected]>
Date: Thursday, 29 August 2013 14:01
To: Joaquín Fernández <[email protected]>, qooxdoo Development <[email protected]>
Subject: Re: [qooxdoo-devel] Frustrating development

Ack!  Accidentally clicked send.

I have a database of composers, the pieces they wrote and the movements of those pieces.  It is sent to qooxdoo as Json, and using a Json marshal made into 3 classes.  The kids property in composers stores the array of pieces, and the same is true for the kids property of pieces, which stores an array of movements.

I use this to render a tree in one tab of the backend, and I can add composers, pieces and movements as needed.  However, because the data set is so large, I do some lazy loading.  At first, each composer's only child node is a piece called "Loading..."  When the code sees it, it lazy loads the children and updates the display.  All this works perfectly for years and still works.

In the tab where I edit concert info, I am re-using the data from the composers/pieces/movements tab to provide the source to choose program info.  I created a widget years ago that allows clicking a composer from a selectbox qx.ui.form.List, which populates another selectbox with the composer's pieces, lazy loading as necessary, and then chooses movements, lazy loading as necessary.

This process is too slow, it takes me hours because there is a subtle bug where selecting the movements does not actually work unless I de-select them and re-select them.  Also, scrolling through the list of composers and pieces is too slow.

Thus I had the idea of instead using qx.ui.list.List.  Coupling this with a text field and using that to filter the list.  So if we are playing Beethoven String Quartet Op. 59 No. 2, I could type "bee" and it will filter the list of 115 composers to just Beethoven.  I click Beethoven and the qx.ui.list.List model changes to the pieces he wrote and the text field changes to "Beethoven, Ludwig can ".  I type "59" after that and it filters to the 3 Op. 59 quartets he wrote.  I click 59 No. 2 and the text field changes to "Beethoven, Ludwig van String Quartet in E minor Op. 59 No. 2" and disables itself.  Meanwhile, the list updates to the 4 movements and I can deselect any we are not playing on that concert.

All of this worked except when I clicked Beethoven, the widget remained selected after the model change and often the movements would not display properly sometimes, with no clear cause-effect, implying an asynchronous issue of some sort.

I pulled out the specific code, and the bug disappeared.  So, there is something about the interaction of the larger code and the smaller that triggers the bug.  This is why I erased the whole thing.  It needs a smarter re-imagining before I can restart it, and as I said, my available development time was this week.  I won't have another window for a month or so.

If you have read this far, thank you!

Thank you also for the quick and calmly measured responses.  Much appreciated

Greg



On Aug 29, 2013, at 7:44, Gregory Beaver <[email protected]> wrote:

------------------------------------------------------------------------------ Learn the latest--Visual Studio 2012, SharePoint 2013, SQL 2012, more! Discover the easy way to master current and previous Microsoft technologies and advance your career. Get an incredible 1,500+ hours of step-by-step tutorial videos with LearnDevNow. Subscribe today and save! http://pubads.g.doubleclick.net/gampad/clk?id=58040911&iu=/4140/ostg.clktrk_______________________________________________ qooxdoo-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
------------------------------------------------------------------------------
Learn the latest--Visual Studio 2012, SharePoint 2013, SQL 2012, more!
Discover the easy way to master current and previous Microsoft technologies
and advance your career. Get an incredible 1,500+ hours of step-by-step
tutorial videos with LearnDevNow. Subscribe today and save!
http://pubads.g.doubleclick.net/gampad/clk?id=58040911&iu=/4140/ostg.clktrk
_______________________________________________
qooxdoo-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
August 29, 2013 8:01 AM
Ack!  Accidentally clicked send.

I have a database of composers, the pieces they wrote and the movements of those pieces.  It is sent to qooxdoo as Json, and using a Json marshal made into 3 classes.  The kids property in composers stores the array of pieces, and the same is true for the kids property of pieces, which stores an array of movements.

I use this to render a tree in one tab of the backend, and I can add composers, pieces and movements as needed.  However, because the data set is so large, I do some lazy loading.  At first, each composer's only child node is a piece called "Loading..."  When the code sees it, it lazy loads the children and updates the display.  All this works perfectly for years and still works.

In the tab where I edit concert info, I am re-using the data from the composers/pieces/movements tab to provide the source to choose program info.  I created a widget years ago that allows clicking a composer from a selectbox qx.ui.form.List, which populates another selectbox with the composer's pieces, lazy loading as necessary, and then chooses movements, lazy loading as necessary.

This process is too slow, it takes me hours because there is a subtle bug where selecting the movements does not actually work unless I de-select them and re-select them.  Also, scrolling through the list of composers and pieces is too slow.

Thus I had the idea of instead using qx.ui.list.List.  Coupling this with a text field and using that to filter the list.  So if we are playing Beethoven String Quartet Op. 59 No. 2, I could type "bee" and it will filter the list of 115 composers to just Beethoven.  I click Beethoven and the qx.ui.list.List model changes to the pieces he wrote and the text field changes to "Beethoven, Ludwig can ".  I type "59" after that and it filters to the 3 Op. 59 quartets he wrote.  I click 59 No. 2 and the text field changes to "Beethoven, Ludwig van String Quartet in E minor Op. 59 No. 2" and disables itself.  Meanwhile, the list updates to the 4 movements and I can deselect any we are not playing on that concert.

All of this worked except when I clicked Beethoven, the widget remained selected after the model change and often the movements would not display properly sometimes, with no clear cause-effect, implying an asynchronous issue of some sort.

I pulled out the specific code, and the bug disappeared.  So, there is something about the interaction of the larger code and the smaller that triggers the bug.  This is why I erased the whole thing.  It needs a smarter re-imagining before I can restart it, and as I said, my available development time was this week.  I won't have another window for a month or so.

If you have read this far, thank you!

Thank you also for the quick and calmly measured responses.  Much appreciated

Greg



On Aug 29, 2013, at 7:44, Gregory Beaver <[email protected]> wrote:

------------------------------------------------------------------------------
Learn the latest--Visual Studio 2012, SharePoint 2013, SQL 2012, more!
Discover the easy way to master current and previous Microsoft technologies
and advance your career. Get an incredible 1,500+ hours of step-by-step
tutorial videos with LearnDevNow. Subscribe today and save!
http://pubads.g.doubleclick.net/gampad/clk?id=58040911&iu=/4140/ostg.clktrk
_______________________________________________
qooxdoo-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel

Reply via email to