TabControlPainter.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. // Permission is hereby granted, free of charge, to any person obtaining
  2. // a copy of this software and associated documentation files (the
  3. // "Software"), to deal in the Software without restriction, including
  4. // without limitation the rights to use, copy, modify, merge, publish,
  5. // distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to
  7. // the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. //
  20. // Copyright (c) 2007 Novell, Inc.
  21. //
  22. // Authors:
  23. // Andreia Gaita ([email protected])
  24. using System;
  25. using System.Drawing;
  26. using System.Drawing.Text;
  27. using System.Windows.Forms;
  28. using System.Windows.Forms.VisualStyles;
  29. namespace System.Windows.Forms.Theming.Default
  30. {
  31. /// <summary>
  32. /// Summary description for TabControl.
  33. /// </summary>
  34. internal class TabControlPainter {
  35. protected SystemResPool ResPool { get { return ThemeEngine.Current.ResPool; } }
  36. #region private
  37. private Size defaultItemSize;
  38. private Point defaultPadding;
  39. private int minimumTabWidth;
  40. private Rectangle selectedTabDelta;
  41. private Point tabPanelOffset;
  42. private int selectedSpacing;
  43. private Size rowSpacingNormal;
  44. private Size rowSpacingButtons;
  45. private Size rowSpacingFlatButtons;
  46. private int scrollerWidth;
  47. private Point focusRectSpacing;
  48. private Rectangle tabPageSpacing;
  49. private int colSpacing;
  50. private int flatButtonSpacing;
  51. private Point imagePadding;
  52. private StringFormat defaultFormatting;
  53. private Rectangle borderThickness;
  54. #endregion
  55. #region Properties
  56. public virtual Size DefaultItemSize {
  57. get { return defaultItemSize; }
  58. set { defaultItemSize = value; }
  59. }
  60. public virtual Point DefaultPadding {
  61. get { return defaultPadding; }
  62. set { defaultPadding = value; }
  63. }
  64. public virtual int MinimumTabWidth {
  65. get { return minimumTabWidth; }
  66. set { minimumTabWidth = value; }
  67. }
  68. public virtual Rectangle SelectedTabDelta {
  69. get { return selectedTabDelta; }
  70. set { selectedTabDelta = value; }
  71. }
  72. public virtual Point TabPanelOffset {
  73. get { return tabPanelOffset; }
  74. set { tabPanelOffset = value; }
  75. }
  76. public virtual int SelectedSpacing {
  77. get { return selectedSpacing; }
  78. set { selectedSpacing = value; }
  79. }
  80. public virtual Size RowSpacingNormal {
  81. get { return rowSpacingNormal; }
  82. set { rowSpacingNormal = value; }
  83. }
  84. public virtual Size RowSpacingButtons {
  85. get { return rowSpacingButtons; }
  86. set { rowSpacingButtons = value; }
  87. }
  88. public virtual Size RowSpacingFlatButtons {
  89. get { return rowSpacingFlatButtons; }
  90. set { rowSpacingFlatButtons = value; }
  91. }
  92. public virtual Point FocusRectSpacing {
  93. get { return focusRectSpacing; }
  94. set { focusRectSpacing = value; }
  95. }
  96. public virtual int ColSpacing {
  97. get { return colSpacing; }
  98. set { colSpacing = value; }
  99. }
  100. public virtual int FlatButtonSpacing {
  101. get { return flatButtonSpacing; }
  102. set { flatButtonSpacing = value; }
  103. }
  104. public virtual Rectangle TabPageSpacing {
  105. get { return tabPageSpacing; }
  106. set { tabPageSpacing = value; }
  107. }
  108. public virtual Point ImagePadding {
  109. get { return imagePadding; }
  110. set { imagePadding = value; }
  111. }
  112. public virtual StringFormat DefaultFormatting {
  113. get { return defaultFormatting; }
  114. set { defaultFormatting = value; }
  115. }
  116. public virtual Rectangle BorderThickness {
  117. get { return borderThickness; }
  118. set { borderThickness = value; }
  119. }
  120. public virtual int ScrollerWidth {
  121. get { return scrollerWidth; }
  122. set { scrollerWidth = value; }
  123. }
  124. public virtual Size RowSpacing (System.Windows.Forms.TabControl tab) {
  125. switch (tab.Appearance) {
  126. case TabAppearance.Normal:
  127. return rowSpacingNormal;
  128. case TabAppearance.Buttons:
  129. return rowSpacingButtons;
  130. case TabAppearance.FlatButtons:
  131. return rowSpacingFlatButtons;
  132. default:
  133. throw new Exception ("Invalid Appearance value: " + tab.Appearance);
  134. }
  135. }
  136. #endregion
  137. public TabControlPainter ()
  138. {
  139. defaultItemSize = new Size (42, 16);
  140. defaultPadding = new Point (6, 3);
  141. selectedTabDelta = new Rectangle (2, 2, 4, 3);
  142. selectedSpacing = 0;
  143. rowSpacingNormal = new Size (0, 0);
  144. rowSpacingButtons = new Size (3, 3);
  145. rowSpacingFlatButtons = new Size (9, 3);
  146. colSpacing = 0;
  147. minimumTabWidth = 42;
  148. scrollerWidth = 17;
  149. focusRectSpacing = new Point (2, 2);
  150. tabPanelOffset = new Point (4, 0);
  151. flatButtonSpacing = 8;
  152. tabPageSpacing = new Rectangle (4, 2, 3, 4);
  153. imagePadding = new Point (2, 3);
  154. defaultFormatting = new StringFormat();
  155. // Horizontal Alignment is handled in the Draw method
  156. defaultFormatting.Alignment = StringAlignment.Near;
  157. defaultFormatting.LineAlignment = StringAlignment.Center;
  158. defaultFormatting.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
  159. defaultFormatting.HotkeyPrefix = HotkeyPrefix.Show;
  160. borderThickness = new Rectangle (1, 1, 2, 2);
  161. }
  162. public virtual Rectangle GetLeftScrollRect (System.Windows.Forms.TabControl tab)
  163. {
  164. switch (tab.Alignment) {
  165. case TabAlignment.Top:
  166. return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth * 2), tab.ClientRectangle.Top + 1, scrollerWidth, scrollerWidth);
  167. default:
  168. Rectangle panel_rect = GetTabPanelRect (tab);
  169. return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth * 2), panel_rect.Bottom + 2, scrollerWidth, scrollerWidth);
  170. }
  171. }
  172. public virtual Rectangle GetRightScrollRect (System.Windows.Forms.TabControl tab)
  173. {
  174. switch (tab.Alignment) {
  175. case TabAlignment.Top:
  176. return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth), tab.ClientRectangle.Top + 1, scrollerWidth, scrollerWidth);
  177. default:
  178. Rectangle panel_rect = GetTabPanelRect (tab);
  179. return new Rectangle (tab.ClientRectangle.Right - (scrollerWidth), panel_rect.Bottom + 2, scrollerWidth, scrollerWidth);
  180. }
  181. }
  182. public Rectangle GetDisplayRectangle (System.Windows.Forms.TabControl tab)
  183. {
  184. Rectangle ext = GetTabPanelRect (tab);
  185. // Account for border size
  186. return new Rectangle (ext.Left + tabPageSpacing.X, ext.Top + tabPageSpacing.Y,
  187. ext.Width - tabPageSpacing.X - tabPageSpacing.Width, ext.Height - tabPageSpacing.Y - tabPageSpacing.Height);
  188. }
  189. public Rectangle GetTabPanelRect (System.Windows.Forms.TabControl tab)
  190. {
  191. // Offset the tab page (panel) from the top corner
  192. Rectangle res = tab.ClientRectangle;
  193. if (tab.TabCount == 0)
  194. return res;
  195. int spacing = RowSpacing (tab).Height;
  196. int tabOffset = (tab.ItemSize.Height + spacing - selectedTabDelta.Height) * tab.RowCount + selectedTabDelta.Y;
  197. switch (tab.Alignment) {
  198. case TabAlignment.Top:
  199. res.Y += tabOffset;
  200. res.Height -= tabOffset;
  201. break;
  202. case TabAlignment.Bottom:
  203. res.Height -= tabOffset;
  204. break;
  205. case TabAlignment.Left:
  206. res.X += tabOffset;
  207. res.Width -= tabOffset;
  208. break;
  209. case TabAlignment.Right:
  210. res.Width -= tabOffset;
  211. break;
  212. }
  213. return res;
  214. }
  215. public virtual void Draw (Graphics dc, Rectangle area, TabControl tab)
  216. {
  217. DrawBackground (dc, area, tab);
  218. int start = 0;
  219. int end = tab.TabPages.Count;
  220. int delta = 1;
  221. if (tab.Alignment == TabAlignment.Top) {
  222. start = end;
  223. end = 0;
  224. delta = -1;
  225. }
  226. if (tab.SizeMode == TabSizeMode.Fixed)
  227. defaultFormatting.Alignment = StringAlignment.Center;
  228. else
  229. defaultFormatting.Alignment = StringAlignment.Near;
  230. int counter = start;
  231. for (; counter != end; counter += delta) {
  232. for (int i = tab.SliderPos; i < tab.TabPages.Count; i++) {
  233. if (i == tab.SelectedIndex)
  234. continue;
  235. if (counter != tab.TabPages[i].Row)
  236. continue;
  237. Rectangle rect = tab.GetTabRect (i);
  238. if (!rect.IntersectsWith (area))
  239. continue;
  240. DrawTab (dc, tab.TabPages[i], tab, rect, false);
  241. }
  242. }
  243. if (tab.SelectedIndex != -1 && tab.SelectedIndex >= tab.SliderPos) {
  244. Rectangle rect = tab.GetTabRect (tab.SelectedIndex);
  245. if (rect.IntersectsWith (area))
  246. DrawTab (dc, tab.TabPages[tab.SelectedIndex], tab, rect, true);
  247. }
  248. if (tab.ShowSlider) {
  249. Rectangle right = GetRightScrollRect (tab);
  250. Rectangle left = GetLeftScrollRect (tab);
  251. DrawScrollButton (dc, right, area, ScrollButton.Right, tab.RightSliderState);
  252. DrawScrollButton (dc, left, area, ScrollButton.Left, tab.LeftSliderState);
  253. }
  254. }
  255. protected virtual void DrawScrollButton (Graphics dc, Rectangle bounds, Rectangle clippingArea, ScrollButton button, PushButtonState state)
  256. {
  257. ControlPaint.DrawScrollButton (dc, bounds, button, GetButtonState (state));
  258. }
  259. static ButtonState GetButtonState (PushButtonState state)
  260. {
  261. switch (state) {
  262. case PushButtonState.Pressed:
  263. return ButtonState.Pushed;
  264. default:
  265. return ButtonState.Normal;
  266. }
  267. }
  268. protected virtual void DrawBackground (Graphics dc, Rectangle area, TabControl tab)
  269. {
  270. Brush brush = SystemBrushes.Control;
  271. dc.FillRectangle (brush, area);
  272. Rectangle panel_rect = GetTabPanelRect (tab);
  273. if (tab.Appearance == TabAppearance.Normal) {
  274. ControlPaint.DrawBorder3D (dc, panel_rect, Border3DStyle.RaisedInner, Border3DSide.Left | Border3DSide.Top);
  275. ControlPaint.DrawBorder3D (dc, panel_rect, Border3DStyle.Raised, Border3DSide.Right | Border3DSide.Bottom);
  276. }
  277. }
  278. protected virtual int DrawTab (Graphics dc, System.Windows.Forms.TabPage page, System.Windows.Forms.TabControl tab, Rectangle bounds, bool is_selected)
  279. {
  280. Rectangle interior;
  281. int res = bounds.Width;
  282. dc.FillRectangle (ResPool.GetSolidBrush (tab.BackColor), bounds);
  283. if (tab.Appearance == TabAppearance.Buttons || tab.Appearance == TabAppearance.FlatButtons) {
  284. // Separators
  285. if (tab.Appearance == TabAppearance.FlatButtons) {
  286. int width = bounds.Width;
  287. bounds.Width += (flatButtonSpacing - 2);
  288. res = bounds.Width;
  289. if (tab.Alignment == TabAlignment.Top || tab.Alignment == TabAlignment.Bottom)
  290. ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Etched, Border3DSide.Right);
  291. else
  292. ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Etched, Border3DSide.Top);
  293. bounds.Width = width;
  294. }
  295. if (is_selected) {
  296. ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Sunken, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom);
  297. } else if (tab.Appearance != TabAppearance.FlatButtons) {
  298. ThemeEngine.Current.CPDrawBorder3D (dc, bounds, Border3DStyle.Raised, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom);
  299. }
  300. } else {
  301. CPColor cpcolor = ResPool.GetCPColor (tab.BackColor);
  302. Pen light = ResPool.GetPen (cpcolor.LightLight);
  303. switch (tab.Alignment) {
  304. case TabAlignment.Top:
  305. dc.DrawLine (light, bounds.Left, bounds.Bottom - 1, bounds.Left, bounds.Top + 3);
  306. dc.DrawLine (light, bounds.Left, bounds.Top + 3, bounds.Left + 2, bounds.Top);
  307. dc.DrawLine (light, bounds.Left + 2, bounds.Top, bounds.Right - 3, bounds.Top);
  308. dc.DrawLine (SystemPens.ControlDark, bounds.Right - 2, bounds.Top + 1, bounds.Right - 2, bounds.Bottom - 1);
  309. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 2, bounds.Top + 1, bounds.Right - 1, bounds.Top + 2);
  310. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 1, bounds.Top + 2, bounds.Right - 1, bounds.Bottom - 1);
  311. break;
  312. case TabAlignment.Bottom:
  313. dc.DrawLine (light, bounds.Left, bounds.Top, bounds.Left, bounds.Bottom - 2);
  314. dc.DrawLine (light, bounds.Left, bounds.Bottom - 2, bounds.Left + 3, bounds.Bottom);
  315. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left + 3, bounds.Bottom, bounds.Right - 3, bounds.Bottom);
  316. dc.DrawLine (SystemPens.ControlDark, bounds.Left + 3, bounds.Bottom - 1, bounds.Right - 3, bounds.Bottom - 1);
  317. dc.DrawLine (SystemPens.ControlDark, bounds.Right - 2, bounds.Bottom - 1, bounds.Right - 2, bounds.Top + 1);
  318. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 2, bounds.Bottom - 1, bounds.Right - 1, bounds.Bottom - 2);
  319. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right - 1, bounds.Bottom - 2, bounds.Right - 1, bounds.Top + 1);
  320. break;
  321. case TabAlignment.Left:
  322. dc.DrawLine (light, bounds.Left - 2, bounds.Top, bounds.Right, bounds.Top);
  323. dc.DrawLine (light, bounds.Left, bounds.Top + 2, bounds.Left - 2, bounds.Top);
  324. dc.DrawLine (light, bounds.Left, bounds.Top + 2, bounds.Left, bounds.Bottom - 2);
  325. dc.DrawLine (SystemPens.ControlDark, bounds.Left, bounds.Bottom - 2, bounds.Left + 2, bounds.Bottom - 1);
  326. dc.DrawLine (SystemPens.ControlDark, bounds.Left + 2, bounds.Bottom - 1, bounds.Right, bounds.Bottom - 1);
  327. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left + 2, bounds.Bottom, bounds.Right, bounds.Bottom);
  328. break;
  329. default: // TabAlignment.Right
  330. dc.DrawLine (light, bounds.Left, bounds.Top, bounds.Right - 3, bounds.Top);
  331. dc.DrawLine (light, bounds.Right - 3, bounds.Top, bounds.Right, bounds.Top + 3);
  332. dc.DrawLine (SystemPens.ControlDark, bounds.Right - 1, bounds.Top + 1, bounds.Right - 1, bounds.Bottom - 1);
  333. dc.DrawLine (SystemPens.ControlDark, bounds.Left, bounds.Bottom - 1, bounds.Right - 2, bounds.Bottom - 1);
  334. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Right, bounds.Top + 3, bounds.Right, bounds.Bottom - 3);
  335. dc.DrawLine (SystemPens.ControlDarkDark, bounds.Left, bounds.Bottom, bounds.Right - 3, bounds.Bottom);
  336. break;
  337. }
  338. }
  339. Point padding = tab.Padding;
  340. interior = new Rectangle (bounds.Left + padding.X - 1, // substract a little offset
  341. bounds.Top + padding.Y,
  342. bounds.Width - (padding.X * 2),
  343. bounds.Height - (padding.Y * 2));
  344. if (tab.DrawMode == TabDrawMode.Normal && page.Text != null) {
  345. if (tab.Alignment == TabAlignment.Left) {
  346. dc.TranslateTransform (bounds.Left, bounds.Bottom);
  347. dc.RotateTransform (-90);
  348. dc.DrawString (page.Text, tab.Font,
  349. SystemBrushes.ControlText,
  350. tab.Padding.X - 2, // drawstring adds some extra unwanted leading spaces, so trimming
  351. tab.Padding.Y,
  352. defaultFormatting);
  353. dc.ResetTransform ();
  354. } else if (tab.Alignment == TabAlignment.Right) {
  355. dc.TranslateTransform (bounds.Right, bounds.Top);
  356. dc.RotateTransform (90);
  357. dc.DrawString (page.Text, tab.Font,
  358. SystemBrushes.ControlText,
  359. tab.Padding.X - 2, // drawstring adds some extra unwanted leading spaces, so trimming
  360. tab.Padding.Y,
  361. defaultFormatting);
  362. dc.ResetTransform ();
  363. } else {
  364. Rectangle str_rect = interior;
  365. if (is_selected) {
  366. // Reduce the interior size to match the inner size of non-selected tabs
  367. str_rect.X += selectedTabDelta.X;
  368. str_rect.Y += selectedTabDelta.Y;
  369. str_rect.Width -= selectedTabDelta.Width;
  370. str_rect.Height -= selectedTabDelta.Height;
  371. str_rect.Y -= selectedTabDelta.Y; // Move up the text / image of the selected tab
  372. }
  373. if (tab.ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < tab.ImageList.Images.Count) {
  374. int image_x;
  375. if (tab.SizeMode != TabSizeMode.Fixed) {
  376. image_x = str_rect.X;
  377. }
  378. else {
  379. image_x = str_rect.X + (str_rect.Width - tab.ImageList.ImageSize.Width) / 2;
  380. if (page.Text != null) {
  381. SizeF textSize = dc.MeasureString(page.Text, page.Font, str_rect.Size);
  382. image_x -= (int)(textSize.Width / 2);
  383. }
  384. }
  385. int image_y = str_rect.Y + (str_rect.Height - tab.ImageList.ImageSize.Height) / 2;
  386. tab.ImageList.Draw (dc, new Point (image_x, image_y), page.ImageIndex);
  387. str_rect.X += tab.ImageList.ImageSize.Width + 2;
  388. str_rect.Width -= tab.ImageList.ImageSize.Width + 2;
  389. }
  390. dc.DrawString (page.Text, tab.Font,
  391. SystemBrushes.ControlText,
  392. str_rect,
  393. defaultFormatting);
  394. }
  395. } else if (page.Text != null) {
  396. DrawItemState state = DrawItemState.None;
  397. if (page == tab.SelectedTab)
  398. state |= DrawItemState.Selected;
  399. DrawItemEventArgs e = new DrawItemEventArgs (dc,
  400. tab.Font, bounds, tab.IndexForTabPage (page),
  401. state, page.ForeColor, page.BackColor);
  402. tab.OnDrawItemInternal (e);
  403. return res;
  404. }
  405. // TabControl ignores the value of ShowFocusCues
  406. if (page.Parent.Focused && is_selected) {
  407. Rectangle focus_rect = bounds;
  408. focus_rect.Inflate (-2, -2);
  409. ThemeEngine.Current.CPDrawFocusRectangle (dc, focus_rect, tab.BackColor, tab.ForeColor);
  410. }
  411. return res;
  412. }
  413. public virtual bool HasHotElementStyles (TabControl tabControl) {
  414. return false;
  415. }
  416. }
  417. }