Gluon Connect DataProvider.retrieveList results in IllegalStateException - gluon

Following code produces an IllegalStateException - not on FX application Thread - .
private void populateListView(){
GluonObservableList<MyClass> items = DataProvider.retrieveList(restClient.createListDataReader(MyClass.class));
listview.setItems(items);
}
I guess the reason for this is that the code which adds the elements from the Iterator to the GluonObservableList is not wrapped in Platform.runLater(), despite to the other calls in retrieveList()?

Updating a UI element (such as listview) must be done from the FX Application Thread.
If populateListView() is called from a background thread, the observableList updates in the background causing the listview to try to update from the background.
Setting the observableList to the listview using Platform.runLater occurs on the FX Thread but the observableList still updates in the background after it is loaded to the listview.
new Thread(()->{
GluonObservableList<MyClass> items = DataProvider.retrieveList(restClient.createListDataReader(MyClass.class));
//Option 1
//listview.setItems(items);
//Option 2
//Platform.runLater(()->listview.setItems(items));
//Option 3
//items.initializedProperty().addListener((obv,ov,nv)->{
// listview.setItems(items);
//});
//Option 4
items.stateProperty().addListener((obvs,ovs,nvs)->{
if (nvs.equals(ConnectState.SUCCEEDED)) {
listview.setItems(items);
}else if(nvs.equals(ConnectState.FAILED)){
MobileApplication.getInstance().showMessage("Rest API request failed");
}
});
}).start();
Option 1 and Option 2 update the listview before data is loaded to the observableList. Option 1 and 2 throw multiple exceptions (and option 1 is just ugly).
Both option 3 and option 4 trigger the listview update after the observableList has been loaded and are handled on the FX Application Thread.
Alternatively, you can wrap any call to populateListView() in Platform.runLater

Related

(GMS2) Spawner object doesn't spawn items

In GMS2 I have a spawner item with the following code:
In the create event:
timer = 0;
In the step event:
if(distance_to_object(obj_coffe_bean) > 2)
if(timer == 200) {
instance_create_layer(x, y, obj_coffe_bean, obj_coffe_bean);
timer = 0;
}
else timer++;
This works perfectly fine, coffee beans are spawned when it doesn't detect any coffee bean nearby.
The problem is that the same code doesn't work when I duplicate this object and create a spawner for another item.
The most obvious problem here would be that you are using the object index as the layer index in instance_create_layer - your code only works by chance (of there being a layer with a matching ID).
Ok, I needed to use instance_create_depth instead of instance_create_layer.

protractor - force getText() to resolve since element is removed during the test

I'm writing a test for my angular app.
I'm using protractor with jasmine.
The process in the page is as follows:
1) click a button|
2) handler creates a div with content : bar|
3) element(by.id('foo')).getText().then(function(data) { var some-var-i-declared-earlier = data });|
4) click a button which removes this div from DOM|
5) expect assertation for this element value.|
the problem will this promise is resolved the element is not present and thus I get null.
if i do expect (which resolves the promise) before I hit the button which removes the div I can see the value I need
the problem is that the flow must remove the div before i do expect.
how can I force the promise to resolve to get it's value?
of course any other solution is very welcome.
thanks in advance!
1) click a button| 2) handler creates a div with content : bar| 3) element(by.id('foo')).getText().then(function(data) { var some-var-i-declared-earlier = data });| 4) click a button which removes this div from DOM| 5) expect assertation for this element value.|
element(by...).click(); // step 1
var text = element(by.id('foo')).getText(); // step 3
element(by...).click(); // step 4
expect(text).toEqual('the-right-value'); // step 5
The reason why your original isn't working when you just assign the text data to some-var-i-declared-earlier is because this value is assigned in a promise. This means that by the time you reach the assert statement, the assignment hasn't been done yet (read https://github.com/angular/protractor/blob/master/docs/control-flow.md). In fact, step 1 hasn't even been executed by the time your assertion runs.
Alternatively you can do this (which helps you understand what's going on, but is less concise compared to the first answer):
element(by...).click(); // step 1
var some-var-i-declared-earlier = null;
var text = element(by.id('foo')).getText().then(function(data) {
some-var-i-declared-earlier = data
}); // step 3
element(by...).click().then(function() { // step 4
// the fact that this is run inside the 'then' makes sure it executes at the end of step 4
expect(some-var-i-declared-earlier).toEqual('the-right-value'); // step 5
});

SlickGrid 'mouseleave' event not fired when row invalidated after 'mouseenter' fired

I have a slickgrid that, in the 'onMouseEnter' event, I do the following:
change the underlying css for the row in question
call grid.invalidateRow()
call grid.render()
These latter two calls are necessary for the new css classes to be reflected. However, I then need to capture the onMouseLeave event, and it is not fired when I move my mouse away from the cell (or row), presumably because the call to invalidate/render has placed a new DOM element under my mouse, and it's no longer the one I initially "entered."
So I have two questions:
Is there another way to have the new css classes for a given cell be rendered without calling invalidateRow/render?
If not, is there another way to do this and still have the onMouseLeave event fired?
One option is to use the setCellCssStyles function.
grid.onMouseEnter.subscribe(function(e, args){
var cell = grid.getCellFromEvent(e),
param = {},
columnCss = {};
for(index in columns){
var id = columns[index].id;
columnCss[id] = 'my_highlighter_style'
}
param[cell.row] = columnCss
args.grid.setCellCssStyles("row_highlighter", param);
})
So the above changes the background-color of every cell of the row that has been moused into. In my fiddle, the mouseLeave subscription performs a simple console.log to ensure it is still firing.
Edit: fixed the external resource usages in the fiddle for cross-browser support

GWT - Unexpected firing of CellList's SelectionChangeEvent

I have two buttons "Next element" and "Previous element" and some custom widget containing CellList.
On button clicks I call to my widget's method which changeselection in CellList by calling it's SelectionModel:
selectionModel.setSelected(value, true);
When I refresh CellList's contents, the buttons work just fine, but when I select element in list by clicking on it, this behavior happens:
For example, I have elements {0, 1, 2, 3, 4, ..} on the list. I click on element 1, then press "Next element" button two times. These SelectionChangeEvent occurs:
Change selection from 1 -> 2 (on first button press)
2 -> 3 (on second press)
3 -> 1
But after step 2 if I press "Previous" it correctly go back to element 1. So element that I clicked with mouse doesn't let selection go more than 1 step around it.
I have no idea where the third event is coming from. My only guess is that manual selection event continues pending after firing, but I don't know how to check that.
Anybody knows the reason of this problem?
upd:
I found confirmation that selection by clicking event continues to hanging there somewhere in the EventBus: when I change search filters I access SelectionModel the same way as on button clicks and set selection to first element. But if there was user click on CellList before that the same thing happens: first, selection changes to 0, second, it goes back to previously selected if new selection of data contains that element.
upd (for Ümit's question):
nextButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
/* Omitting checks that there are elements on the list,
some element is selected and isn't last */
T value = dataProvider.getList().get(currentIndex() + 1);
singleSelectionModel.setSelected(value, true);
singleSelectionModel.isSelected(value);
Element rowElement = cellList.getRowElement(index);
rowElement.scrollIntoView();
}
}
upd: Found what was causing this problem: http://code.google.com/p/google-web-toolkit/issues/detail?id=6310
It seems you need to use HandlerRegistration. So multiple click event is not called.
Refer to:
GWT Handler Registratin
and
related link

Proper way how to prepare data in async cancellable workflow with responsive UI

This question is based on Async.TryCancelled doesn't work with Async.RunSynchronously that looks complex, so I will cut a simple part that I try to solve.
Suppose I have this functions:
let prepareModel () =
async {
// this might take a lot of time (1-50seconds)
let! a = ...
let! b = ...
let! res = combine a b
return res
}
let updateUI model =
runOnUIThread model
prepareModel prepares data that should be displayed to the user. updateUI refreshes the UI (removes old controls and creates new ctls based on new data).
Question: How should I call the two functions so that prepareModel is cancellable any time?
The flow is
user clicks refresh
prepareModel(1) started and is running asynchronously, so the UI is responsive and user can work with the application
user changes data and clicks refresh again
prepareModel(1) from is cancelled and
new prepareModel(2) is started
user changes data and clicks refresh again
prepareModel(2) is cancelled and
new prepareModel(3) is started
..
prepareModel(n) finished
updateUI is ran on UI thread, redraws the UI
(My first solution is based on MailboxProcessor that ensures that only one prepareModel is executed, see at Async.TryCancelled doesn't work with Async.RunSynchronously but as I experimented with this, it's not bug free)
One possible approach would be to start the workflow asynchronously in the background using Async.Start (then it should be cancellable). To redraw the UI at the end, you can use Async.SwitchToContext to make sure that the last part of the workflow executes on the UI. Here is a sketch:
// Capture current synchronization context of the UI
// (this should run on the UI thread, i.e. when starting)
let syncContext = System.Threading.SynchronizationContext.Current
// Cancellation token source that is used for cancelling the
// currently running workflow (this can be mutable)
let cts = ref (new CancellationTokenSource())
// Workflow that does some calculations and then updates gui
let updateModel () =
async {
// this might take a lot of time (1-50seconds)
let! a = ...
let! b = ...
let! res = combine a b
// switch to the GUI thread and update UI
do! Async.SwitchToContext(syncContext)
updateUserInterface res
}
// This would be called in the click handler - cancel the previous
// computation, creat new cancellation token & start the new one
cts.Cancel()
cts := new CancellationTokenSource()
Async.Start(updateModel(), cts.Token)

Resources