Spec - how to update a tree list keeping current selection? - smalltalk

I am displaying a tree and I need to add an element, but I want to keep the currently selected item.
If I do it like this:
oldSelectedItem := treePresenter selectedItem.
treePresenter
roots: newRootCollection;
selectItem: oldSelectedItem
it will of course work, but there is a problem: the activation event (whenActivatedDo:) will be triggered and I do not what this!
How can I proceed to avoid it?

Trees and Tree tables in spec implement the API updateRootsKeepingSelection: to prevent precisely this.
In practice, it makes the same as the code above, but it will not trigger any activation, and no activation transmission.
This code:
treePresenter updateRootsKeepingSelection: newRootCollection
Will have the same effect as the original code, but it will not trigger an activation when executed.
(Of course, to work properly is required that the new collection contains the previously selected elements).

Related

Is there any example or sample code for the find and filter feature in Cytoscape JS

I saw in cytoscape application we have features like find and filter by keywords and degree. I tried a workaround following the original docs. Here you can see the demo webdemo.intolap.com/cytoscape (view-source for the source code or snippet). The filter works well partially. Example, "apple" will display apple and it's connected nodes (1st level) just what I am looking for.
But the problem I am facing is about resetting the graph and filter again with a
different keyword. It seems the filter function does not work after the text box is cleared and then keyed in a different keyword.
I mean when I clear the text box, it resets the graph to original which is correct. I did that using an init() function which reinstates the graph. But then if I search for "Ball" filter does not work. Any help please. Thanks!
actually there is a reasonably good explanation in the official docs here, but to be honest, I too struggled with this feature at first:
Basically, you can filter the specific collection you want to search by just inserting a filter query. So if you want to filter all nodes, you can use this:
cy.nodes(filterQuery);
If you want to filter all elements, just call this:
cy.elements(filterQuery);
If you want to make it easy, you can use this short version (short for cy.filter(...)):
cy.$(filterQuery);
The filter query itself is not that hard, you can do this (assuming that you have a node with the id "first" or an attribute like nodeColor "#2763c4"):
cy.$('[id != "first"]');
cy.$('[id = "first"]');
cy.$('[nodeColor = "#2763c4"]');
cy.$('[weight > 50]');
Additionally, you can specify the target collection within your filter query like this:
cy.$('node[id != "first"]');
Lastly, if you need complex filtering, you can use a function to apply that logic to the filter, for that just do this:
cy.$(function(element, i){
return element.isNode() && element.data('weight') > 50;
});
Sounds like you are trying to cy.filter on a cytoscape instance that no longer exists at that point. That's why it works the first time, but not the second time (after you reinstate the graph, which probably means destroy & create).
You need to make sure you point your filter handlers to the active cytoscape instance.

Stop operation in change()

Is there a way to stop an remove operation in model.document.on('change') ?
I listen to change with this code:
model.document.on('change',(eventInfo,batch) => {
// My code here.
}
And it works fine, in so far as I do get and can inspect all changes. But there does not appear to be any way to reject the change.
I tried to call eventInfo.stop() and reset() on the differ. Both of these methods does stop the change, but always later results in a model-nodelist-offset-out-of-bounds:
exception if I try to stop a remove operation.
What I am trying to do is to change how text delete works, so when the user delete text, instead of really deleting the text from the editor, I create a marker which marks which text have been "deleted" by the user. (For optional change control).
Instead of stopping the change, maybe you could save the data value after a change and "reset" to the previous value when a delete happens:
var oldData = '';
var editor = ClassicEditor
.create(document.querySelector('#editor'))
.then(editor => {
editor.model.document.on('change',(eventInfo, batch) => {
if(oldData.length > editor.getData().length) {
// or use eventInfo.source.differ.getChanges() and check for type: "remove"
editor.setData(oldData);
}
oldData = editor.getData();
});
})
.catch( error => {
console.error( error );
});
Note instead of checking the data length, you could loop through the changes happened using eventInfo.source.differ.getChanges() and checking if there were changes of type "remove".
An operation that was already applied (and the change event is fired after operations are applied) cannot be silently removed. They need to be either reverted (by applying new operations which will revert the state of the model to the previous one) or prevented before they actually get applied.
Reverting operations
The most obvious but extremely poorly performing solution is to use setData(). That will, however, reset the selection and may cause other issues.
A far more optimal solution is to apply reversed operations for each applied operation that you want to revert. Think like in git – you cannot remove a commit (ok, you can, but you'd have to do a force push, so you don't). Instead, you apply a reversed commit.
CKEditor 5's operations allow getting their reversed conterparts. You can then apply those. However, you need to make sure that the state of the model is correct after you do that – this is tricky because when working on such a low level, you lose the normalization logic which is implemented in the model writer.
Another disadvantage of this solution is that you will end up with empty undo steps. Undo steps are defined by batches. If you'll add to a batch operations which revert those which are already in it, that batch will be an empty undo step. Currently, I don't know of a mechanism which would allow removing it from the history.
Therefore, while reverting operations is doable, it's tricky and may not work flawlessly at the moment.
Preventing applying operations
This is a solution that I'd recommend. Instead of fixing an already changed model, make sure that it doesn't change at all.
CKEditor 5 features are implemented in the form of commands. Commands have their isEnabled state. If a command is disabled, it cannot be executed.
In CKEditor 5 even features such as support for typing are implemented in the form of commands (input, delete and forwardDelete). Hence, preventing typing can be achieved by disabling these commands.

RSA Archer user cannot specify a date in the future / past

Has anyone any good patterns for RSA Archer validation which prevents a user from saving the record when a given date specified is in the future (or past)?
Currently I am catching this using calculated fields after the data has been saved, in a data exceptions report. But ideally I would like to catch this early prior to the user saving the record.
I would suggest that you use custom object in this case.
So remove the basic onclick attribute of the SAVE and APPLY button.
In your custom object, check if the entered date matches the system date (or the time-zone you need). Set a flag. Based on the flag value, you can call the actual function call of the SAVE or APPLY button.
Hope that helps!
Alex,Tanveer is correct. You have to use a Custom Object with embedded JavaScript code to implement described functionality.
You will need to create a function that will validate the value entered by the end user and either accept it or make user correct himself.
Now, you have two options how to do it:
1. You can attach your validation function to the Save and Apply buttons as Tanveer described. I have shared a similar code in the following question before. You can review it here: LINK
2. You can attach your validation function to the element you plan to validate directly. So when user is done with given input element and input element loses focus your function will be called. Here is a sample code with jQuery:
$('#elementid').blur(function() {
// validate entered value here
// if required show a pop-up message
WarningAlert(msg, title);
});
Good luck!

Volt: Equivalent of destroy_all?

I have a form with a "reset this collection" button. Looks kind of like this:
<button e-click="reset_patients">reset patients</button>
In my controller, I do this:
def reset_patients
puts "destroying"
store.patients.each{|p| p.destroy}
end
What I expect is that the clients displaying the list will show an empty list. What is actually happening is that some but not all of the items are deleted.
How is a "dump the entire collection in the trash can" operation handled on a persistent backed store (i.e.: model :store)? Also, is there a way to make these cascade through related collections?
We don't have .destroy_all yet. Its on my short list, but I'm reworking one thing in the data provider API to make it a bit smarter. For now you can do
store.patients.reverse.each(&:destroy)
(The .reverse is needed since your deleting array objects as you loop)

PsychoPy Builder - How to I take a rest part way through a set of trials?

In PsychoPy builder, I have a lot of trials and I want to let the participant take a rest/break part way through and then press SPACE to continue when they're ready.
Any suggestions about how best to do this?
PsychoPy Builder uses the TrialHandler class and you can make use of its attributes to do control when you want to take a rest.
Assuming you're trial loop is utilising an Excel/csv file to get the trial data then make use of trialHandler's attribute : thisTrialN
e.g.
1/ Add a routine containing a text component into your loop (probably at the beginning) with your 'now take a rest...' message and a keyboard component to take the response when they are ready to continue.
2/ Add a custom code component as well and place something similar to this code into its "Begin Routine" tab:
if trials.thisTrialN not in [ int(trials.nTotal / 2) ]:
continueRoutine=False
where 'trials' is the 'name' of your trial loop.
The above will put a rest in the middle of the current set of trials but you could replace it with something like this
if trials.thisTrialN not in [10,20]:
continueRoutine=False
if you wanted to stop after 10 and again after 20 trials.
Note, if you're NOT using an Excel file but are simply using the 'repeat' feature of a simple trial loop, then you'll need to replace thisTrialN with thisRepN
If you're using an Excel file AND reps you'll need to factor in both when working out when you want to rest.
This works by using one of Builder's own variables - continueRoutine and sets it false for most trials so that most of the time it doesn't display the 'take a rest' message.
If you want to understand more, then use the 'compile script' button (or F5) and take a look at the python code that Builder generates for you.