PageRenderTime 73ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/Scenes/UserInterfaces/Controls/Panel.cs

#
C# | 613 lines | 391 code | 74 blank | 148 comment | 25 complexity | 7511b11eabd10194129000fd1e23522c MD5 | raw file
Possible License(s): Apache-2.0
  1. using System.Collections.Generic;
  2. using Delta.Engine;
  3. using Delta.InputSystem;
  4. using Delta.Rendering.Basics.Fonts;
  5. using Delta.Scenes.Enums;
  6. using Delta.Scenes.UserInterfaces.Designs;
  7. using Delta.Utilities;
  8. using Delta.Utilities.Datatypes;
  9. using Delta.Utilities.Datatypes.Advanced;
  10. using NUnit.Framework;
  11. namespace Delta.Scenes.UserInterfaces.Controls
  12. {
  13. /// <summary>
  14. /// Panel
  15. /// </summary>
  16. public class Panel : BaseControl
  17. {
  18. #region Padding (Public)
  19. /// <summary>
  20. /// Defines the padding for the inner content / controls of the panel.
  21. /// Note: NOT SUPPORTED YET
  22. /// </summary>
  23. /// <returns>Spacing</returns>
  24. public Margin Padding
  25. {
  26. get;
  27. set;
  28. }
  29. #endregion
  30. #region Protected
  31. #region FallbackDesign (Protected)
  32. /// <summary>
  33. /// Defines the theme which will be used if no "Theme" was set explicitely.
  34. /// </summary>
  35. protected override ControlDesign FallbackDesign
  36. {
  37. get
  38. {
  39. return Theme.Current.PanelDesign;
  40. } // get
  41. }
  42. #endregion
  43. #endregion
  44. #region Private
  45. #region horizontalScrollbar (Private)
  46. private readonly Scrollbar horizontalScrollbar;
  47. #endregion
  48. #region needCheckForScrollbars (Private)
  49. private bool needCheckForScrollbars;
  50. #endregion
  51. #endregion
  52. #region Constructors
  53. /// <summary>
  54. /// Create panel
  55. /// </summary>
  56. public Panel()
  57. {
  58. horizontalScrollbar = new Scrollbar(this);
  59. Add(horizontalScrollbar);
  60. horizontalScrollbar.State = ElementState.Invisible;
  61. needCheckForScrollbars = false;
  62. }
  63. #endregion
  64. #region Dispose (Public)
  65. /// <summary>
  66. /// Disposes the whole control (inclusive children).
  67. /// </summary>
  68. public override void Dispose()
  69. {
  70. // At first we need to destroy all Children of us
  71. List<BaseControl> rememberToRemove = new List<BaseControl>();
  72. foreach (BaseControl child in Children)
  73. {
  74. rememberToRemove.Add(child);
  75. } // foreach
  76. foreach (BaseControl child in rememberToRemove)
  77. {
  78. child.Dispose();
  79. } // foreach
  80. base.Dispose();
  81. }
  82. #endregion
  83. #region Add (Public)
  84. /// <summary>
  85. /// Add
  86. /// </summary>
  87. /// <param name="newChild">New child</param>
  88. public override bool Add(AlignableElement newChild)
  89. {
  90. bool wasControlAdded = base.Add(newChild);
  91. // We need to recheck if the scrollbar are needed now if a new control
  92. // was added because maybe it doesn't fit (fully) in the visible panel
  93. // space anymore or if a scrollbar is already shown then the available
  94. // "scroll space" needs to be updated anyway
  95. if (wasControlAdded)
  96. {
  97. // Note:
  98. // We only modify that flag for "successful actions" to avoid
  99. // "delete" this flag in the case that series of controls would be
  100. // added in one frame and the last one was a "bad" one
  101. needCheckForScrollbars = true;
  102. } // if
  103. return wasControlAdded;
  104. }
  105. #endregion
  106. #region Remove (Public)
  107. /// <summary>
  108. /// Remove
  109. /// </summary>
  110. /// <param name="childControl">Child control</param>
  111. public override bool Remove(AlignableElement childControl)
  112. {
  113. bool wasControlRemoved = base.Remove(childControl);
  114. // We need to recheck if the scrollbar are needed now if a control was
  115. // removed again because if the scrollbar is shown already the current
  116. // "scroll space" need to be updated or maybe the remaining control fit
  117. // now (fully) in the visible panel space and the scrollbar is not needed
  118. // anymore
  119. if (wasControlRemoved)
  120. {
  121. // Note:
  122. // We only modify that flag for "successful actions" to avoid
  123. // "delete" this flag in the case that series of controls would be
  124. // removed in one frame and the last one was a "bad" one
  125. needCheckForScrollbars = true;
  126. } // if
  127. return wasControlRemoved;
  128. }
  129. #endregion
  130. #region GetChildrenOfType (Public)
  131. /// <summary>
  132. /// Get children of type
  133. /// Recursive search for all children of Type T in this panel and all
  134. /// subpanels.
  135. /// </summary>
  136. /// <returns>List</returns>
  137. public List<T> GetChildrenOfType<T>() where T : class
  138. {
  139. List<T> foundChildren = new List<T>();
  140. for (int i = 0; i < Children.Count; i++)
  141. {
  142. if (Children[i] as Panel != null)
  143. {
  144. foundChildren.AddRange((Children[i] as Panel).GetChildrenOfType<T>());
  145. } // if
  146. else if ((Children[i] as T) != null)
  147. {
  148. foundChildren.Add(Children[i] as T);
  149. } // else if
  150. } // for
  151. return foundChildren;
  152. }
  153. #endregion
  154. #region GetScrolledOffset (Public)
  155. /// <summary>
  156. /// Get scrolled offset
  157. /// </summary>
  158. public float GetScrolledOffset()
  159. {
  160. return horizontalScrollbar.CurrentScrollOffset;
  161. }
  162. #endregion
  163. #region ScrollForeward (Public)
  164. /// <summary>
  165. /// Scroll foreward
  166. /// </summary>
  167. public void ScrollForeward()
  168. {
  169. //float horizontalContentWidth = GetContentWidth();
  170. //if (horizontalContentWidth <= Size.Width)
  171. //{
  172. // // It only make sense to scroll if the content area is bigger than the
  173. // // panel area
  174. // return;
  175. //} // if
  176. //float newScrollOffset = horizontalScrollbar.CurrentScrollOffset +
  177. // horizontalScrollbar.ScrollWidth;
  178. //if ((newScrollOffset + Size.Width) < horizontalContentWidth)
  179. //{
  180. // horizontalScrollbar.CurrentScrollOffset = newScrollOffset;
  181. //} // if
  182. //else
  183. //{
  184. // horizontalScrollbar.CurrentScrollOffset = horizontalContentWidth -
  185. // horizontalScrollbar.ScrollWidth;
  186. //} // else
  187. horizontalScrollbar.ScrollForewards();
  188. }
  189. #endregion
  190. #region Methods (Private)
  191. #region GetAlignmentArea
  192. /// <summary>
  193. /// Get alignment area
  194. /// </summary>
  195. /// <param name="totalArea">Total area</param>
  196. /// <param name="areaToAlign">Area to align</param>
  197. /// <param name="wishedAlignment">Wished alignment</param>
  198. internal static Rectangle GetAlignmentArea(Size totalArea, Size areaToAlign,
  199. Alignment wishedAlignment) //LOOK ?:, float borderThickness = 0.0f)
  200. {
  201. Point areaPos = Point.Zero;
  202. switch (wishedAlignment)
  203. {
  204. case Alignment.BottomLeft:
  205. areaPos.Y = totalArea.Height - areaToAlign.Height;
  206. break;
  207. case Alignment.Bottom:
  208. areaPos.X = totalArea.Width * 0.5f - areaToAlign.Width * 0.5f;
  209. areaPos.Y = totalArea.Height - areaToAlign.Height;
  210. break;
  211. case Alignment.BottomRight:
  212. areaPos.X = totalArea.Width - areaToAlign.Width;
  213. areaPos.Y = totalArea.Height - areaToAlign.Height;
  214. break;
  215. default:
  216. Log.Warning("Sorry, the Alignment '" + wishedAlignment +
  217. "' isn't supported yet.");
  218. break;
  219. } // switch
  220. return new Rectangle(areaPos, areaToAlign);
  221. }
  222. #endregion
  223. #region UpdateDrawData
  224. /// <summary>
  225. /// Updates all data of this element which are required for the following
  226. /// draw call.
  227. /// </summary>
  228. /// <remarks>
  229. /// This method will only be called if the element is in an enabled state.
  230. /// </remarks>
  231. protected override void UpdateDrawData()
  232. {
  233. base.UpdateDrawData();
  234. //return;
  235. if (needCheckForScrollbars)
  236. {
  237. horizontalScrollbar.State = (GetContentWidth() > Size.Width)
  238. ? ElementState.Enabled
  239. : ElementState.Invisible;
  240. } // if
  241. }
  242. #endregion
  243. #region DrawData
  244. /// <summary>
  245. /// Draws all data of this Panel which needs to be visualized.
  246. /// </summary>
  247. /// <remarks>
  248. /// This method will only be called if the Panel is in a visible state.
  249. /// </remarks>
  250. protected override void DrawData()
  251. {
  252. base.DrawData();
  253. //horizontalScrollbar.Run();
  254. }
  255. #endregion
  256. #region OnSizeChanging
  257. /// <summary>
  258. /// On size changing
  259. /// </summary>
  260. /// <param name="oldSize">Old size</param>
  261. /// <returns>
  262. /// 'True' if the new value can be used or 'false' if the change should be
  263. /// aborted.
  264. /// </returns>
  265. protected override bool OnSizeChanging(Size oldSize)
  266. {
  267. if (base.OnSizeChanging(oldSize))
  268. {
  269. // Notify the scrollbar that the size has changed
  270. horizontalScrollbar.LocalArea = GetAlignmentArea(Size,
  271. new Size(Size.Width, horizontalScrollbar.Size.Height),
  272. Alignment.Bottom);
  273. return true;
  274. } // if
  275. return false;
  276. }
  277. #endregion
  278. #region GetScrolledControls
  279. /// <summary>
  280. /// Get scrolled controls
  281. /// </summary>
  282. internal List<BaseControl> GetScrolledControls()
  283. {
  284. List<BaseControl> visibleControls = new List<BaseControl>();
  285. List<BaseControl> leftPartialControls = new List<BaseControl>();
  286. List<BaseControl> rightPartialControls = new List<BaseControl>();
  287. Rectangle visibleScrollArea = horizontalScrollbar.VisibleScrollArea;
  288. foreach (BaseControl element in Children)
  289. {
  290. Rectangle childLocalArea = element.LocalArea;
  291. // Checking which control is fully visible in the current scrolled
  292. // panel section
  293. if (childLocalArea.Left >= visibleScrollArea.Left &&
  294. childLocalArea.Right <= visibleScrollArea.Right)
  295. {
  296. visibleControls.Add(element);
  297. } // if
  298. // but still check for the other controls that aren't fully visible
  299. // because maybe some elements are at least partial visible
  300. // -> at least partial visible on the left side
  301. else if (childLocalArea.Left < visibleScrollArea.Left &&
  302. childLocalArea.Right > visibleScrollArea.Left)
  303. {
  304. leftPartialControls.Add(element);
  305. // Test:
  306. //visibleControls.Add(element);
  307. } // else if
  308. // -> at least partial visible on the right side
  309. else if (childLocalArea.Left < visibleScrollArea.Right &&
  310. childLocalArea.Right > visibleScrollArea.Right)
  311. {
  312. rightPartialControls.Add(element);
  313. // Test:
  314. //visibleControls.Add(element);
  315. } // else if
  316. } // foreach
  317. return visibleControls;
  318. }
  319. #endregion
  320. #region GetContentWidth
  321. /// <summary>
  322. /// Get content width
  323. /// </summary>
  324. internal float GetContentWidth()
  325. {
  326. float horizontalContentWidth = 0.0f;
  327. foreach (BaseControl element in Children)
  328. {
  329. if (element.LocalArea.Right > horizontalContentWidth)
  330. {
  331. horizontalContentWidth = element.LocalArea.Right;
  332. } // if
  333. } // foreach
  334. return horizontalContentWidth;
  335. }
  336. #endregion
  337. #endregion
  338. /// <summary>
  339. /// Tests for Panel controls
  340. /// </summary>
  341. [Category("Visual")]
  342. internal class PanelTests
  343. {
  344. #region Helpers
  345. #region DisplayTwoPanelsWithChildren
  346. /// <summary>
  347. /// This test shows two panel with several children, one where the
  348. /// children completely fit in the panel and no scrollbar is necessary and
  349. /// one panel where the children controls need more space than the panel
  350. /// has and a scrollbar is required.
  351. /// </summary>
  352. public static void DisplayTwoPanelsWithChildren()
  353. {
  354. Screen testScene = new Screen();
  355. Margin panelPadding = new Margin(0.01f);
  356. Panel fittingPanel = new Panel
  357. {
  358. //LocalArea = Rectangle.FromCenter(0.35f, 0.4f, 0.5f, 0.2f),
  359. LocalArea = new Rectangle(0.2f, 0.3f, 0.3f, 0.15f),
  360. CustomDesign = new ControlDesign
  361. {
  362. Background = BaseTheme.GetUIMaterial(Color.Yellow),
  363. },
  364. Padding = panelPadding,
  365. };
  366. testScene.Add(fittingPanel);
  367. Label singleItem = new Label
  368. {
  369. LocalArea = new Rectangle(panelPadding.Left, panelPadding.Top, 0.1f,
  370. 0.1f),
  371. CustomDesign = new TextControlDesign
  372. {
  373. Background = BaseTheme.GetUIMaterial(Color.Green),
  374. TextFont = new Font(Font.Default, Color.Brown),
  375. },
  376. Text = "Lonely Label",
  377. };
  378. fittingPanel.Add(singleItem);
  379. Panel toSmallPanel = new Panel
  380. {
  381. //LocalArea = Rectangle.FromCenter(0.35f, 0.4f, 0.5f, 0.2f),
  382. LocalArea = new Rectangle(0.2f, 0.5f, 0.3f, 0.15f),
  383. CustomDesign = new ControlDesign
  384. {
  385. Background = BaseTheme.GetUIMaterial(Color.Yellow),
  386. },
  387. Padding = panelPadding,
  388. };
  389. testScene.Add(toSmallPanel);
  390. BaseControl lastControl = null;
  391. for (int index = 0; index < 5; index++)
  392. {
  393. Label panelItem = new Label
  394. {
  395. LocalArea = new Rectangle(panelPadding.Left, panelPadding.Top, 0.1f,
  396. 0.1f),
  397. CustomDesign = new TextControlDesign
  398. {
  399. Background = BaseTheme.GetUIMaterial(Color.Green),
  400. TextFont = new Font(Font.Default, Color.Brown),
  401. },
  402. Text = "Label " + (index + 1),
  403. };
  404. if (lastControl != null)
  405. {
  406. panelItem.LocalArea = new Rectangle(panelItem.LocalArea.Left +
  407. lastControl.LocalArea.Right,
  408. panelItem.LocalArea.Top,
  409. panelItem.LocalArea.Width, panelItem.LocalArea.Height);
  410. } // if
  411. toSmallPanel.Add(panelItem);
  412. lastControl = panelItem;
  413. } // for
  414. // Open now the scene to "activate" for the test
  415. testScene.Open();
  416. //Font infoFont = Font.Default;
  417. Application.Start(delegate
  418. {
  419. if (Input.Keyboard.SpaceReleased)
  420. {
  421. toSmallPanel.ScrollForeward();
  422. } // if
  423. });
  424. }
  425. #endregion
  426. #endregion
  427. #region DisplayPanelWithChildren (Static)
  428. /// <summary>
  429. /// Display panel with children
  430. /// </summary>
  431. [Test]
  432. public static void DisplayPanelWithChildren()
  433. {
  434. Panel testPanel = new Panel
  435. {
  436. //LocalArea = Rectangle.FromCenter(0.35f, 0.4f, 0.5f, 0.2f),
  437. LocalArea = new Rectangle(0.2f, 0.3f, 0.3f, 0.15f),
  438. CustomDesign = new ControlDesign
  439. {
  440. Background = BaseTheme.GetUIMaterial(Color.Yellow),
  441. },
  442. //Padding =
  443. };
  444. float leftBorder = 0.01f;
  445. float topBorder = leftBorder;
  446. BaseControl lastControl = null;
  447. for (int index = 0; index < 7; index++)
  448. {
  449. Label panelItem = new Label
  450. {
  451. LocalArea = new Rectangle(leftBorder, topBorder, 0.1f, 0.1f),
  452. CustomDesign = new TextControlDesign
  453. {
  454. Background = BaseTheme.GetUIMaterial(Color.Green),
  455. TextFont = new Font(Font.Default, Color.Brown),
  456. },
  457. Text = "Label " + (index + 1),
  458. };
  459. if (lastControl != null)
  460. {
  461. panelItem.LocalArea = new Rectangle(panelItem.LocalArea.Left +
  462. lastControl.LocalArea.Right,
  463. panelItem.LocalArea.Top,
  464. panelItem.LocalArea.Width, panelItem.LocalArea.Height);
  465. } // if
  466. testPanel.Add(panelItem);
  467. lastControl = panelItem;
  468. } // for
  469. Screen testScene = new Screen();
  470. testScene.Add(testPanel);
  471. // Open now the scene to "activate" for the test
  472. testScene.Open();
  473. //Font infoFont = Font.Default;
  474. Application.Start(delegate
  475. {
  476. if (Input.Keyboard.SpaceReleased)
  477. {
  478. testPanel.ScrollForeward();
  479. } // if
  480. });
  481. }
  482. #endregion
  483. #region DisplayPanelWithButtons (Static)
  484. /// <summary>
  485. /// Display panel with children
  486. /// </summary>
  487. [Test]
  488. public static void DisplayPanelWithButtons()
  489. {
  490. Panel testPanel = new Panel
  491. {
  492. CustomDesign = new ControlDesign
  493. {
  494. Background = BaseTheme.GetUIMaterial(
  495. new Color(1.0f, 1.0f, 1.0f, 0.5f))
  496. },
  497. //Size = new Size(0.18f, 0.1f),
  498. //LocalPosition = new Point(0.5f, 0.5f),
  499. LocalArea = new Rectangle(0.2f, 0.3f, 0.3f, 0.15f),
  500. };
  501. // Create add and remove emitter buttons with some basic props.
  502. Button addEmitterButton = new Button
  503. {
  504. CustomDesign = new TextControlDesign
  505. {
  506. //Background = null,
  507. Background = BaseTheme.GetUIMaterial(Color.Black),
  508. TextFont = Font.Default,
  509. },
  510. State = ElementState.Enabled,
  511. Text = "A Test Button",
  512. LocalArea = new Rectangle(0.0f, 0.0f, 0.18f, 0.05f),
  513. };
  514. Button removeEmitterButton = new Button
  515. {
  516. CustomDesign = new TextControlDesign
  517. {
  518. Background = BaseTheme.GetUIMaterial(Color.Black),
  519. TextFont = Font.Default,
  520. },
  521. Text = "TEST ABC",
  522. LocalArea = new Rectangle(0.0f, addEmitterButton.Size.Height, 0.18f, 0.05f),
  523. };
  524. testPanel.Add(addEmitterButton);
  525. testPanel.Add(removeEmitterButton);
  526. testPanel.State = ElementState.Enabled;
  527. Screen testScene = new Screen();
  528. testScene.Add(testPanel);
  529. // Open now the scene to "activate" for the test
  530. testScene.Open();
  531. //Font infoFont = Font.Default;
  532. Application.Start(delegate
  533. {
  534. });
  535. }
  536. #endregion
  537. }
  538. }
  539. }