0

My application is loading list items from a database in the background with a separate thread. During the loading process the user can interact with the application, so I don't want to change the default cursor. Still, to visually signal the loading process, I'd like to display the Windows busy (hourglass, IDC_WAIT) cursor on my form like an animated image. What would be the easiest way to do that? I was thinking of converting to a GIF and displaying in a GIFImage but I'd like to display the default Windows cursor (thus getting the resource from the system dynamically).

Note: I'm using Delphi7.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Steve
  • 2,510
  • 4
  • 34
  • 53
  • 1
    AFAIK, there is no way to convert a system cursor to an animated GIF. I would just use [`TAnimate`](https://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TAnimate) instead, providing your own `AVI` resource to play. There are plenty of freeware AVIs you can download and then compile into your app. Or, if you really want to use `TGifImage`, there are plenty of freeware animated GIFs to download. – Remy Lebeau Feb 02 '22 at 17:55
  • 1
    Set the [cursor to `crAppStart`](https://docwiki.embarcadero.com/Libraries/Sydney/en/Vcl.Controls.TCursor) - that's exactly its purpose (and the reason why it has both: pointer **and** waiting indicator). But nowadays nobody knows/understands this difference to `crHourGlass` anymore (neither programmer, nor user). – AmigoJack Feb 03 '22 at 00:42

1 Answers1

2

Just a partial solution: Declare variables

h: HICON;
FFrameIdx: Integer;

Then load the cursor:

h := LoadImage(0, MakeIntResource(OCR_WAIT), IMAGE_CURSOR, 0, 0, LR_SHARED);

Then draw the current frame in the OnPaint handler:

DrawIconEx(Canvas.Handle, 100, 100, h, 0, 0, FFrameIdx, 0, DI_NORMAL);

To animate it, you can use a TTimer with this code:

Inc(FFrameIdx);
Invalidate; // or something more suitable

To try this, you can just put the variables in a new VCL application's main form class (below private), put the LoadImage in the form's OnCreate handler and the DrawIconEx in the OnPaint handler.

But if you want to use this in a real app, you'd better create a new custom control with it.

Obviously, you need to replace FFrameIdx with FFrameIdx mod FFrameCount or Inc(FFrameIdx) with FFrameIdx := Succ(FFrameIdx) mod FFrameCount, where FFrameCount is the number of frames in the animated cursor.

You should also set the timer's Interval to match the cursor's frame rate.

Screen recording

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • The reason why I don't tell you how to obtain the `FFrameCount` is that I know of no Win32 API that gives you that information! A quick WWW search told me to see at what `FFrameIdx` the `DrawIconEx` fails, but that doesn't seem like a good approach to me. – Andreas Rejbrand Feb 02 '22 at 18:16
  • 2
    In any case, I wouldn't try to load the system's wait cursor and use that. Instead, I would create my own custom control with an animation made by my own code. Sure, that won't be the system default wait cursor, but on the other hand, displaying system cursors as images may confuse the user. (Hey! I have two mouse cursors! Which one is the real one?) In addition, with the "custom code" approach it is easier to manage DPI scaling and flicker-free drawing. You get full control. – Andreas Rejbrand Feb 02 '22 at 18:19
  • That's a really good point that you make, having two cursors may indeed confuse the user. I haven't thought about that. – Steve Feb 03 '22 at 09:29