Building A Better Mouse Trap
The creators of Delphi went to a lot of effort to make the hard things easy, so when you come across something that should be easy but ends up being a lot of code, you know you are on the wrong road and it is time to take a step back.
Although controlling the mouse cursor seems trivial, it would be nice to know when your computer is busy. When your application takes a long time to do something you should change the mouse to an hourglass and when you are finished, you should change it back. In fact this type of code might be throughout your application:
procedure TForm1.Button1Click(Sender: TObject);
begin;
Screen.Cursor := crHourglass;
// Do something.;
Screen.Cursor := crDefault;
end;
Often things you think are quick can take a long time on a slow machine, or a machine loaded with a lot of data. To err on the side of user friendliness, it is better to set the mouse cursor to an hourglass.
When you get a bit more advanced you will wrap your code in a try finally block to ensure the mouse gets changed back from an hourglass:
procedure TForm1.Button1Click(Sender: TObject);
begin;
Screen.Cursor := crHourglass;
try;
// Do something.
finally;
Screen.Cursor := crDefault;
end
end;
Over time you will start to chain some procedures together. Some of these may take a long time or cumulatively they may take a long time. Either way you will need to save the mouse state as you enter the procedure and restore it when you leave:
procedure TForm1.Button1Click(Sender: TObject); var HoldCursor : TCursor; begin; HoldCursor := Screen.Cursor; Screen.Cursor := crHourglass; try; // Do something. finally; Screen.Cursor := HoldCursor; end end;
Although the problem seems to be solved, during testing you may start to notice that the mouse is not returning from an hourglass state or returns too early. All your application logic is now covered by screen handling logic, making your application unnecessarily complex and hard to read.
The Solution
Delphi provides access to the Application object. One of the events of this object is the OnIdle event. It seems like a natural place to turn the mouse from an hourglass into the default arrow. Setting up an event handler is simple:
constructor TmyForm.Create(AOwner: TComponent );
begin;
inherited;
Application.OnIdle := ApplicationOnIdle;
end;
procedure TmyForm.ApplicationOnIdle (Sender: TObject; var Done: Boolean);
begin;
if Screen.Cursor = crAppStart then
begin
Screen.Cursor := crDefault;
end;
end;
Now whenever the application has nothing to do it will change the mouse to the default arrow. The only check we have to perform is to make sure the current cursor is not an Hourglass/Arrow combination cursor which indicates a thread is running. If your application does use threads then the OnIdle event will not fire until all the threads have finished running. It is also worth noting that the OnIdle event fires a lot - do not put any code in there that will take more than a millisecond to run or your application will really slow to a crawl.
The final task is to sprinkle Screen.Cursor := crHourglass; commands throughout the application with regular frequency. Adding them to base classes and overriding TTable & TQuery classes will hit the hotspots in most applications quickly. All of the code that catches exceptions and remembers the mouse state can be removed, the OnIdle event will fire even if there was an unhandled exception.

