Hint

Strona g│≤wna

 

HintWindow

O czym bΩdzie ten artyku│? Ot≤┐ o Hintach, czyli tzw. dymk≤w podpowiedzi, kt≤re pojawiaj▒ siΩ gdy nasuniesz kursor nad jaki╢ komponent. 
W Delphi mo┐na to bardzo │atwo stworzyµ, ale je┐eli chcemy uzyskaµ jakie╢ bardziej skomplikowane kszta│ty to trzeba trochΩ popisaµ :) 

Zacznijmy od rzeczy prostszych, czyli...

Standardowe Hinty

Po pierwsze musisz zmieniµ w│a╢ciwo╢µ ShowHint na True. Ka┐dy widoczny komponent posiada tak▒ w│a╢ciwo╢µ. Je┐eli ju┐ j▒ zmienisz to odnajd╝ pole Hint. Tutaj trzeba wpisaµ swoj▒ podpowied╝ - wpisz obojΩtnie jaki tekst. Teraz uruchom program i najed╝ kursorem na komponent - wy╢wietli siΩ dymek podpowiedzi. 

Teraz co╢ trudniejszego. Chcia│by╢ np., aby osobny tekst pojawia│ siΩ w dymku i osobny na komponencie. Mo┐esz u┐yµ do tego celu komponentu StatusBar, zmieniµ jego w│a╢ciwo╢µ SimpleBar na True oraz AutoHint na True.

Teraz ustawiaj▒c pole Hint jakiego╢ komponentu tekst podpowiedzi musisz wpisaµ w dw≤ch czΩ╢ciach oddzielaj▒c go od siebie znakiem | ( znak stawia siΩ naciskaj▒c klawisz Shift + \ ). 

Np. tekst podpowiedzi m≤g│by wygl▒daµ nastΩpuj▒co:

Jestem etykiet▒ | To jest etykieta na kt≤rej przechowuje siΩ tekst

Tekst po lewej oddzielony znakiem | wy╢wietli│ by siΩ w dymku, a tekst po prawej na komponencie StatusBar. 

Je┐eli chcia│by╢, aby tekst wy╢wietli│ siΩ gdzie indziej trzeba napisaµ kod :) W sekcji private umie╢µ taki oto nag│≤wek:

    procedure MyCoolHint(Sender: TObject);

Teraz uzupe│nij ten kod w sekcji Implementation:

procedure TMainForm.MyCoolHint(Sender: TObject);
begin
{
  Jezeli dlugosc podpowiedzi bedzie dluzsza niz 0 to wyswietl
  podpowiedz na pasku tytulowym formy.
}
  if Length(Application.Hint) > 0 then
    Caption := Application.Hint;
end;

Jak widzisz klasa Application posiada w│a╢ciwo╢µ Hint, kt≤ra okre╢la w│a╢nie dymek podpowiedzi. Zawiera ona tak┐e inne ciekawe opcje:

W│a╢ciwo╢µ Opis
HintColor Okre╢la kolor t│a podpowiedzi. 
HintHidePause Definiuje okre╢la czasu, w kt≤rym podpowied╝ zostaje ukryta. Domy╢lnie jest to 2500 milisekund.
HintPause Okre╢la czas jaki mija od chwili zatrzymania kursora nad komponentem do tego w jakim zostanie pokazana podpowied╝. Domy╢lnie 500 milisekund.
HintShortPause Je┐eli przemieszczasz siΩ kursorem pomiΩdzy kilkoma komponentem to nastΩpuje pewien czas, w kt≤rym stara podpowied╝ zostaje schowana, a nowa pokazana - to w│a╢nie definiuje ta w│a╢ciwo╢µ. Warto╢µ domy╢lna: 50 milisekund.  

 

Zaawansowane "dymki"

To wymaga trochΩ wiΩcej kodu. Delphi posiada klasΩ THintWindow, kt≤ra odpowiedzialna jest za dymki. Nale┐y wiΩc zdefiniowaµ klasΩ pochodn▒ do THintWindow:

TMyHint = class(THintWindow)
  private
    FRegion: THandle;
    procedure FreeRegion;
  public
    destructor Destroy; override;
    procedure ActivateHint(ARect: TRect; const AHint: string); override;
    procedure Paint; override;
  end;

Zauwa┐, ┐e wiΩkszo╢µ metod jest przedefiniowana tzn., ┐e istniej▒ w klasie bazowej. [ je┐eli nie wiesz o co chodzi radzΩ przeczytaµ artyku│ Klasy ]. 

W tym przyk│adzie stworzymy bardziej zaawansowan▒ klasΩ, kt≤ra zamiast prostok▒ta wy╢wietli elipsΩ z tekstem 3D. Do tego celu bΩdziemy musieli u┐yµ region≤w, aby stworzyµ kszta│t elipsy za pomoc▒ funkcji API. Najpierw wygenerujmy procedurΩ ActivateHint - to w niej bΩdzie siΩ odbywaµ tworzenie nowego regionu: 

procedure TMyHint.ActivateHint(ARect: TRect; const AHint: string);
begin
{  Ustaw dodatkowy margines po prawej stronie }
  with ARect do
    Right := Right + Canvas.TextWidth('XXXXX');

  BoundsRect := ARect; // przypisz zmienna do w│a╢ciwo╢ci
  FreeRegion; // zwolnij dotychczasowy region

  with BoundsRect do
{
  Stworz nowy region w postaci elipsy.
}
    FRegion := CreateRoundRectRgn(0, 0, Width, Height, Width, Height);

  if FRegion <> 0 then
    SetWindowRgn(Handle, FRegion, True); // ustaw nowy region
  inherited ActivateHint(ARect, AHint); // wywolaj procedure z klasy bazowej
end;

Na pocz▒tku z prawej strony dodawany jest dodatkowy margines. Funkcja TextWidth przelicza na piksele d│ugo╢µ tekstu podanego w nawiasie. NaastΩpnie do zmiennej BoundsRect, kt≤ra jest typu TRect zostaje przypisana zmienn▒ ARect, kt≤ra okre╢la rozmiary regionu - podpowiedzi. Najwa┐niejsze jednak nastΩpuje w linii, w kt≤rej tworzona zostaje elipsa. Zostaje ona przypisana do zmiennej FRegion. NastΩpnie przy pomocy polecenia SetWindowRgn zostaje ustawiony nowy region w postaci elipsy - mamy ju┐ konstrukcjΩ dymku. 

Tylko konstrukcjΩ bowiem nale┐y w tym dymku umie╢ciµ jaki╢ tekst: 

procedure TMyHint.Paint;
var
  R: TRect;
begin
  R := ClientRect; // pobierz rozmiary okna dymku
  Inc(R.Left, 1);  // przesun o piksel w lewo

  Canvas.Font.Color := clSilver; // ustaw czcionke na bial

{  narysuj tekst wysrodkowany w pionie i w poziomie }
  DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R,
           DT_NOPREFIX or DT_WORDBREAK or DT_CENTER or DT_VCENTER);

  Inc(R.Left, 2); // przesun o kolejne dwa piksele
  Canvas.Brush.Style := bsClear; // ustaw na przezroczyste
  Canvas.Font.Color := clBlack; // zmien czcionke na czarna

{  narysuj tekst ponownie inna czcionka z przesunieciem - daje efekt cienia }
    DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R,
           DT_NOPREFIX or DT_WORDBREAK or DT_CENTER or DT_VCENTER);
end;

Tutaj wszystko odbywa siΩ za pomoc▒ funkcji API - DrawText. Jest to polecenie, kt≤re umo┐liwia narysowanie tekstu wycentrowanego w pionie jak i w poziomie. 
Wszystko jest rysowane dwa razy, aby daµ efekt cienia. Pierwszy tekst rysowany jest z czcionk▒ koloru srebrnego, a drugi raz koloru czarnego z minimalnym przesuniΩciem. 

To w│a╢ciwie wszystko co najwa┐niejsze. Trzeba jeszcze napisaµ jedn▒ procedurΩ zwi▒zan▒ z regionami, a mianowicie zwalniaj▒ca owy region. 

procedure TMyHint.FreeRegion;
begin
  if FRegion <> 0 then // Czy region nie jest uzywany?
  begin
    SetWindowRgn(Handle, 0, True); // ustaw na brak regionu
    DeleteObject(FRegion); // usun region
    FRegion := 0;
  end;
end;

Ok - wykorzystanie nowej klasy mo┐esz ju┐ sprawdziµ. Umie╢µ w module sekcje initialization i jako klase podpowiedzi ( HintWindowClass ) przypisz przed chwil▒ stworzon▒ klasΩ: 

initialization
  HintWindowClass := TMyHint; // przypisz nowa klase podpowiedzi
  Application.HintColor := clWhite; // ustaw tlo Hinta na bialy

Gotowe. Oto ca│y kod programu: 

(****************************************************************)
(*                                                              *)
(*                  THintWindow test programme                  *)
(*      Copyright (c) 2001 by Adam Boduch  <28.05.2001>         *)
(*                HTTP://PROGRAMOWANIE.OF.PL                    *)
(*              E - mail:  boduch@poland.com                    *)
(*                                                              *)
(****************************************************************)

unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TMainForm = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
  private
  { procedura wyswietlajca podpowiedz na pasku formy }
    procedure MyCoolHint(Sender: TObject);
  end;

{ nowa klasa podpowiedzi dziedziczaca z klasy THintWindow }
TMyHint = class(THintWindow)
  private
    FRegion: THandle;
    procedure FreeRegion;
  public
    destructor Destroy; override;
    procedure ActivateHint(ARect: TRect; const AHint: string); override;
    procedure Paint; override;
  end;


var
  MainForm: TMainForm;

implementation

{$R *.DFM}

destructor TMyHint.Destroy;
begin
  FreeRegion; // wywolaj procedure
  inherited Destroy;
end;

procedure TMyHint.FreeRegion;
begin
  if FRegion <> 0 then // Czy region nie jest uzywany?
  begin
    SetWindowRgn(Handle, 0, True); // ustaw na brak regionu
    DeleteObject(FRegion); // usun region
    FRegion := 0;
  end;
end;

procedure TMyHint.ActivateHint(ARect: TRect; const AHint: string);
begin
{  Ustaw dodatkowy margines po prawej stronie }
  with ARect do
    Right := Right + Canvas.TextWidth('XXXXX');

  BoundsRect := ARect; // przypisz zmienna do wlasciwosci
  FreeRegion; // zwolnij dotychczasowy region

  with BoundsRect do
{
  Stworz nowy region w postaci elipsy.
}
    FRegion := CreateRoundRectRgn(0, 0, Width, Height, Width, Height);

  if FRegion <> 0 then
    SetWindowRgn(Handle, FRegion, True); // ustaw nowy region
  inherited ActivateHint(ARect, AHint); // wywolaj procedure z klasy bazowej
end;

procedure TMyHint.Paint;
var
  R: TRect;
begin
  R := ClientRect; // pobierz rozmiary okna dymku
  Inc(R.Left, 1);  // przesun o piksel w lewo

  Canvas.Font.Color := clSilver; // ustaw czcionke na bial

{  narysuj tekst wysrodkowany w pionie i w poziomie }
  DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R,
           DT_NOPREFIX or DT_WORDBREAK or DT_CENTER or DT_VCENTER);

  Inc(R.Left, 2); // przesun o kolejne dwa piksele
  Canvas.Brush.Style := bsClear; // ustaw na przezroczyste
  Canvas.Font.Color := clBlack; // zmien czcionke na czarna

{ narysuj tekst ponownie inna czcionka z przesunieciem - daje efekt cienia }
    DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R,
           DT_NOPREFIX or DT_WORDBREAK or DT_CENTER or DT_VCENTER);
end;


procedure TMainForm.MyCoolHint(Sender: TObject);
begin
{
  Jezeli dlugosc podpowiedzi bedzie dluzsza niz 0 to wyswietl
  podpowiedz na pasku tytulowym formy.
}
  if Length(Application.Hint) > 0 then
    Caption := Application.Hint;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
// ustaw procedure podpowiedzi na wczesniej zdefiniowana
  Application.OnHint := MyCoolHint; 
end;

initialization
  HintWindowClass := TMyHint; // przypisz nowa klase podpowiedzi
  Application.HintColor := clWhite; // ustaw tlo Hinta na bialy

end.


Oczywi╢cie mo┐esz tworzyµ bardziej skomplikowane kszta│ty ( w procedurze ActivateHint ) lub w samym dymku podpowiedzi umieszczaµ np. bitmapkΩ ( procedura Paint ). 

Klikaj▒c na poni┐szy link mo┐esz ╢ci▒gn▒µ gotowy program wykorzystuj▒cy napisan▒ wcze╢niej klasΩ. 

HintWindow.zip ( 3 kB )

Adam Boduch