[Closed] Undo and spinners
I have a spinner that change the rotation values on a bunch of bones. Every time I change the spinner value, the value gets put into a function that changes the needed bones. Simple stuff.
Problem is, if I put an “undo on” on the spinner changed event, I get an undo item for every single change of the spinners that happens while the mouse button is held down. Is there any way I can trick the undo to only catch the start and end of the change?
Thanks! “theHold.begin” was new to me. Will read about it and see if I get it to work.
There was a similar thread recently, where you can find part of the code for this.
http://forums.cgsociety.org/showthread.php?f=98&t=1341344
Here is a new version, which uses the “entered” event of the spinner instead, to better handle theHold.
(
try (destroydialog ::RO_SCALE) catch()
rollout RO_SCALE "Scale Nodes" width:162 height:176
(
spinner spn_scale "Scale: " pos:[24, 16] fieldwidth:68 range:[-1E9,1E9,0] type:#worldunits scale:0.01
button bt_print "Print Info" pos:[24,128] width:112 height:32
button bt_default "Default Scale" pos:[24, 48] width:112 height:32
button bt_reset "Reset Scale" pos:[24, 80] width:112 height:32
local nodes = #()
fn Hold mAction mString: =
(
case mAction of
(
#start: if not thehold.holding() do thehold.begin()
#accept: if thehold.holding() do thehold.accept mString
#cancel: if thehold.holding() do thehold.cancel()
)
)
fn ScaleNodes val =
(
for j in nodes do j[1].scale = (j[2]+val) / j[2] * j[3]
)
fn StoreNodes =
(
Hold #start
nodes = for j in selection collect #(j, j.max-j.min, j.scale)
)
on spn_scale buttondown do StoreNodes()
on spn_scale changed arg do ScaleNodes arg
on spn_scale entered inSpin inCancel do
(
if not inSpin do
(
StoreNodes()
ScaleNodes spn_scale.value
spn_scale.value = 0.0
Hold #accept mString:"ScaleNodes"
)
if not inCancel then
(
spn_scale.value = 0.0
Hold #accept mString:"ScaleNodes"
)else(
Hold #cancel
)
nodes = #()
)
on bt_default pressed do
(
Hold #start
selection.scale = [1,1,1]
Hold #accept mString:"ScaleNodes"
)
on bt_reset pressed do
(
Hold #start
resetscale selection
Hold #accept mString:"ScaleNodes"
)
on bt_print pressed do
(
for j in selection do format "% :
size: %
scale: %
" j.name (j.max-j.min) j.scale
)
)
createdialog RO_SCALE
)
That’s really usefull, Jorge!
Any ideas for ColorPicker? You don’t have so much events as for spinner…
The ColorPicker control does only have one event and I don’t find it useful for undoing.
There are other alternatives you could explore, but I think all of them will present different challenges, for example recreating the Sample Color Box and updating it on Undo-Redo.
MXS
colorPickerDlg()
ManagedServides DLL
MaxColorPicker()
.Net SDK
IColPick Interface
CreateColorPicker()
HSVDlgDo()
If you use it for a specific node parameter, perhaps you and install an Undo callback and update things there.
you can find my several examples of “undoable” spinner on this forum… but after many years of using the similar technique i found the better way of implementation “undoable” rollout controls (including colorpicker).
the idea is to use DUMMY custom attribute with parameter block and built-in undo/redo mechanism
i don’t want to share the complete solution but i can give you a clue:
ca = attributes "ca_color"
(
parameters params rollout:params
(
color type:#color default:red ui:ui_color
on color set val do
(
selection.wirecolor = val
)
)
rollout params "Params" width:200
(
colorpicker ui_color "Test Color: " modal:off
)
)
delete objects
b = box isselected:on
c = createinstance ca
createdialog c.params
Thanks both.
My only conclusion is the same I always say: I hate UIs!!
Jorge: I’ll possibly try the colorPickerDlg() by recreating the Sample Color Box. I don’t mind by restoring the original color if undo is done (if fact, none of my controls are restored after undo). It’s not matter at all in this case.
DenisT: About your method, I see it clear how to implement it. Thanks. But it’s too late for me now to redo all my struct. I’ll try it next time.
Here’s a code for lazy people like me…
I use a timer to check for changes. At least, it reduces the undo buffer to everytime the user stops changing the color.
But… Can we catch if the colorpicker is opened (with hwnd or others)? If yes, it’s easy to implement to only one undo (or none if no changes).
(
try (destroydialog ::RO_COLOR) catch()
rollout RO_COLOR "Undo ColorPicker" width:162 height:100
(
colorPicker cp_color "Wire Color " color:(color 255 255 255) enabled:true modal:false offset:[10, 25]
local theNodes = #()
local tt = dotnetobject "System.Windows.Forms.Timer"
local cpColor_old
local cpColor_new
fn Hold mAction mString: =
(
case mAction of
(
#start: if not thehold.holding() do thehold.begin()
#accept: if thehold.holding() do thehold.accept mString
#cancel: if thehold.holding() do thehold.cancel()
)
)
fn SelectGeometry =
(
if (not tt.enabled) then
(
tt.start()
Hold #start
theNodes = for o in selection where (isKindOf o GeometryClass) collect o
)
else if cpColor_new != cpColor_old do
(
theNodes.wireColor = cpColor_new
)
)
fn undoColorPicker s e =
(
if cpColor_new == cpColor_old then
(
Hold #accept mString:"WireColor Change"
s.Stop()
)
else
(
cpColor_old = cpColor_new
)
)
on RO_COLOR open do
(
tt.Interval = 500
cpColor_old = (color 255 255 255)
dotnet.addEventHandler tt "Tick" undoColorPicker
)
on cp_color changed new_col do
(
cpColor_new = new_col
SelectGeometry()
)
)
createdialog RO_COLOR
)