There was a problem with my previous code: if the mouse dragging is fast, the view and the order might not properly update. This is due to elements ‘bubbling’ from one spot to the next, ie. swapping places with its neighbor (left or right). In the adapted code below I use a different approach: positions and order of all elements are calculated on every mouse drag, so even if some spots where missed due to rapid mouse movement, the next update will fix this. The resulting code is both shorter and more concise - don’t you just love when that happens:)
(
~size = 100;
~spacing = 10;
~order = [\In, \Delay, \Ring, \Reverb, \Out];
~createView = {|n|
var e = (
name: StaticText().string_(n).stringColor_(Color.white).align_(\center),
but: Button().states_([[\OFF], [\ON, \, Color.green]]).mouseMoveAction_{ true } // prevents propagation to parrent view
.fixedHeight_(20).value_(2.rand)
);
View()
.layout_(VLayout(e[\name], e[\but])
.spacing_(5).margins_(5)).background_(Color.rand).fixedSize_((~size - ~spacing)@(~size - ~spacing))
// for real life usage you would output the event 'e' from the function and have access to both
// the parrent view and the subviews: e[\view], e[\name], e[\but]
};
v = ();
~order.do{|n|v.add(n -> ~createView.(n))};
~layout = HLayout(*~order.collect{|n|v[n] }).spacing_(~spacing).margins_(~spacing);
w = View().layout_(~layout).front.alwaysOnTop_(true);
~mouseDrag = {|order, v, size = 100, spacing = 10|
var leftInit, offset, curKey, oldI, newI;
order.do{|n|
v[n].mouseDownAction_{|view, x, y|
leftInit = v[n].bounds.left;
offset = x;
curKey = n;
};
v[n].mouseUpAction_{
v[curKey].moveTo(order.indexOfEqual(curKey) * size + spacing, spacing)
};
v[n].mouseMoveAction_{|view, x, y|
var pos = v[n].bounds.left - leftInit;
var relativePos = pos%size;
{ v[n].moveTo((v[n].bounds.left + x - offset).max(0).min(order.size - 1 * size + (2 * spacing)), spacing) }.defer;
v[n].front;
newI = (v[n].bounds.left / size).round.asInteger;
oldI = order.indexOfEqual(n);
order.move(oldI, newI);
order.debug(\order);
order.do {|name, i|
if (i != newI)
{ { v[name].moveTo(spacing + (i * size), spacing ) }.defer }
// you could add logic to only call .moveTo if the calculated new bounds are different fromt the current bounds
};
}
}
};
~mouseDrag.(~order, v, ~size, ~spacing);
)