Not that the TPanel
did not comply with ReleaseCapture
, the capture does not matter at all. This happens after the popup menu is launched and activated, and the control is clicked again:
- Clicking cancels the cycle of the modal menu, the menu closes and a mouse down message is displayed.
- VCL sets the flag in the processing of mouse messages
[csClicked]
. - The mouse event handler is called, you release the capture.
- After the mouse message is omitted, the processed message is pushed up, the VCL checks the flag and clicks the control if it is set.
- The click handler displays a menu.
Of course, I did not follow the working example, so I can not say when and how ReleaseCapture
is useful. In any case, this cannot help.
The solution I propose is slightly different from the existing project.
We want to make a second click so as not to trigger a click. See this piece of code:
procedure DropMenuDown(Control: TControl; PopupMenu: TPopupMenu); var APoint: TPoint; begin ... PopupMenu.PopupComponent := Control; PopupMenu.Popup(APoint.X, APoint.Y); TickCountMenuClosed := GetTickCount; end;
The second click is what closes the menu before starting it again through the same handler. This PopupMenu.Popup
call to PopupMenu.Popup
. Therefore, we can say that the mouse button is pressed (either the left button or double-click), but the VCL has not yet been processed. This means that the message is still in the queue.
Remove the registration mechanism (hacker mouse manipulator) with this approach, it is not needed, but the class itself and global variables.
procedure DropMenuDown(Control: TControl; PopupMenu: TPopupMenu); var APoint: TPoint; Msg: TMsg; Wnd: HWND; ARect: TRect; begin APoint := Control.ClientToScreen(Point(0, Control.ClientHeight)); PopupMenu.PopupComponent := Control; PopupMenu.Popup(APoint.X, APoint.Y); if (Control is TWinControl) then Wnd := TWinControl(Control).Handle else Wnd := Control.Parent.Handle; if PeekMessage(Msg, Wnd, WM_LBUTTONDOWN, WM_LBUTTONDBLCLK, PM_NOREMOVE) then begin ARect.TopLeft := Control.ClientOrigin; ARect.Right := ARect.Left + Control.Width; ARect.Bottom := ARect.Top + Control.Height; if PtInRect(ARect, Msg.pt) then PeekMessage(Msg, Wnd, WM_LBUTTONDOWN, WM_LBUTTONDBLCLK, PM_REMOVE); end; end;
In addition, it does not depend on the processing time.