Window Shake

Hi All,

I’m still learning my way around XoJo…

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.

The Shake routine is here: Article 19204: : Shake It!

Any thoughts?

Put the code to update Label in a method and call it after code to shake

I tried this and the results were the same.

Here’s what I’m doing when I want the shake and display the results. As before, the Shake works great. The Results are displayed properly as well, but that at same time the shake starts.

Shake()
ShowResult()

Shake()
myAwesomeTimer() → ShowResult()

Just a thought as I’m not at my Windows machine right now

The quick n dirty hack would be to wait for the timer to finish using DoEvents. This method would make the shake “synchronous” and the next line of code wouldn’t execute until the shake completes.

It’s a really lazy hack though… add this to the end of the Shaker.shake method:

// Wait for the Timer to finish
while pShaker.RunMode <> Timer.RunModes.Off
  App.DoEvents
  
wend

A better idea would be to use a delegate to call a “shake completed” method but that’s not as easy to copy and paste here into the forum.

1 Like

:grinning:

https://documentation.xojo.com/api/language/timer.html#timer-calllater

2 Likes

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. :slight_smile:

You really should not use app.doEvents, especially when there is a much better solution. I’m honestly surprised that Tim suggested it.

Was just a quick n dirty hack. Here’s an updated project file that shows how to use a delegate to implement a “shake completed” method.

Shake w Delegate.zip

4 Likes

Thanks again Tim! I will study this code to understand what’s going on and use it instead of the doEvents.

Thanks Tim
I’m lacking in Delegate knowledge so will study this also

Can the end of one Timer launch the start of a second Timer?

Can anybody explain to me why nobody is even considering the solution that is build in and custom made for this situation as it gives you control of the timing???

Timer.CallLater(afterMilliseconds As Integer, method As Timer.TimerCallLater)

Used to call a method (without parameters) once after the specified delay in milliseconds.

Why muck around with delegates or two timers instead???

1 Like

As the first timer (the shaker) is firing multiple times that won’t work here, but calling the second timer with CallLater after calling shake will.

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.

:thinking: If it’s untraceable then how do you know it happens? :flushed:

But seriously, can you elaborate what the problem is? There are LOTS of ways to crash an app (aka buggy code), and people should usually learn not to do that.

1 Like

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.

1 Like

Thanks for the clarification. Yes, that info should be in the documentation.

On the plus side it doesn’t really apply to the case here, so I would still suggest that using CallLater is the correct way of solving the question.

IMHO CallLater is never the correct way to solve a problem; we have Timer and AddHandler, so why loose control over a function call?

1 Like

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)
End Sub

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
		End Sub

		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
		  Case 0
		    W.Left = StartingPosition - ShakeDelta
		  Case -ShakeDelta
		    W.Left = StartingPosition + ShakeDelta
		    Rep = Rep + 1
		  Else
		    W.Left = StartingPosition - ShakeDelta
		  End Select
		  
		  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
		  End if
		End Sub

	
Properties:
		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.

-Karen