I found this code that will shake the window like when the Mac doesn’t like the password that was entered. I added it to a project of mine and the shake works great. I’m trying to get a short pause after the shake is complete before updating a label but it’s instantaneous. I’ve been playing with a second timer, but so far all of my attempts have failed. The Label is updating as soon as the shake starts.
Thanks Tim! This works great and is just fine for my purposes. Right now, I’m just writing a bunch of simple apps to get more familiar with XoJo and to refresh my memory of this type of programming. It’s been years, but used to do VB and Delphi.
Because you can end up with an unexpected, untraceable application wide NilObjectExceptions if you don’t know how to use them correctly.
You can if you place it inside the If on the line after WindowToShake = Nil as this is only run once when the shaking has finished. You could then either safely call a method inside cShaker without any code change or use a delegate to call a method back on the calling Window.
If for whatever reason you decide to use WeakAddressOf in the CallLater the object it calls later can be released by the time the timer fires which will cause the CallLater to fail with the NilObjectException.
The reason it can be untraceable is that due to the nature of timers they don’t always occur when you think they will and the stack trace you’re given on the NOE doesn’t elude as to which timer caused the issue if you have multiple of them and to further exacerbate the issue, the NOE ends up being raised at the application level, far away from the actual issue.
I’m surprised that this information isn’t on the CallLater documentation page as a warning about using WeakAddressOf with CallLater.
Another gotcha with CallLater is calling it too many time before the timer has a chance to run, you can end up with your program just locking up in windows, this usually only happens when its placed in a tight loop.
I don’t have the Xdev article but this topic came up about a year ago IIRC and for the fun of it I wrote a subclass time subclass to do the shake.
I put in in a module where I also had and Extension method on at the Window Class:
The extension method is one line:
Public Sub Shake(Extends W as Window)
DIm Shaker as new ShakeTimer(W)
Notice that it does not keep a reference to the timer anywhere.
Normally if you don’t use Timer.CallLater the timer would be destroyed immediately upon exiting the method as the reference count of the instance would go to zero. That means the timer would never fire and the window never shake.
I personally would rather manage the timer myself for something like this and I do it by having the timer store a reference to itself in a property, which it sets to NIL in it’s action event, when done shaking. That results in the reference count going to 0, meaning no memory leak.
I am sure someone will have an objection to doing to this way, but I can’t see how it can go wrong.
The Timer subclass in the module is:
Private Class ShakeTimer Inherits Timer
Sub Constructor(W as Window) ' This is what is called from the shake method
me.Period = 75
me.Mode = Timer.ModeMultiple
me.W = W
me.Myself = me ' Saving a reference to itself so it won't be destroyed
me.StartingPosition = W.Left
me.OriginalTop = W.Top
Sub Action() ' the Action event that does the shaking and kills the timer when done
Const ShakeDelta as Integer =18
Const Shakes as Integer = 2
Select Case W.Left - StartingPosition
W.Left = StartingPosition - ShakeDelta
W.Left = StartingPosition + ShakeDelta
Rep = Rep + 1
W.Left = StartingPosition - ShakeDelta
W.Top = OriginalTop
If Rep >= Shakes Then ' Stop the time and NIL it's reference to itself to kill it
Mode = Timer.ModeOff
W.Left = StartingPosition
W = Nil
Myself = Nil
Private Myself As Shaketimer
OriginalTop As integer
Private Rep As Integer
Private StartingPosition As Integer
Private W As Window
End of timer subclass
As this was intended for UI feedback and happens so quickly, I did not think it happening asynchronously mattered.