{"id":30743,"date":"2006-06-26T10:00:11","date_gmt":"2006-06-26T10:00:11","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2006\/06\/26\/coding-in-place-tooltips\/"},"modified":"2006-06-26T10:00:11","modified_gmt":"2006-06-26T10:00:11","slug":"coding-in-place-tooltips","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060626-11\/?p=30743","title":{"rendered":"Coding in-place tooltips"},"content":{"rendered":"<p>\nToday we&#8217;ll look at how to implement in-place tooltips.\nThese are tooltips that appear when the user hovers the mouse\nover a string that cannot be displayed in its entirety.\nThe tooltip overlays the partially-displayed text and provides\nthe remainder of the text that had been truncated.\nThe keys to this technique are the\n<code>TTN_SHOW<\/code> notification (which lets you adjust\nthe positioning of a tooltip before it is shown) and\nthe <code>TTM_ADJUSTRECT<\/code> message which tells you\nprecisely where you need the tooltip to be.\n<\/p>\n<p>\nStart with our\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">\nscratch program<\/a>\nand add the following:\n<\/p>\n<pre>\nHFONT g_hfTT;\nHWND g_hwndTT;\nRECT g_rcText;\nLPCTSTR g_pszText = TEXT(\"<a HREF=\"http:\/\/www.lipsum.com\/\">Lorem ipsum<\/a> dolor sit amet.\");\nconst int c_xText = 50;\nconst int c_yText = 50;\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n g_hwndTT = CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, NULL,\n                           TTS_NOPREFIX,\n                           0, 0, 0, 0,\n                           hwnd, NULL, g_hinst, NULL);\n if (!g_hwndTT) return FALSE;\n g_hfTT = GetStockFont(ANSI_VAR_FONT);\n SetWindowFont(g_hwndTT, g_hfTT, FALSE);\n HDC hdc = GetDC(hwnd);\n HFONT hfPrev = SelectFont(hdc, g_hfTT);\n SIZE siz;\n GetTextExtentPoint(hdc, g_pszText, lstrlen(g_pszText), &amp;siz);\n SetRect(&amp;g_rcText, c_xText, c_yText,\n                    c_xText + siz.cx, c_yText + siz.cy);\n SelectFont(hdc, hfPrev);\n ReleaseDC(hwnd, hdc);\n TOOLINFO ti = { sizeof(ti) };\n ti.uFlags = TTF_TRANSPARENT | TTF_SUBCLASS;\n ti.hwnd = hwnd;\n ti.uId = 0;\n ti.lpszText = const_cast&lt;LPTSTR&gt;(g_pszText);\n ti.rect = g_rcText;\n SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM)&amp;ti);\n return TRUE;\n}\nvoid\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n HFONT hfPrev = SelectFont(pps-&gt;hdc, g_hfTT);\n TextOut(pps-&gt;hdc, g_rcText.left, g_rcText.top,\n         g_pszText, lstrlen(g_pszText));\n SelectFont(pps-&gt;hdc, hfPrev);\n}\n<\/pre>\n<p>\nAfter declaring a few variables, we dig into our\ncomputations at window creation.\nWe create the tooltip window, passing ourselves as the\nowner window.\n(Passing ourselves as the owner window is important in order\nto get proper Z-order behavior.\nI refer the reader to the fifth of my &#8220;Five Things Every\nWin32 Developer Should Know&#8221; topics for further details.)\nWe then obtain our font and set it into the tooltip control\nso that the tooltip renders in the same font we do.\n(I&#8217;ll take up more complex font manipulation in a future entry.)\nWe then measure our text in the target font and set\nthe <code>g_rcText<\/code> rectangle to the dimensions of\nthat text.\nWe use that rectangle to establish the boundaries of a tool\nin the tooltip control.\nBy setting the <code>TTF_SUBCLASS<\/code> flag,\nwe indicate that the tooltip control should subclass\nour window in order to intercept mouse messages.\nThis is a convenience to avoid us having to use the\n<code>TTM_RELAYEVENT<\/code> message to forward the mouse\nmessages manually.\nThis hooks up the tooltip.\n<\/p>\n<p>\nPainting the content is a simple matter of selecting the\nfont and drawing the text.\n<\/p>\n<p>\nRun this program and hover over the text.\nThe tooltip appears, but it&#8217;s in the wrong place.\nAside from that, though, things are working as expected.\nThe tooltip has the correct font,\nit fires only when the mouse is over the text itself,\nand it dismisses when the mouse leaves the text.\nLet&#8217;s position the tooltip:\n<\/p>\n<pre>\nLRESULT\nOnTooltipShow(HWND hwnd, NMHDR *pnm)\n{\n RECT rc = g_rcText;\n MapWindowRect(hwnd, NULL, &amp;rc);\n SendMessage(pnm-&gt;hwndFrom, TTM_ADJUSTRECT, TRUE, (LPARAM)&amp;rc);\n SetWindowPos(pnm-&gt;hwndFrom, 0, rc.left, rc.top, 0, 0,\n   SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);\n return TRUE; \/\/ suppress default positioning\n}\nLRESULT\nOnNotify(HWND hwnd, int idFrom, NMHDR *pnm)\n{\n if (pnm-&gt;hwndFrom == g_hwndTT) {\n  switch (pnm-&gt;code) {\n  case TTN_SHOW:\n   return OnTooltipShow(hwnd, pnm);\n  }\n }\n return 0;\n}\n\/\/ Add to WndProc\n    HANDLE_MSG(hwnd, WM_NOTIFY, OnNotify);\n<\/pre>\n<p>\nThe <code>TTN_SHOW<\/code> notification is sent\nwhen the tooltip is about to be displayed.\nWe respond to the notification by mapping the\ntext rectangle to screen coordinates and\nusing the <code>TTM_ADJUSTRECT<\/code> message\nto expand the rectangle to include all the\nmargins and borders that the tooltip control\nwill place around the text.\nThat way, when we position the tooltip at that\nlocation, the margins and borders match up\nprecisely, and the text appears at the desired location.\nIt is important to return <code>TRUE<\/code>\nto indicate to the tooltip control that we\ntook care of positioning the window\nand it should not do its default positioning.\n<\/p>\n<p>\nWhen you run this program, you will find one more problem:\nTooltip animations are still taking place,\nwhich is particularly distracting if\nthe animation is a slide animation.\nThis is easy to fix:\nTweak the way we create the tooltip control.\n<\/p>\n<pre>\n g_hwndTT = CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, NULL,\n                           TTS_NOPREFIX <font COLOR=\"blue\">| TTS_NOANIMATE<\/font>,\n                           0, 0, 0, 0,\n                           hwnd, NULL, g_hinst, NULL);\n<\/pre>\n<p>\nThe <code>TTS_NOANIMATE<\/code> style suppress animations,\nwhich means that the tooltip simply pops into place,\nexactly what we want.\n<\/p>\n<p>\nSo there you have it&mdash;the basics of in-place tooltips.\nOf course, there are many details you may wish to deal with,\nsuch as showing the tooltip only if the string is clipped.\nBut those issues are independent of in-place tooltips,\nso I won&#8217;t go into them here.\nWe&#8217;ll look at selected aspects of tooltips in future installments.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today we&#8217;ll look at how to implement in-place tooltips. These are tooltips that appear when the user hovers the mouse over a string that cannot be displayed in its entirety. The tooltip overlays the partially-displayed text and provides the remainder of the text that had been truncated. The keys to this technique are the TTN_SHOW [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-30743","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Today we&#8217;ll look at how to implement in-place tooltips. These are tooltips that appear when the user hovers the mouse over a string that cannot be displayed in its entirety. The tooltip overlays the partially-displayed text and provides the remainder of the text that had been truncated. The keys to this technique are the TTN_SHOW [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/30743","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=30743"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/30743\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=30743"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=30743"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=30743"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}