As promised last time, I present here a very simple implementation of an automatically cancelling hourglass exploiting the same life-time management used previously to automatically dispose of temporary objects.
First, The Code And An Apology
I have tested the download this time and it appears fine.
Also a quick apology to anyone who experienced difficulty over the weekend accessing the site. Those problems should now have been resolved by my ISP.
Then, How It Is Used
As you will see if you download it, the implementation really is embarrasingly simply – the license and documentation is quite a bit more extensive than the code itself! This is most apparent in the usage itself, handled by just two functions, one of which is needed only occasionally:
To turn the hourglass on, call HourglassOn().
You don’t actually need to do anything for it to be turned off!
procedure TMyForm.ButtonOKClick(Sender: TObject); begin HourglassOn; // Do your processing here end;
Once your code leaves the scope in which you turned the hourglass on, the reference you created by calling HourglassOn() is released. If that is the only reference then the hourglass will turn itself off again. Even if your code calls other routines that in turn call HourglassOn(), the references will all be released and once all of them have been released, the hourglass goes off:
procedure TMyForm.ButtonOKClick(Sender: TObject); begin HourglassOn; // Do your processing here SomeCommonProcessing; end; procedure TMyForm.SomeCommonProcessing; begin HourglassOn; // Common processing code occurs here end;
And as with AutoFree(), these references are released even where an unhandled exception is encountered or raised.
So why even have an HourglassOff() facility?
I’m not sure. Certainly I don’t recall ever having to use it. It was implemented when I thought it may be useful to ensure that the hourglass is off prior to presenting a message box or other user interaction, but before I had realised that even this doesn’t appear to be necessary. I left it in just in case.
You never know.
HourglassOff() effectively clears the decks – any subsequent calls to HourglassOn() will effectively be managing an entirely new hourglass cursor. Any references to a prior existing hourglass will silently be released without interfering with the new cursor.
More Tinkering With (Deltics.)Forms
There is actually a third function that I’ve not mentioned so far: HourglassActive().
This returns a Boolean that indicates whether an hourglass (as represented by an hourglass object in this implementation) is currently active. This is used primarily by a recent change in my Deltics.Forms unit in a slight modification of the behaviour of ShowModal.
ShowModal saves the current Screen.Cursor at the time that a form is shown modally before applying a Screen.Cursor of crDefault. When the modal form has been dismissed, if Screen.Cursor has not been further modified the saved cursor is restored. If the modal form itself has changed the Screen.Cursor at all, the saved cursor is NOT restored.
In Deltics.Forms, ShowModal has now been extended to also consider the HourglassActive() indicator. That is, regardless of whether the modal form has modified the Screen.Cursor, once a modal form is dismissed the hourglass cursor (NOT any saved cursor) is reinstated if HourglassActive() is TRUE.
Firstly and most obviously of course, this is not a general purpose cursor management framework. It is solely concerned with simple use of an hourglass to indicate a “busy” state in an application.
Secondly the implementation deals with hourglass control for processes entirely bounded by scope. If you have some process that encompasses separate scopes then there is the possibility of cursor flicker:
procedure TMyForm.ButtonDoItClick(Sender: TObject); begin ProcessPart1; ProcessPart2; end; procedure TMyForm.ProcessPart1; begin HourglassOn; // Do some processing end; procedure TMyForm.ProcessPart2; begin HourglassOn; // Do some other processing end;
In this case the cursor will toggle between hourglass and default states for each of ProcessPart1 and ProcessPart2 as the hourglass turned on by each is turned off when each completes. Usually this sort of situation is easily resolved by ensuring that the hourglass is turned on in the event – originating in the GUI – that spans the scopes involved, i.e. that calls the methods.
Other than that, this very simple implementation has served me well, although it has not perhaps been exercised over rigorously.
I should point out that, as Barry Kelly mentioned in the comments to the AutoFree() post, the behaviour on which this relies (or perhaps more specifically the precise timing of the behaviour) is not a formally defined aspect of the language or the run-time, and so cannot be guaranteed in future Delphi versions.
He is better placed than I (he actually works on the Delphi compiler!) to know, but I would be very surprised if this behaviour did change as there may well be code “in the wild” – quite apart from that which I’ve presented recently – that relies on, or is at least sensitive to, this timing.
For myself I’m not overly concerned – Barry wasn’t saying that it would change, only that it could.
Never-the-less, I guess I should consider myself warned. And, now, so too should you.