1
0

ThemePageControl.cpp 25 KB

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