Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cursor not changing on MouseDown

Tags:

delphi

I have a TPanel with 2 events:

procedure TForm1.Panel1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Panel1.Cursor := crSizeAll;
end;

procedure TForm1.Panel1MouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Panel1.Cursor := crDefault;
end;

When I click on the panel (MouseDown event) the cursor does not change to crSizeAll. What am I doing wrong and how can I resolve this?

like image 562
zig Avatar asked Dec 14 '18 16:12

zig


2 Answers

You did all right. There is just a simple trick with capturing the mouse input. Once you've pressed Left Mouse Button (LMB) on TPanel this panel has got mouse unput.

After that happened, your code Panel1.Cursor := crSizeAll; is beginning to run. As you can see in the sources of VCL, TControl sends message CM_CURSORCHANGED to itself to set needed type of cursor. Since TWinControl is based on TControl, it handles this message and checks if mouse input has been captured. If this is not so, then TWinControl sends message WM_SETCURSOR to itself in order to set new cursor.

But Microsoft clearly stated that message WM_SETCURSOR has a little restriction:

Sent to a window if the mouse causes the cursor to move within a window and mouse input is not captured.

So, there is no way to change cursor's type if mouse input has been captured. One thing you could do is to call ReleaseCapture:

procedure TForm1.Panel1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  ReleaseCapture;
  Panel1.Cursor := crSizeAll;
end;

But I wouldn't recommend do it this way since it breaks the paradigm of mouse input (in my opinion, once LMB was pressed, then it should stay pressed until it released by important reason (f.e. click on button), but you are free to decide how to use ReleaseCapture on your own).

Important!
Test executed by user Zig proved that using ReleaseCapture method is not appropriate since it forbids generating OnMouseUp event if mouse cursor gone outside of TPanel and LMB has been released.

Useful information:

  1. WM_SETCURSOR message
  2. SetCapture function
  3. GetCapture function
  4. ReleaseCapture function
like image 169
Josef Švejk Avatar answered Oct 13 '22 17:10

Josef Švejk


As Dima explained the reason the Cursor setting of the panel does not have an effect is that, the panel cannot process WM_SETCURSOR messages since the mouse is captured as long as the mouse button is pressed.

To set the cursor immediately, you can use the SetCursor API. The possible downside is that the cursor will remain what you set until you release the mouse button, even outside the panel.

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  SetCursor(Screen.Cursors[crSizeAll]);
end;

You don't need to reset the cursor since the default will be assumed (in response to WM_SETCURSOR messages) after the mouse button is released.


If you want the cursor to revert outside of the panel and set again when it enters on the panel again, you can include an OnMouseMove event handler. That's because mouse messages will be directed to the panel even when outside the panel since it captured the mouse:

procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if Mouse.Capture = Panel1.Handle then begin
    if PtInRect(Rect(0, 0, Panel1.Width, Panel1.Height), Point(X, Y)) then
      SetCursor(Screen.Cursors[crSizeAll])
    else
      SetCursor(Screen.Cursors[crDefault])
  end;
end;
like image 23
Sertac Akyuz Avatar answered Oct 13 '22 15:10

Sertac Akyuz