Hints para Itens de Menu? 

 

Quando o mouse passa por cima de um componente (um TButton, por exemplo) se a propriedade ShowHint for True e houver algum texto na propriedade Hint, a janela Hint/ToolTip será exibida para o componente.

Por design do Windows, mesmo se fixamos o valor da propriedade Hint para um item de Menu, o popup do Hint não será exibido. Porém, os itens de menu Iniciar exibem Hints, e o menu Favoritos do Internet Explorer também exibe hints de itens de menu.

Está bastante normal utilizar o evento OnHint da variável global Application, em aplicações Delphi, para exibir hints  (longos) de itens de menu em uma barra de estado.

O Windows não expõe as mensagens necessárias para suportar um evento OnMouseEnter tradicional.

Se quisermos adicionar hints popups de item de menu (tooltips) aos menus de aplicações Delphi, precisamos “apenas” controlar apropriadamente a mensagem WM_MenuSelect.

A classe TMenuItemHint - Hints para itens  de menu! 

Já que não podemos confiar no método Application.ActivateHint para exibir a janela de hint para itens de menu (pois o tratamento de menus é completamente controlando pelo Windows), para exibir a janela de hint, temos que criar nossa própria versão da mesma - derivando uma nova classe THintWindow.

Vejamos como criar uma classe TMenuItemHint - uma viúva de hint, que de fato é exibida para itens de menu! Em primeiro lugar, precisamos controlar a mensagem Windows WM_MENUSELECT:

 

type

   TForm1 = class(TForm)

   ...

   private

     procedure WMMenuSelect(var Msg: TWMMenuSelect) ; message WM_MENUSELECT;

   end

...

implementation

...

procedure TForm1.WMMenuSelect(var Msg: TWMMenuSelect) ;

var

   menuItem : TMenuItem;

   hSubMenu : HMENU;

begin

   inherited; // from TCustomForm (so that Application.Hint is assigned)

 

   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;

   miHint.DoActivateHint(menuItem);

end; (*WMMenuSelect*)

 

Informação rápida: a mensagem WM_MENUSELECT é enviada para janela owner do menu (Form1!), quando o usuário seleciona (não clica!) um item de menu. Usando o método FindItem da classe TMenu, podemos obter o item de menu atualmente selecionado. Os parâmetros do FindItem tem relação com as propriedades da mensagem recebida.

Uma vez que saibamos qual o item de menu por onde o mouse está passando, chamamos o método DoActivateHint da classe TMenuItemHint.

Nota: a variável miHint está definida como “var miHint: TMenuItemHint" e é criada no tratador de evento OnCreate do Formulário.

Agora, só resta a implementar a classe TMenuItemHint. Vejamos a parte da interface:

 

TMenuItemHint = class(THintWindow)

private

   activeMenuItem: TMenuItem;

   showTimer: TTimer;

   hideTimer: TTimer;

   procedure HideTime(Sender: TObject);

   procedure ShowTime(Sender: TObject);

public

   constructor Create(AOwner: TComponent); override;

   procedure DoActivateHint(menuItem: TMenuItem);

   destructor Destroy; override;

end;

 

Basicamente, a função DoActivateHint chama o método ActivateHint do THintWindow que usa a propriedade Hint  do TMenuItem (se for designada).

O showTimer é usado para garantir que o HintPause (da Application) ocorra antes do hint ser exibido. O hideTimer usa Application.HintHidePause para esconder a janela hint depois de um intervalo especificado.

Quando usaríamos Hints de Itens de Menu ? 

Apesar de que alguém poderia dizer que não é um bom projeto para exibir hints de itens de menu, há situações onde a exibição de hints  de itens de menu, de fato é muito melhor do que usar uma barra de estado. Uma lista de itens de menus “mais recentemente usados” (Most Recently Used - MRU) é tal caso. Um menu de barra de tarefa personalizado é outro.

Crie uma nova aplicação Delphi. No formulário principal coloque um MainMenu ("Menu1") (paleta Standard), um StatusBar (paleta Win32) e um ApplicationEvents (paleta Aditional).

Acrescente vários itens de menu ao menu. Designe uma propriedade Hint para alguns itens de menu, deixe alguns itens de menu "livres" de Hint.

A seguir, o código de fonte completo da unit, junto com a implementação da classe TMenuItemHint:

 

unit Unit1;

 

interface

 

uses

   Windows, Messages, SysUtils, Variants, Classes, Graphics,

   Controls, Forms,Dialogs, Menus, AppEvnts,

   StdCtrls, ExtCtrls, ComCtrls;

 

 

type

   TMenuItemHint = class(THintWindow)

   private

     activeMenuItem: TMenuItem;

     showTimer: TTimer;

     hideTimer: TTimer;

     procedure HideTime(Sender: TObject);

     procedure ShowTime(Sender: TObject);

   public

     constructor Create(AOwner: TComponent); override;

     procedure DoActivateHint(menuItem: TMenuItem) ;

     destructor Destroy; override;

   end;

 

   TForm1 = class(TForm)

...

     procedure FormCreate(Sender: TObject) ;

     procedure ApplicationEvents1Hint(Sender: TObject);

   private

     miHint : TMenuItemHint;

     procedure WMMenuSelect(var Msg: TWMMenuSelect); message WM_MENUSELECT;

   end;

 

var

   Form1: TForm1;

 

implementation

{$R *.dfm}

 

procedure TForm1.FormCreate(Sender: TObject);

begin

   miHint := TMenuItemHint.Create(self);

end; (*FormCreate*)

 

procedure TForm1.ApplicationEvents1Hint(Sender: TObject) ;

begin

   StatusBar1.SimpleText := 'App.OnHint : ' + Application.Hint;

end; (*Application.OnHint*)

 

procedure TForm1.WMMenuSelect(var Msg: TWMMenuSelect) ;

var

   menuItem: TMenuItem;

   hSubMenu: HMENU;

begin

   inherited; // from TCustomForm (ensures that Application.Hint is assigned)

 

   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;

 

   miHint.DoActivateHint(menuItem) ;

end; (*WMMenuSelect*)

 

 

{ TMenuItemHint }

constructor TMenuItemHint.Create(AOwner: TComponent) ;

begin

  inherited;

  showTimer := TTimer.Create(self) ;

  showTimer.Interval := Application.HintPause;

 

  hideTimer := TTimer.Create(self) ;

  hideTimer.Interval := Application.HintHidePause;

end; (*Create*)

 

destructor TMenuItemHint.Destroy;

begin

  hideTimer.OnTimer := nil;

  showTimer.OnTimer := nil;

  self.ReleaseHandle;

  inherited;

end; (*Destroy*)

 

procedure TMenuItemHint.DoActivateHint(menuItem: TMenuItem) ;

begin

  //force remove of the "old" hint window

  hideTime(self) ;

 

  if (menuItem = nil) or (menuItem.Hint = '') then

  begin

    activeMenuItem := nil;

    Exit;

  end;

 

  activeMenuItem := menuItem;

 

  showTimer.OnTimer := ShowTime;

  hideTimer.OnTimer := HideTime;

end; (*DoActivateHint*)

 

procedure TMenuItemHint.ShowTime(Sender: TObject) ;

var

  r: TRect;

  wdth: integer;

  hght: integer;

begin

  if activeMenuItem <> nil then

  begin

    //position and resize

    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; (*ShowTime*)

 

procedure TMenuItemHint.HideTime(Sender: TObject) ;

begin

  //hide (destroy) hint window

  self.ReleaseHandle;

  hideTimer.OnTimer := nil;

end; (*HideTime*)

 

end.