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()
。
我从网站“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()
。