It can certainly be done but will be complicated I can give you some general pointers but realistically, this is code that should be developed and tested interactively, rather than something I could just type out in one go.
I’d suggest you start by associating each stimulus with its ordinal location, say counting lifting to right from 0 to 19:
stimuli = [square0, square1, square2, …] # up to 20, you already have this list
# create a dictionary that associates each stimulus with an ordinal
# number from left to right:
stimulus_boxes = {image: i for i, image in enumerate([stimuli])}
You could also/instead construct a dictionary where you associate the stimuli with their actual x
coordinates (in pixels or degrees or whatever).
Then in the 'each frame" tab, you need to monitor for when a drag had been commenced but now the mouse has been released, so the drag has ended. At that point, check if the mouse coordinates are within another stimulus. If not, zoom the stimulus back to its previous location (by looking it up in the stimulus_boxes
dictionary). i.e. a drag won’t “stick” unless it lands on a valid location.
If the mouse coordinates do overlap another stimulus, look up its box number. If it is greater than the one being dragged, then cycle through all the stimuli from the one with a box number one greater than the dragged one through to the underlying one, and shift them to a box number that is one less. Conversely, if it is less, then shift the affected ones to the right. Lastly, alter the box number of the one that was dragged to match the one that was beneath it. Then cycle through all affected stimuli, shifting their locations to match their respective ordinal positions (i.e. box numbers).
Hopefully you can turn that recipe into pseudo-code and then functional code…