Delphi - Cursors

Cursors are transparent images attached to the mouse pointer. Using the standard Windows cursors is pretty simple

This page discusses how to create and use your own cursors.

In addition to this page, I have the following

Basics | What Microsoft says | Steps to create a cursor | Using the cursor | Displaying the cursor on a form | Finding the hotspot | Adding a cursor to a component | References


Basics

Cursors are images with a designated hot spot (tip of the arrow, center of a bull's eye, ...) and are controlled by a pointing device (like a mouse). Windows provides a number of default cursors (arrow, hourglass, hand, etc) and programs can add their own. They can be stored as *.cur files or placed in the resource section of an exe or a dll file.

Windows identifies a cursor with a handle, Delphi apps use an index into the TScreen.Cursors[] "array" (actually, a linked list of TCursorRec records) to get the handles. In the "array", the predefined Windows cursors have negative indices and your custom cursors should have positive (reduces conflicts). The only real problem will be if you have more than one dcu with cursors. In that case, you need to do something to avoid conflicts.

Note: Besides reducing conflicts, when the application terminates, the Screen object automatically releases all cursors with positive indices.

Using the built-in cursors is trivial and well documented, however, creating and including custom cursors is very poorly documented. I assume that you are supposed to use a provided tool and just follow the instructions. Unfortunately, the only tool I have is fairly old and does not support any of the new features.


What Microsoft says

According to Microsoft Well - that's cool. How do I create them?

Microsoft does not appear to have an answer. I was not able to determine a single technical detail. They don't even bother to suggest what size to use!

After many hours of digging, I stumbled onto the following CURSORDIR help in the Windows 32 Developers Reference supplied with Delphi 5.

The CreateCursor help (same source) allows the hotspot to be set and requires both an AND bitmask and an XOR bitmask. It also says that the size is system dependent and must be determined via a function call.

The LoadCursor help (same source) says

So the questions remain unanswered After a week or so working on this, I finally found some of those answers .. at O'Reilly! I won't repeat their excellent article here - enjoy. Unfortunately, they only give half the answer - to actually use their information I would have to write a program to convert a gimp produced ico file into a cur file and then figure out how to use a resource script to add it to the program.


Steps to create a cursor

These are the steps to create a simple "bulls eye" cursor using the Delphi 5 Image Editor. This editor is old (Which is why I was trying to find specs .. so I could use another tool.) Using, the Image Editor it is possible to create either a cur file or an res file. Other than copy and paste, that tool does not provide a way to import a cur file into a res file, and the Delphi 5 linker requires an res file. Note that these instructions make the icon 31x31 pixels even though the drawing area is 32x32. This is so that the hotspot will be in the middle. This is not a big deal, but if the circle diameter was an even number of pixels, then center would be at the corner between 4 pixels and not at a specific integer location.

Note that this tool only supports a black and white image, to add colors, you can handle the mouse movements yourself and draw anything you like.


Using the cursor

To use the cursor, it needs to be included in the application's resource section. By default, Delphi creates a resource file for every application (app_name.res) and automatically includes it when building the exe file. Several sources say NOT to place your cursor (or any other resource) there - instead you should create a new resource file and link it to your application.

In one of your source files (it does not matter which one) include the resource file. Do not use an asterisk - in the resource statement, the asterisk is not a generic wildcard. Instead, it is replaced by the name of the source file.

Using the cursor in code is a bit more involved - the following code searches for an unused index, loads the cursor, and returns the index. The following code allows an image to be replaced with a cursor. Note that it is important to first move the mouse location to the hotspot - otherwise, the cursor will appear where the mouse clicks instead of lined up where the initial image was (ie, it looks more professional). Screen.Cursor is a master override - it is initially set to crDefault (zero) and each control is able to control the displayed cursor just be moving the mouse. (No extra code needed.) When Screen.Cursor is changed to another value, then that overrides the cursors associated with individual controls until it is changed back to crDefault.


Displaying the cursor on a form

Cursors and icons have almost the exact same structures. If you want to use a cursor as an image on your form, you can Once you have a handle, simply assign it to a TIcon handle (after releasing the previous handle .. if any). The following example uses a handle from Screen.Cursors[]. At design time, just set the button's Cursor property to one of the options and this will display it. Since the index is just an integer, you could also use I have looked at the Delphi 5 code and Icon.ReleaseHandle decrements a reference count and frees the handle when it reaches zero. When using Screen.Cursors[] to get handles, this is probably a nit since the application will release those handles when it terminates.


Finding the hotspot

This is how to find the hotspot of a cursor. From the Windows API help file. From windows.pas - as you can see, the Delphi TIconInfo field names are the same. This code will get the hotspot of the specified cursor. Using this code, I was surprised to learn that so many Windows icons had their hotspot at 10,10 - I was expecting 0,0 and 15,15.

Using TIconInfo and CreateIconIndirect, it is possible to create a cursor from an icon - just set fIcon false and define the hotspot. If fIcon if true, then the values you enter will be ignored and the hotspot will be the center.


Adding a cursor to a component

When I first added cursors to my controls, I loaded the resource in the control's constructor. Eventually, I realized that that was creating a new instance for each control on the form. The better practice is to load the resource in the initialization section and then use the stored value for each control.


References


Author: Robert Clemenzi
URL: http:// mc-computing.com / Languages / Delphi / Cursors.html