ThemePageControl.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <Common.h>
  5. #include <vsstyle.h>
  6. #include <memory>
  7. #include <PasTools.hpp>
  8. #include <TBXOfficeXPTheme.hpp>
  9. #include <StrUtils.hpp>
  10. #include <CustomWinConfiguration.h>
  11. #include "ThemePageControl.h"
  12. //---------------------------------------------------------------------------
  13. #pragma package(smart_init)
  14. //---------------------------------------------------------------------------
  15. // Based on
  16. // https://www.codeproject.com/Articles/6355/XP-Themes-Tab-Control-in-any-orientation
  17. //---------------------------------------------------------------------------
  18. #define IDS_UTIL_TAB L"TAB"
  19. //---------------------------------------------------------------------------
  20. static inline void ValidCtrCheck(TThemePageControl *)
  21. {
  22. new TThemePageControl(NULL);
  23. }
  24. //---------------------------------------------------------------------------
  25. namespace Themepagecontrol
  26. {
  27. void __fastcall PACKAGE Register()
  28. {
  29. TComponentClass classes[2] = {__classid(TThemePageControl), __classid(TThemeTabSheet)};
  30. RegisterComponents(L"Scp", classes, 1);
  31. }
  32. }
  33. //----------------------------------------------------------------------------------------------------------
  34. __fastcall TThemeTabSheet::TThemeTabSheet(TComponent * Owner) :
  35. TTabSheet(Owner)
  36. {
  37. FShadowed = false;
  38. FButton = ttbNone;
  39. FCaptionTruncation = tttNone;
  40. }
  41. //----------------------------------------------------------------------------------------------------------
  42. TThemePageControl * TThemeTabSheet::GetParentPageControl()
  43. {
  44. return DebugNotNull(dynamic_cast<TThemePageControl *>(Parent));
  45. }
  46. //----------------------------------------------------------------------------------------------------------
  47. void __fastcall TThemeTabSheet::Invalidate()
  48. {
  49. TThemePageControl * ThemePageControl = GetParentPageControl();
  50. if (DebugAlwaysTrue(ThemePageControl != NULL))
  51. {
  52. ThemePageControl->InvalidateTab(TabIndex);
  53. }
  54. else
  55. {
  56. Parent->Invalidate();
  57. }
  58. }
  59. //----------------------------------------------------------------------------------------------------------
  60. void __fastcall TThemeTabSheet::SetShadowed(bool Value)
  61. {
  62. if (Shadowed != Value)
  63. {
  64. FShadowed = Value;
  65. Invalidate();
  66. }
  67. }
  68. //----------------------------------------------------------------------------------------------------------
  69. void __fastcall TThemeTabSheet::SetButton(TThemeTabSheetButtons Value)
  70. {
  71. if (Button != Value)
  72. {
  73. FButton = Value;
  74. Invalidate();
  75. }
  76. }
  77. //----------------------------------------------------------------------------------------------------------
  78. void TThemeTabSheet::SetBaseCaption(const UnicodeString & value)
  79. {
  80. if (FBaseCaption != value)
  81. {
  82. FBaseCaption = value;
  83. UpdateCaption();
  84. }
  85. }
  86. //----------------------------------------------------------------------------------------------------------
  87. UnicodeString TThemeTabSheet::TruncatedCaption()
  88. {
  89. UnicodeString Result = FBaseCaption;
  90. TThemePageControl * ParentPageControl = GetParentPageControl();
  91. if (ParentPageControl->FSessionTabShrink > 0)
  92. {
  93. if (FCaptionTruncation == tttNone)
  94. {
  95. // noop
  96. }
  97. else if (FCaptionTruncation == tttEllipsis)
  98. {
  99. if (ParentPageControl->FSessionTabShrink == 1)
  100. {
  101. Result = Result.SubString(1, 1);
  102. }
  103. else if (ParentPageControl->FSessionTabShrink < Result.Length())
  104. {
  105. Result = Result.SubString(1, ParentPageControl->FSessionTabShrink - 1) + Ellipsis;
  106. }
  107. }
  108. else if (DebugAlwaysTrue(FCaptionTruncation == tttNoText))
  109. {
  110. Result = EmptyStr;
  111. }
  112. }
  113. return Result;
  114. }
  115. //----------------------------------------------------------------------------------------------------------
  116. void TThemeTabSheet::UpdateCaption()
  117. {
  118. UnicodeString ACaption = TruncatedCaption();
  119. TThemePageControl * ParentPageControl = GetParentPageControl();
  120. if (UseThemes() && (Button != ttbNone))
  121. {
  122. int OrigWidth = ParentPageControl->Canvas->TextWidth(ACaption);
  123. int TabButtonWidth = ParentPageControl->TabButtonSize();
  124. while (ParentPageControl->Canvas->TextWidth(ACaption) < OrigWidth + TabButtonWidth)
  125. {
  126. ACaption += L" ";
  127. }
  128. }
  129. Caption = ACaption;
  130. ParentPageControl->TabChanged(TabIndex);
  131. }
  132. //----------------------------------------------------------------------------------------------------------
  133. UnicodeString TThemeTabSheet::GetBaseCaption()
  134. {
  135. DebugAssert(StartsStr(FBaseCaption, Caption));
  136. DebugAssert(RightStr(Caption, Caption.Length() - FBaseCaption.Length()).Trim().Length() == 0);
  137. return FBaseCaption;
  138. }
  139. //----------------------------------------------------------------------------------------------------------
  140. void TThemeTabSheet::SetCaptionTruncation(TThemeTabCaptionTruncation Value)
  141. {
  142. if (FCaptionTruncation != Value)
  143. {
  144. FCaptionTruncation = Value;
  145. UpdateCaption();
  146. }
  147. }
  148. //----------------------------------------------------------------------------------------------------------
  149. //----------------------------------------------------------------------------------------------------------
  150. __fastcall TThemePageControl::TThemePageControl(TComponent * Owner) :
  151. TPageControl(Owner)
  152. {
  153. FOldTabIndex = -1;
  154. FHotTabButton = -1;
  155. FClickedButton = -1;
  156. FSessionTabShrink = 0;
  157. FOnTabButtonClick = NULL;
  158. FOnTabHint = NULL;
  159. }
  160. //----------------------------------------------------------------------------------------------------------
  161. int __fastcall TThemePageControl::GetTabsHeight()
  162. {
  163. // Calculated height includes tab/contents separator line on Windows 7/8,
  164. // but not on Windows XP
  165. TRect Rect = GetClientRect();
  166. ::SendMessage(Handle, TCM_ADJUSTRECT, FALSE, (LPARAM)&Rect);
  167. int Result = Rect.Top - 1;
  168. // Two different ways to calculate the same, not sure which one is more reliable,
  169. // so we want to know in case they differ.
  170. if (DebugAlwaysTrue(PageCount >= 0))
  171. {
  172. TRect Rect = TabRect(0);
  173. int Result2 = Rect.Bottom + 1;
  174. // On Windows 10 with 200% scaling, the first is 40, the second is 42.
  175. // With 250% scaling it's 50 vs 53.
  176. // Using the larger.
  177. if (Result2 > Result)
  178. {
  179. DebugAssert(IsWin10());
  180. Result = Result2;
  181. }
  182. }
  183. return Result;
  184. }
  185. //----------------------------------------------------------------------------------------------------------
  186. void __fastcall TThemePageControl::PaintWindow(HDC DC)
  187. {
  188. // Themes not enabled, give up
  189. if (!UseThemes())
  190. {
  191. TPageControl::PaintWindow(DC);
  192. return;
  193. }
  194. // TODO use GetClipBox
  195. TRect PageRect = GetClientRect();
  196. // 1st paint the tab body
  197. TRect ClientRect = PageRect;
  198. ::SendMessage(Handle, TCM_ADJUSTRECT, FALSE, (LPARAM)&PageRect);
  199. ClientRect.Top = PageRect.Top - 2;
  200. DrawThemesXpTabItem(DC, -1, ClientRect, true, 0, false);
  201. // 2nd paint the inactive tabs
  202. int SelectedIndex = TabIndex; // optimization
  203. for (int Tab = 0; Tab < PageCount; Tab++)
  204. {
  205. if (Tab != SelectedIndex)
  206. {
  207. DrawThemesXpTab(DC, Tab);
  208. }
  209. }
  210. if (SelectedIndex >= 0)
  211. {
  212. DrawThemesXpTab(DC, TabIndex);
  213. }
  214. }
  215. //----------------------------------------------------------------------------------------------------------
  216. TThemeTabSheetButtons __fastcall TThemePageControl::GetTabButton(int Index)
  217. {
  218. TThemeTabSheet * ThemeTabSheet = dynamic_cast<TThemeTabSheet *>(Pages[Index]);
  219. return (UseThemes() && (ThemeTabSheet != NULL)) ? ThemeTabSheet->Button : ttbNone;
  220. }
  221. //----------------------------------------------------------------------------------------------------------
  222. void __fastcall TThemePageControl::DrawThemesXpTab(HDC DC, int Tab)
  223. {
  224. TThemeTabSheet * ThemeTabSheet = dynamic_cast<TThemeTabSheet *>(Pages[Tab]);
  225. bool Shadowed = (ThemeTabSheet != NULL) ? ThemeTabSheet->Shadowed : false;
  226. TRect Rect = TabRect(Tab);
  227. ItemTabRect(Tab, Rect);
  228. int State;
  229. if (Tab != TabIndex)
  230. {
  231. TPoint Point = ScreenToClient(Mouse->CursorPos);
  232. int HotIndex = IndexOfTabAt(Point.X, Point.Y);
  233. State = (Tab == HotIndex ? TIS_HOT : (Shadowed ? TIS_DISABLED : TIS_NORMAL));
  234. }
  235. else
  236. {
  237. State = TIS_SELECTED;
  238. }
  239. DrawThemesXpTabItem(DC, Tab, Rect, false, State, Shadowed);
  240. }
  241. //----------------------------------------------------------------------------------------------------------
  242. // This function draws Themes Tab control parts: a) Tab-Body and b) Tab-tabs
  243. void __fastcall TThemePageControl::DrawThemesXpTabItem(HDC DC, int Item,
  244. const TRect & Rect, bool Body, int State, bool Shadowed)
  245. {
  246. TSize Size = Rect.Size;
  247. // Draw background
  248. HDC DCMem = CreateCompatibleDC(DC);
  249. HBITMAP BitmapMem = CreateCompatibleBitmap(DC, Size.Width, Size.Height);
  250. HBITMAP BitmapOld = (HBITMAP)SelectObject(DCMem, BitmapMem);
  251. TRect RectMem(0, 0, Size.Width, Size.Height);
  252. TRect RectItemMem(RectMem);
  253. if (!Body && (State == TIS_SELECTED))
  254. {
  255. RectMem.Bottom++;
  256. }
  257. if (Body)
  258. {
  259. DrawThemesPart(DCMem, TABP_PANE, State, IDS_UTIL_TAB, &RectMem);
  260. }
  261. else
  262. {
  263. DrawThemesPart(DCMem, TABP_TABITEM, State, IDS_UTIL_TAB, &RectMem);
  264. }
  265. // Init some extra parameters
  266. BITMAPINFO BitmapInfo;
  267. // Fill local pixel arrays
  268. ZeroMemory(&BitmapInfo, sizeof(BITMAPINFO));
  269. BITMAPINFOHEADER & BitmapInfoHeader = BitmapInfo.bmiHeader;
  270. BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
  271. BitmapInfoHeader.biCompression = BI_RGB;
  272. BitmapInfoHeader.biPlanes = 1;
  273. // force as RGB: 3 bytes,24 bits -> good for rotating bitmap in any resolution
  274. BitmapInfoHeader.biBitCount = 24;
  275. BitmapInfoHeader.biWidth = Size.Width;
  276. BitmapInfoHeader.biHeight = Size.Height;
  277. if (!Body && (Item >= 0))
  278. {
  279. DrawTabItem(DCMem, Item, Rect, RectItemMem, (State == TIS_SELECTED), Shadowed);
  280. }
  281. // Blit image to the screen
  282. BitBlt(DC, Rect.Left, Rect.Top, Size.Width, Size.Height, DCMem, 0, 0, SRCCOPY);
  283. SelectObject(DCMem, BitmapOld);
  284. DeleteObject(BitmapMem);
  285. DeleteDC(DCMem);
  286. }
  287. //----------------------------------------------------------------------------------------------------------
  288. void __fastcall TThemePageControl::ItemTabRect(int Item, TRect & Rect)
  289. {
  290. if (Item == TabIndex)
  291. {
  292. // Countered in TabButtonRect
  293. Rect.Inflate(2, 2);
  294. Rect.Bottom--;
  295. }
  296. }
  297. //----------------------------------------------------------------------------------------------------------
  298. void __fastcall TThemePageControl::ItemContentsRect(int Item, TRect & Rect)
  299. {
  300. bool Selected = (Item == TabIndex);
  301. Rect.Left += 6;
  302. Rect.Top += 2;
  303. if (Selected)
  304. {
  305. Rect.Bottom -= 2;
  306. Rect.Top += 1;
  307. }
  308. else
  309. {
  310. Rect.Bottom += 2;
  311. Rect.Top += 3;
  312. }
  313. }
  314. //----------------------------------------------------------------------------------------------------------
  315. bool __fastcall TThemePageControl::HasItemImage(int Item)
  316. {
  317. return (Images != NULL) && (Pages[Item]->ImageIndex >= 0);
  318. }
  319. //----------------------------------------------------------------------------------------------------------
  320. void __fastcall TThemePageControl::ItemTextRect(int Item, TRect & Rect)
  321. {
  322. if (HasItemImage(Item))
  323. {
  324. Rect.Left += Images->Width + 3;
  325. }
  326. else
  327. {
  328. Rect.Left -= 2;
  329. }
  330. Rect.Right -= 3;
  331. OffsetRect(&Rect, 0, ((Item == TabIndex) ? 0 : -2));
  332. }
  333. //----------------------------------------------------------------------------------------------------------
  334. void TThemePageControl::DrawCross(HDC DC, int Width, COLORREF Color, const TRect & Rect)
  335. {
  336. HPEN Pen = CreatePen(PS_SOLID, Width, Color);
  337. HPEN OldPen = static_cast<HPEN>(SelectObject(DC, Pen));
  338. // To-and-back - to make both ends look the same
  339. MoveToEx(DC, Rect.Left, Rect.Bottom - 1, NULL);
  340. LineTo(DC, Rect.Right - 1, Rect.Top);
  341. LineTo(DC, Rect.Left, Rect.Bottom - 1);
  342. MoveToEx(DC, Rect.Left, Rect.Top, NULL);
  343. LineTo(DC, Rect.Right - 1, Rect.Bottom - 1);
  344. LineTo(DC, Rect.Left, Rect.Top);
  345. SelectObject(DC, OldPen);
  346. DeleteObject(Pen);
  347. }
  348. //----------------------------------------------------------------------------------------------------------
  349. void TThemePageControl::DrawDropDown(HDC DC, int Radius, int X, int Y, COLORREF Color, int Grow)
  350. {
  351. // Optimized for even-sized Rect (100% scaling), may need adjustments for even-sized to correctly center
  352. TPoint Points[] = {
  353. Point(X - Radius - 1 - Grow, Y), Point(X + Radius + Grow, Y),
  354. Point(X, Y + Radius + Grow), Point(X - 1, Y + Radius + Grow)
  355. };
  356. HBRUSH Brush = CreateSolidBrush(Color);
  357. HPEN Pen = CreatePen(PS_SOLID, 1, Color);
  358. HGDIOBJ OldBrush = SelectObject(DC, Brush);
  359. HGDIOBJ OldPen = SelectObject(DC, Pen);
  360. Polygon(DC, Points, LENOF(Points));
  361. SelectObject(DC, OldPen);
  362. SelectObject(DC, OldBrush);
  363. }
  364. //----------------------------------------------------------------------------------------------------------
  365. // draw tab item context: possible icon and text
  366. void __fastcall TThemePageControl::DrawTabItem(
  367. HDC DC, int Item, TRect TabRect, TRect Rect, bool Selected, bool Shadowed)
  368. {
  369. ItemContentsRect(Item, Rect);
  370. UnicodeString Text = Pages[Item]->Caption;
  371. if (HasItemImage(Item))
  372. {
  373. int Left;
  374. if (!Text.IsEmpty())
  375. {
  376. Left = Rect.Left + (Selected ? 2 : 0);
  377. }
  378. else
  379. {
  380. Left = (Rect.Right - Images->Width - Rect.Left) / 2;
  381. }
  382. int Y = ((Rect.Top + Rect.Bottom - Images->Height) / 2) - 1 + (Selected ? 0 : -2);
  383. std::unique_ptr<TCanvas> Canvas(new TCanvas());
  384. Canvas->Handle = DC;
  385. Images->Draw(Canvas.get(), Left, Y, Pages[Item]->ImageIndex, !Shadowed);
  386. }
  387. int TextHeight = 20;
  388. int OldMode = SetBkMode(DC, TRANSPARENT);
  389. if (!Text.IsEmpty())
  390. {
  391. ItemTextRect(Item, Rect);
  392. HFONT OldFont = (HFONT)SelectObject(DC, Font->Handle);
  393. wchar_t * Buf = new wchar_t[Text.Length() + 1 + 4];
  394. wcscpy(Buf, Text.c_str());
  395. TRect TextRect(0, 0, Rect.Right - Rect.Left, TextHeight);
  396. // Truncates too long texts with ellipsis
  397. ::DrawText(DC, Buf, -1, &TextRect, DT_CALCRECT | DT_SINGLELINE | DT_MODIFYSTRING | DT_END_ELLIPSIS);
  398. DrawText(DC, Buf, -1, &Rect, DT_NOPREFIX | DT_CENTER);
  399. delete[] Buf;
  400. TThemeTabSheetButtons Button = GetTabButton(Item);
  401. if (Button != ttbNone)
  402. {
  403. Rect = TabButtonRect(Item);
  404. Rect.Offset(-TabRect.Left, -TabRect.Top);
  405. if (IsHotButton(Item))
  406. {
  407. HBRUSH Brush = CreateSolidBrush(GetSelectedBodyColor());
  408. FillRect(DC, &Rect, Brush);
  409. DeleteObject(Brush);
  410. HPEN Pen = CreatePen(PS_SOLID, 1, ColorToRGB(clHighlight));
  411. HPEN OldPen = static_cast<HPEN>(SelectObject(DC, Pen));
  412. Rectangle(DC, Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
  413. SelectObject(DC, OldPen);
  414. DeleteObject(Pen);
  415. }
  416. COLORREF BackColor = GetPixel(DC, Rect.Left + (Rect.Width() / 2), Rect.Top + (Rect.Height() / 2));
  417. COLORREF ShapeColor = ColorToRGB(Font->Color);
  418. #define BlendValue(FN) (((4 * static_cast<int>(FN(BackColor))) + static_cast<int>(FN(ShapeColor))) / 5)
  419. COLORREF BlendColor = RGB(BlendValue(GetRValue), BlendValue(GetGValue), BlendValue(GetBValue));
  420. #undef BlendValue
  421. if (Button == ttbClose)
  422. {
  423. int CrossPadding = GetCrossPadding();
  424. TRect CrossRect(Rect);
  425. CrossRect.Inflate(-CrossPadding, -CrossPadding);
  426. int CrossWidth = ScaleByTextHeight(this, 1);
  427. DrawCross(DC, CrossWidth + 1, BlendColor, CrossRect);
  428. DrawCross(DC, CrossWidth, ShapeColor, CrossRect);
  429. }
  430. else if (DebugAlwaysTrue(Button == ttbDropDown))
  431. {
  432. // See TTBXOfficeXPTheme.PaintDropDownArrow
  433. int Radius = ScaleByTextHeight(this, 2);
  434. int X = ((Rect.Left + Rect.Right)) / 2;
  435. int Y = ((Rect.Top + Rect.Bottom) / 2) - (Radius * 2 / 3);
  436. DrawDropDown(DC, Radius, X, Y, BlendColor, 1);
  437. DrawDropDown(DC, Radius, X, Y, ShapeColor, 0);
  438. }
  439. }
  440. SelectObject(DC, OldFont);
  441. }
  442. SetBkMode(DC, OldMode);
  443. }
  444. //----------------------------------------------------------------------------------------------------------
  445. int __fastcall TThemePageControl::TabButtonSize()
  446. {
  447. return ScaleByTextHeight(this, 16);
  448. }
  449. //----------------------------------------------------------------------------------------------------------
  450. int __fastcall TThemePageControl::GetCrossPadding()
  451. {
  452. return ScaleByTextHeight(this, 4);
  453. }
  454. //----------------------------------------------------------------------------------------------------------
  455. TRect __fastcall TThemePageControl::TabButtonRect(int Index)
  456. {
  457. TRect Rect = TabRect(Index);
  458. ItemTabRect(Index, Rect);
  459. ItemContentsRect(Index, Rect);
  460. ItemTextRect(Index, Rect);
  461. int ATabButtonSize = TabButtonSize();
  462. int CrossPadding = GetCrossPadding();
  463. TEXTMETRIC TextMetric;
  464. Canvas->Font = Font;
  465. GetTextMetrics(Canvas->Handle, &TextMetric);
  466. Rect.Top += TextMetric.tmAscent - ATabButtonSize + CrossPadding;
  467. Rect.Left = Rect.Right - ATabButtonSize - ScaleByTextHeight(this, 1);
  468. if (Index == TabIndex)
  469. {
  470. // To counter Inflate(2, 2) in ItemTabRect
  471. Rect.Left -= 2;
  472. }
  473. Rect.Right = Rect.Left + ATabButtonSize;
  474. Rect.Bottom = Rect.Top + ATabButtonSize;
  475. return Rect;
  476. }
  477. //----------------------------------------------------------------------------------------------------------
  478. bool TThemePageControl::IsHotButton(int Index)
  479. {
  480. // This was an attempt to allow tracking close buttons, even while drop down button menu is popped,
  481. // but MouseMove does not trigger then.
  482. return (Index == FClickedButton) || (Index == FHotTabButton);
  483. }
  484. //----------------------------------------------------------------------------------------------------------
  485. void TThemePageControl::TabChanged(int Index)
  486. {
  487. // When the "clicked" tab changes, it's probably not anymore the tab that was actually clicked.
  488. // For example, when the last tab is closed, it's replaced with either local-local tab (without the X button),
  489. // or removed altogether. The Login dialog pops up and when new session is opened, its tab's X button is rendered clicked,
  490. // until connection openning finishes (and WMLButtonDown finishes).
  491. if (Index == FClickedButton)
  492. {
  493. UpdateHotButton(FClickedButton, -1);
  494. }
  495. }
  496. //----------------------------------------------------------------------------------------------------------
  497. void TThemePageControl::UpdateHotButton(int & Ref, int Index)
  498. {
  499. if (Ref != Index)
  500. {
  501. bool WasHot = (Index >= 0) && IsHotButton(Index);
  502. int Prev = Ref;
  503. Ref = Index;
  504. if ((Prev >= 0) && !IsHotButton(Prev))
  505. {
  506. InvalidateTab(Prev);
  507. }
  508. if ((Index >= 0) && !WasHot)
  509. {
  510. InvalidateTab(Index);
  511. }
  512. }
  513. }
  514. //----------------------------------------------------------------------------------------------------------
  515. void __fastcall TThemePageControl::MouseMove(TShiftState Shift, int X, int Y)
  516. {
  517. TPageControl::MouseMove(Shift, X, Y);
  518. UpdateHotButton(FHotTabButton, IndexOfTabButtonAt(X, Y));
  519. }
  520. //----------------------------------------------------------------------------------------------------------
  521. int __fastcall TThemePageControl::IndexOfTabButtonAt(int X, int Y)
  522. {
  523. int Result = IndexOfTabAt(X, Y);
  524. if ((Result < 0) ||
  525. !GetTabButton(Result) ||
  526. !TabButtonRect(Result).Contains(TPoint(X, Y)))
  527. {
  528. Result = -1;
  529. }
  530. return Result;
  531. }
  532. //----------------------------------------------------------------------------------------------------------
  533. void __fastcall TThemePageControl::DrawThemesPart(HDC DC, int PartId,
  534. int StateId, LPCWSTR PartNameID, LPRECT Rect)
  535. {
  536. HTHEME Theme = OpenThemeData(NULL, PartNameID);
  537. if (Theme != 0)
  538. {
  539. DrawThemeBackground(Theme, DC, PartId, StateId, Rect, NULL);
  540. CloseThemeData(Theme);
  541. }
  542. }
  543. //----------------------------------------------------------------------------------------------------------
  544. bool __fastcall TThemePageControl::CanChange()
  545. {
  546. FOldTabIndex = ActivePageIndex;
  547. return TPageControl::CanChange();
  548. }
  549. //----------------------------------------------------------------------------------------------------------
  550. void __fastcall TThemePageControl::InvalidateTab(int Index)
  551. {
  552. if (HandleAllocated())
  553. {
  554. TRect Rect = TabRect(Index);
  555. if (Index == TabIndex)
  556. {
  557. Rect.Inflate(2, 2);
  558. }
  559. // Original code was invalidating range against parent window
  560. // (recalculating coordinates first)
  561. InvalidateRect(Handle, &Rect, true);
  562. }
  563. }
  564. //----------------------------------------------------------------------------------------------------------
  565. void __fastcall TThemePageControl::Change()
  566. {
  567. // note that TabIndex yields correct value already here,
  568. // while ActivePageIndex is not updated yet
  569. if ((FOldTabIndex >= 0) && (FOldTabIndex != TabIndex) && UseThemes())
  570. {
  571. InvalidateTab(FOldTabIndex);
  572. }
  573. TPageControl::Change();
  574. }
  575. //---------------------------------------------------------------------------
  576. void __fastcall TThemePageControl::WMLButtonDown(TWMLButtonDown & Message)
  577. {
  578. int Index = IndexOfTabButtonAt(Message.XPos, Message.YPos);
  579. if (Index >= 0)
  580. {
  581. Message.Result = 1;
  582. if (FOnTabButtonClick != NULL)
  583. {
  584. UpdateHotButton(FClickedButton, Index);
  585. try
  586. {
  587. FOnTabButtonClick(this, Index);
  588. }
  589. __finally
  590. {
  591. UpdateHotButton(FClickedButton, -1);
  592. }
  593. }
  594. }
  595. else
  596. {
  597. TPageControl::Dispatch(&Message);
  598. }
  599. }
  600. //---------------------------------------------------------------------------
  601. void TThemePageControl::CMHintShow(TCMHintShow & HintShow)
  602. {
  603. TPageControl::Dispatch(&HintShow);
  604. if (OnTabHint != NULL)
  605. {
  606. int Tab = IndexOfTabAt(HintShow.HintInfo->CursorPos.x, HintShow.HintInfo->CursorPos.y);
  607. OnTabHint(this, Tab, HintShow.HintInfo->HintStr);
  608. HintShow.HintInfo->CursorRect = TabRect(Tab);
  609. }
  610. }
  611. //---------------------------------------------------------------------------
  612. void __fastcall TThemePageControl::Dispatch(void * Message)
  613. {
  614. TMessage * M = reinterpret_cast<TMessage*>(Message);
  615. if (M->Msg == CM_MOUSELEAVE)
  616. {
  617. UpdateHotButton(FHotTabButton, -1);
  618. TPageControl::Dispatch(Message);
  619. }
  620. else if (M->Msg == WM_LBUTTONDOWN)
  621. {
  622. WMLButtonDown(*reinterpret_cast<TWMLButtonDown *>(M));
  623. }
  624. else if (M->Msg == WM_WANTS_SCREEN_TIPS)
  625. {
  626. M->Result = 1;
  627. }
  628. else if (M->Msg == CM_HINTSHOW)
  629. {
  630. CMHintShow(*reinterpret_cast<TCMHintShow *>(M));
  631. }
  632. else
  633. {
  634. TPageControl::Dispatch(Message);
  635. }
  636. }
  637. //----------------------------------------------------------------------------------------------------------
  638. TThemeTabSheet * TThemePageControl::GetPage(int Index)
  639. {
  640. return DebugNotNull(dynamic_cast<TThemeTabSheet *>(TPageControl::Pages[Index]));
  641. }
  642. //----------------------------------------------------------------------------------------------------------
  643. TThemeTabSheet * TThemePageControl::GetActivePage()
  644. {
  645. TTabSheet * TabSheet = TPageControl::ActivePage;
  646. TThemeTabSheet * Result = NULL;
  647. if (TabSheet != NULL)
  648. {
  649. Result = DebugNotNull(dynamic_cast<TThemeTabSheet *>(TabSheet));
  650. }
  651. return Result;
  652. }
  653. //----------------------------------------------------------------------------------------------------------
  654. int TThemePageControl::TotalTabsWidth()
  655. {
  656. TRect FirstTabRect = TabRect(0);
  657. TRect LastTabRect = TabRect(PageCount - 1);
  658. return -FirstTabRect.Left + LastTabRect.Right;
  659. }
  660. //----------------------------------------------------------------------------------------------------------
  661. void TThemePageControl::UpdateTabsCaptionTruncation()
  662. {
  663. DisableAlign();
  664. Tabs->BeginUpdate();
  665. try
  666. {
  667. FSessionTabShrink = 0;
  668. for (int Index = 0; Index < PageCount; Index++)
  669. {
  670. Pages[Index]->UpdateCaption();
  671. }
  672. int TabsWidth = TotalTabsWidth();
  673. int MaxWidth = ClientWidth - ScaleByTextHeight(this, 8); // arbitrary margin to avoid left/right buttons flicker
  674. if (TabsWidth > MaxWidth)
  675. {
  676. int NeedWidth = (TabsWidth - MaxWidth);
  677. int MaxLen = 0;
  678. int CaptionsWidth = 0;
  679. for (int Index = 0; Index < PageCount; Index++)
  680. {
  681. UnicodeString TabCaption = Pages[Index]->BaseCaption;
  682. MaxLen = std::max(MaxLen, TabCaption.Length());
  683. CaptionsWidth += Canvas->TextWidth(TabCaption);
  684. }
  685. bool Repeat;
  686. do
  687. {
  688. int NewShrink;
  689. if (FSessionTabShrink == 0)
  690. {
  691. NewShrink = MaxLen; // remove only new tab caption
  692. }
  693. else
  694. {
  695. NewShrink = FSessionTabShrink - 1;
  696. }
  697. if (NewShrink < 1)
  698. {
  699. Repeat = false;
  700. }
  701. else
  702. {
  703. FSessionTabShrink = NewShrink;
  704. int NewCaptionsWidth = 0;
  705. for (int Index = 0; Index < PageCount; Index++)
  706. {
  707. UnicodeString TabCaption = Pages[Index]->TruncatedCaption();
  708. NewCaptionsWidth += Canvas->TextWidth(TabCaption);
  709. }
  710. int GainedWidth = (CaptionsWidth - NewCaptionsWidth);
  711. Repeat = (GainedWidth < NeedWidth);
  712. }
  713. }
  714. while (Repeat);
  715. for (int Index = 0; Index < PageCount; Index++)
  716. {
  717. Pages[Index]->UpdateCaption();
  718. }
  719. }
  720. }
  721. __finally
  722. {
  723. Tabs->BeginUpdate();
  724. EnableAlign();
  725. }
  726. }
  727. //----------------------------------------------------------------------------------------------------------
  728. #ifdef _DEBUG
  729. void __fastcall TThemePageControl::RequestAlign()
  730. {
  731. TPageControl::RequestAlign();
  732. }
  733. #endif
  734. //----------------------------------------------------------------------------------------------------------