How to stop Screen.Cursor affects all controls on the form?
Asked Answered
F

1

5

I will try to simplify my problem. If for example you drop 2 TSpeedButton and do:

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  SpeedButton2.Cursor := crHandPoint; // note I'm setting other cursor than crDefault
end;

The SpeedButton2.Cursor remains showing Screen.Cursor which was set to crHourGlass.
I have looked into the TScreen.SetCursor code, and realize it sets the cursor for the entire form.
My question: is it somehow possible to use the Screen.Cursor for the entire form, BUT without impacting some control(s) which I want to set other cursor.

The same happens with a TButton. I don't mind placing the SpeedButton on a windowed control if I can somehow control it's cursor while Screen.Cursor is set to crHourGlass.

Thanks.

Fillander answered 4/12, 2019 at 16:27 Comment(3)
Screen.Cursor documentation clearly explains this is intended behavior: "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". You can derive your button class that overrides WM_SETCURSOR to change the behavior.Tyr
@SertacAkyuz, Thanks. I have tried WM_SETCURSOR for TSpeedButton but the message does not sent for it. this however works for TButton 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.Fillander
You're welcome. Yes, wincontrols. I'm not entirely positive I understand how graphic control's cursors work. Anyway, I guess you might need to differentiate a specific control if you don't want the cursor of all buttons to remain unaffected. Perhaps sth like: if (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
T
7

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;
Tyr answered 4/12, 2019 at 19:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.