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 |.
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.
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:
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: [email protected] *)
(* *)
(****************************************************************)
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).
Zaawansowane hinty [efekt transparentu przeźroczystości]:
Polecam Unity: psvRndHint, psvTransparentHint ze stronki
http://cvs.sourceforge.net/viewcvs.py/psvlib/hint/
skad mozan sciagnac komponent do pokazywania baloników ?? zeby same wyskakiwały