ThemePageControl.cpp 26 KB

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