Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

Více o výpisu textu do okna - nastavení parametrů.

6.1.2002

Minulé pokračování jsme ukončili tím, že jsme si tím nejjednodušším způsobem vypsali do okna text tak, aby zůstal na svém místě po celou dobu běhu programu. V té souvislosti jsme si řekli obecně jak funguje překreslování okna v reakci na zprávu WM_PAINT. V tomto pokračování se naučíme o trochu více o funkcích GDI, tedy kreslení do okna, přičemž jak jsem již uvedl kreslením budu mít nadále na mysli také výstup textu. Nejdříve si řekneme o další funkci pro výpis textu, kterou je DrawText:
int DrawText(
  HDC hDC,          // handle  kontextu zařízení
  LPCTSTR lpString, // text
  int nCount,       // délka textu
  LPRECT lpRect,    // obdélník
  UINT uFormat      // volby
);
Tato funkce se liší od funkce TextOut především tím, že cílové souřadnice nejsou určeny souřadnicemi počátku textu, ale obdélníkem (struktura RECT), do kterého je text formátován způsobem určeným parametrem uFormat. Navíc je zde drobná výhoda v tom, že tato funkce akceptuje jako parametr nCount (délka textu) hodnotu -1, při které si sama spočítá celkovou délku zadaného textu a správně jej vypíše. Řekněme si o některých běžně používaných hodnotách, ze kterých se skládá parametr uFormat:
  • DT_CENTER - vystředí text horizontálně
  • DT_LEFT -  zarovná text horizontálně vlevo
  • DT_RIGHT - zarovná text horizontálně vpravo
  • DT_SINGLELINE - vypíše text do jedné řádky i kdyby obsahoval znaky konce řádků. Tento parametr je dále významný proto, že následující formátovací volby fungují pouze za současného použití DT_SINGLELINE.
  • DT_VCENTER - vystředí text vertikálně
  • DT_BOTTOM - zarovná text ke spodnímu okraji obdélníka lpRect
Další formátovací parametry umožňují s textem dělat ještě více, zatím je zde nebudu vysvětlovat, zájemci je samozřejmě naleznou v dokumentaci. Ukážeme si tedy, jak dosáhnout toho, aby náš text byl stále zobrazován uprostřed okna. Přesně řečeno uprostřed obdélníka jeho klientského oblasti. Pro lepší budoucí přehlednost našeho kódu, který budeme postupně rozšiřovat, si budeme pro jednotlivé postupy vytvářet vlastní funkce s parametrem HDC získaným v handleru zprávy WM_PAINT, z kterého tyto funkce budeme volat, případně jejich volání "zapoznámkujeme" pro lepší přehlednost výsledného výstupu. Vytvoříme si tedy funkci pro zmíněné vycentrování textu:
void centerText(HDC hdc)
{
  RECT rect;
  GetClientRect(g_hwndMain, &rect);
  DrawText(hdc, "Text uprostřed okna", -1, &rect,
    DT_SINGLELINE | DT_CENTER | DT_VCENTER);
 }
Tuto funkci si zavoláme z handleru zprávy WM_PAINT, který jsme si vytvořili již minule:
void OnWM_PAINT()
{
  PAINTSTRUCT ps;
  HDC hdc;
  hdc =  BeginPaint(g_hwndMain, &ps);
  centerText(hdc);
  EndPaint(g_hwndMain, &ps);
}
Když nyní program spustíme, text bude uprostřed okna. Když budeme okno roztahovat za okraj, text se bude automaticky překreslovat tak aby byl stále uprostřed. Ale pozor! Automatické překreslování textu, resp. celé klientské oblasti okna je závislé na nastavení stylu třídy okna. Když se podíváte do funkce InitApp, kde jsme registrovali třídu, najdete tam tento řádek:
wc.style = CS_HREDRAW | CS_VREDRAW;
Tyto vlastnosti ve stylu třídy určují, jak jejich název napovídá, zda se při horizontální či vertikální změně velikosti okna má překreslit celá klientská oblast. Když si zkusíte hodnotu wc.style nastavit na 0, zjistíte, že při roztažení okna zůstane text stále na místě do té doby, než bude jeho překreslení vyvoláno "standardním" způsobem, tedy například, skrytím a znovuodkrytím okna. Nyní provedeme změnu v nastavení pozadí okna. Nastavíme jeho barvu tak, aby odpovídala systémové barvě prostorových prvků. Změníme proto jeden řádek ve funkci InitApp takto:
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
Po spuštění programu zjistíme, že již to není to pravé ořechové.

win-api-6-1

Bílý podklad textu nepůsobí příliš esteticky. Bývá zvykem, že texty se vypisují jako průhledné s podkladem odpovídajícím barvě, nebo i vzorku okna. Musíme se tedy naučit nastavovat barevné parametry textového výstupu. Je důležité vědět, že následující funkce, kterými se budeme zabývat, nastaví příslušnou vlastnost kontextu zařízení a nikoli textu nebo jen některé funkci. Znamená to, že po nastavení některé z vlastností bude tato vlastnost aplikována na všechny následné volání funkcí pro výstup textu (zatím používáme 2 - TextOut a DrawText). Na druhou stranu toto nastavení se "ztratí", jakmile ukončíme kreslení funkcí EndPaint. Nyní již ke třem základním funkcím, nastavujícím barvy textového výstupu. Nastavení barvy písma:
COLORREF SetTextColor(
  HDC hdc,         // handle kontextu zařízení
  COLORREF crColor // barva textu
);
Nastavení barvy pozadí textu:
COLORREF SetBkColor(
  HDC hdc,         // handle  kontextu zařízení
  COLORREF crColor // barva pozadí
);
Nastavení průhlednosti textu:
COLORREF SetBkMode(
  HDC hdc,        // handle  kontextu zařízení
  int iBkMode     // průhlednost
);
Hodnota crColor použitá u prvních 2 funkcí je 32-bitové číslo, v jehož nejnižší BYTE obsahuje hodnotu červené složky barvy, následující zelené a modré. Nejvyšší BYTE musí být 0. V tomto seriálu se postupně dostaneme k funkcím, resp. makrům, umožňujícím pohodlnější práci s typem COLORREF. Běžně se požívá hexadecimální zápis ve tvaru 0x00BBGGRR, kde BB, GG a RR jsou ony barevné složky v rozsahu 0x00 - 0xFF. Parametr iBkMode ve funkci SetBkMode může nabývat 2 předdefinovaných hodnot:
  • OPAQUE - pozadí textu je vyplněno barvou nastavenou funkcí SetBkColor.
  • TRANSPARENT - průhledný text, pozadí textu je tvořeno pozadím okna.
Tímto jsme se tedy dostali k odpovědi na otázku, jak vypsat text průhledný, jak to bývá zvykem a jak to i lépe vypadá. Ukažme si tedy na závěr praktickou realizaci. Nejprve se podívejte na výsledek:

win-api-6-2

A takto vypadá zdrojový kód funkce, kterou si zavoláme z funkce OnWM_PAINT:
void pozadiTextu(HDC hdc)
{
  TCHAR chText[100];
  lstrcpy(chText, TEXT("Modrá barva písma..."));
  SetTextColor(hdc, 0x00FF0000); // modrý text
  TextOut(hdc, 10, 10, chText, lstrlen(chText));
  SetBkColor(hdc, 0x0000FFFF); // žluté pozadí
  lstrcpy(chText, TEXT("Změníme pozadí, písmo zůstává..."));
  TextOut(hdc, 10, 40, chText, lstrlen(chText));
  lstrcpy(chText, TEXT("Konečně máme průhledný text !!!"));
  SetBkMode(hdc, TRANSPARENT); // průhlednost textu
  TextOut(hdc, 10, 70, chText, lstrlen(chText));
  lstrcpy(chText, TEXT("Pokračování příště ..."));
  SetTextColor(hdc, 0x000000FF); // modrý text
  TextOut(hdc, 10, 110, chText, lstrlen(chText));
}
Doprovodný projekt (Visual C++ 6) je ke stažení zde: win_api_6.zip