Delphi 菜单提示错误

Delphi Menu Hint Bug

我从网站“https://www.thoughtco.com”(Zarko Gajic) 获得了下面的代码。位于菜单项中时,在鼠标指针附近显示提示。

但是,它有一个错误。当通过键盘打开菜单时,无论鼠标指针在屏幕上的哪个位置,工具提示都会出现在鼠标指针旁边。

我试图通过添加被注释掉的行来修复错误。现在的错误是无论你是否快速点击菜单项,提示总是出现。

如何解决这个问题?

谢谢。

代码:

procedure TfrmPrincipal.WMMenuSelect(var Msg: TWMMenuSelect);
var
  menuItem : TMenuItem;
  hSubMenu : HMENU;
  hPopupWnd: HWND; // Added
  R: TRect;        // Added
  Pt: TPoint;      // Added
begin
  inherited;

  menuItem := nil;

  if (Msg.MenuFlag <> $FFFF) or (Msg.IDItem <> 0) then
  begin
    if Msg.MenuFlag and MF_POPUP = MF_POPUP then
    begin
      hSubMenu := GetSubMenu(Msg.Menu, Msg.IDItem);
      menuItem := Self.Menu.FindItem(hSubMenu, fkHandle);
    end
    else
    begin
      menuItem := Self.Menu.FindItem(Msg.IDItem, fkCommand);
    end;
  end;

  hPopupWnd := FindWindow('#32768', nil); // Added

  if hPopupWnd = 0 then Exit;             // Added

  GetWindowRect(hPopupWnd, R);            // Added

  GetCursorPos(Pt);                       // Added

  if PtInRect(R, Pt) then                 // Added
    miHint.DoActivateHint(menuItem)
  else                                    // Added
    miHint.DoActivateHint(nil);           // Added


end;

constructor TMenuItemHint.Create(AOwner: TComponent);
begin
  inherited;

  showTimer := TTimer.Create(self);
  showTimer.Interval := Application.HintPause;

  hideTimer := TTimer.Create(self);
  hideTimer.Interval := Application.HintHidePause;

end;

destructor TMenuItemHint.Destroy;
begin
  hideTimer.OnTimer := nil;
  showTimer.OnTimer := nil;
  self.ReleaseHandle;
  inherited;
end;

procedure TMenuItemHint.DoActivateHint(menuItem: TMenuItem);
begin
  hideTime(self);

  if (menuItem = nil) or (menuItem.Hint = '') then
  begin
    activeMenuItem := nil;
    Exit;
  end;

  activeMenuItem := menuItem;

  showTimer.OnTimer := ShowTime;
  hideTimer.OnTimer := HideTime;
end;

procedure TMenuItemHint.HideTime(Sender: TObject);
begin
  self.ReleaseHandle;
  hideTimer.OnTimer := nil;
end;

procedure TMenuItemHint.ShowTime(Sender: TObject);
var
  r : TRect;
  wdth : integer;
  hght : integer;
begin
  if activeMenuItem <> nil then
  begin

    wdth := Canvas.TextWidth(activeMenuItem.Hint);
    hght := Canvas.TextHeight(activeMenuItem.Hint);

    r.Left := Mouse.CursorPos.X + 16;
    r.Top := Mouse.CursorPos.Y + 16;
    r.Right := r.Left + wdth + 6;
    r.Bottom := r.Top + hght + 4;

    ActivateHint(r,activeMenuItem.Hint);
  end;

  showTimer.OnTimer := nil;

end;

WM_MENUSELECT 告诉您菜单项是通过鼠标还是键盘选择的。

如果存在 MF_MOUSESELECT 标志,则使用 GetCursorPos() (or the VCL's TMouse.CursorPos wrapper), or GetMessagePos() 提供的鼠标坐标。

如果该标志不存在,使用 GetMenuItemRect() 获取指定菜单项的边界矩形的屏幕坐标,然后使用该矩形内的任意坐标(居中、底部边缘)等)。

您根本不应该尝试直接使用菜单 window,因此不要调用 FindWindow()GetWindowRect()PtInRect()