Congratulations!
Fritz
On Thu, 29 Aug 2013, Gregory Beaver wrote:
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);
}
}
});
John Spackman <mailto:[email protected]>
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]
<mailto:[email protected]>>
Reply-To: qooxdoo Development <[email protected]
<mailto:[email protected]>>
Date: Thursday, 29 August 2013 14:01
To: Joaquín Fernández <[email protected]
<mailto:[email protected]>>, qooxdoo Development
<[email protected]
<mailto:[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]
<mailto:[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_______________________________________________
<http://pubads.g.doubleclick.net/gampad/clk?id=58040911&iu=/4140/ostg.clktrk_______________________________________________>
qooxdoo-devel mailing list [email protected]
<mailto:[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
Gregory Beaver <mailto:[email protected]>
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]
<mailto:[email protected]>> wrote:
--
Oetiker+Partner AG tel: +41 62 775 9903 (direct)
Fritz Zaucker +41 62 775 9900 (switch board)
Aarweg 15 +41 79 675 0630 (mobile)
CH-4600 Olten fax: +41 62 775 9905
Schweiz web: www.oetiker.ch
------------------------------------------------------------------------------
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