ThemePageControl.cpp 25 KB

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