This is intentional behavior as explained in the documentation for TScreen.Cursor
:
... When Cursor is crDefault, the individual objects determine the
cursor image. Assigning any other value sets the mouse cursor image
for all windows belonging to the application. The global mouse cursor
image remains in effect until the screen's Cursor property is changed
back to crDefault. ..
Windowed controls handle their cursors in TWinControl.WMSetCursor
procedure, handler of WM_SETCURSOR
message, where they explicitly set the screen cursor if it is anything other than crDefault
and disregard their own cursor.
So to change the behavior you can handle the mentioned message. For a TButton
interposer, an example could be:
procedure TButton.WMSetCursor(var Message: TWMSetCursor);
begin
if (Cursor <> crDefault) and (Message.HitTest = HTCLIENT) then begin
Message.Result := 1;
Windows.SetCursor(Screen.Cursors[Cursor]);
end else
inherited;
end;
Graphic controls' cursors are handled by their parent TWinControl
. So to change the behavior of a speed button, you would still need to handle the same message on its parent. This would likely be impractical since the parent class might not be known beforehand.
Still, a very non-generalized implementation, for example for a graphic control placed directly on the form, might look like the below:
procedure TForm1.WMSetCursor(var Message: TWMSetCursor);
var
SmPt: TSmallPoint;
Control: TControl;
begin
DWORD(SmPt) := GetMessagePos;
Control := ControlAtPos(ScreenToClient(SmallPointToPoint(SmPt)), True);
if Assigned(Control) and Boolean(Control.Tag) then begin
Message.Result := 1;
Windows.SetCursor(Screen.Cursors[Control.Cursor])
end else
inherited;
end;
Above example would require the graphic control to have a non zero tag value. E.g.:
procedure TForm1.Button1Click(Sender: TObject);
begin
Screen.Cursor := crHourGlass;
SpeedButton1.Cursor := crHandPoint;
SpeedButton1.Tag := 1;
end;
WM_SETCURSOR
forTSpeedButton
but the message does not sent for it. this however works forTButton
and I guess for any windowed control (interposer):procedure TButton.WMSetCursor(var Message: TWMSetCursor); begin Message.Result := 1; Windows.SetCursor(Screen.Cursors[crHandPoint]); end;
I will investigate further. – Fillanderif (Cursor <> crDefault) and (Message.HitTest = HTCLIENT) then Windows.SetCursor(Screen.Cursors[Cursor]) else inherited;
. Or perhaps adding an "overridecursor" property that is by default false... – Tyr