Sunday, January 09, 2022

Displaying hints

In the OP's "ERP" program, recently I have been adding screens that display a list of items, normally dockets, where an action can be applied to the list, such as sending emails to all the people connected to the dockets, or changing the status of all the dockets retrieved. It is prudent to allow the possibility of removing a docket from the list (displayed in a grid) prior to executing the action, so I implemented a very simple 'remove' action that deletes the current item from the clientdataset that is in the background. This deletion does not delete the record from the database, of course.

Then it occurred to me that it might be beneficial to allow marking several items then deleting these in one go. At work, I jest that I am paid a bonus for every key press that I save workers; in this case, n + 1 key presses are required to delete several consecutive items as opposed to 2n key presses for single choices. So far, so good.

I then wanted a way to signal to the user that the grid accepts multiple choices. After turning this over in my head for a little while, I realised that the way to signal to the user is via a hint. There's nothing dramatic about this: the speedbuttons used in saved queries (see here) have hints defined. The standard mechanism for displaying a hint is a small window that opens near the control, displaying the help text, whenever the mouse hovers over the control. I can't explain exactly why, but this mechanism seems out of place; seemingly a better technique is to display the hint in a status bar at the bottom of the form.

The canonical method of displaying hints in a status bar is via the following code:

Constructor TMyForm.Create; begin inherited create (nil); ... Application.OnHint:= MyHint; ... end; procedure TMyForm.MyHint (Sender: TObject); begin sb.simpletext:= Application.Hint; end; procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction); begin Application.OnHint:= nil; ... end;

I've used such code in several forms without any problem, but this time around, a warning light went on in my head: I keep on assigning application.onhint to a procedure local to a given form, but application is a global object! In other words, all hints will be displayed in the status bar of the last form to be created.  ☹️ I checked this out by creating two instances of the same form; one is active and one is inactive. When I moved the mouse over the inactive form, the hints appeared in the status bar of the active form. This approach is clearly wrong.

So how does one cause that each form displays only hints emanating from its own controls and not from other forms? After a great deal of spelunking, I found a partial solution that uses the undocumented messages CM_MouseEnter and CM_MouseLeave, that are sent whenever a mouse begins or stops hovering over a control. The form can trap these messages and display the appropriate hint.

This solution worked on one form but not on another; the first form had a grid placed directly to the form, whereas in the second form, the grid was placed on a tabsheet that is placed on a page control that is placed on the form. There was no way that my mouse messages approach would work here. It's time to escalate the problem to Stack Exchange.

... to be continued.

No comments: