Control.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //-----------------------------------------------------------------------------
  2. // Control.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using Microsoft.Xna.Framework;
  10. using Microsoft.Xna.Framework.Graphics;
  11. namespace UserInterfaceSample.Controls
  12. {
  13. /// <summary>
  14. /// Control is the base class for a simple UI controls framework.
  15. ///
  16. /// Controls are grouped into a heirarchy: each control has a Parent and
  17. /// an optional list of Children. They draw themselves
  18. ///
  19. /// Input handling
  20. /// ==============
  21. /// Call HandleInput() once per update on the root control of the heirarchy; it will then call HandleInput() on
  22. /// its children. Controls can override HandleInput() to check for touch events and the like. If you override this
  23. /// function, you need to call base.HandleInput() to let your child controls see input.
  24. ///
  25. /// Note that Controls do not have any special system for setting TouchPanel.EnabledGestures; if you're using
  26. /// gesture-sensitive controls, you need to set EnabledGestures as appopriate in each GameScreen.
  27. ///
  28. /// Rendering
  29. /// =========
  30. /// Override Control.Draw() to render your control. Control.Draw() takes a 'DrawContext' struct, which contains
  31. /// the GraphicsDevice and other objects useful for drawing. It also contains a SpriteBatch, which will have had
  32. /// Begin() called *before* Control.Draw() is called. This allows batching of sprites across multiple controls
  33. /// to improve rendering speed.
  34. ///
  35. /// Layout
  36. /// ======
  37. /// Controls have a Position and Size, which defines a rectangle. By default, Size is computed
  38. /// auotmatically by an internal call to ComputeSize(), which each child control can implement
  39. /// as appropriate. For example, TextControl uses the size of the rendered text.
  40. ///
  41. /// If you *write* to the Size property, auto-sizing will be disabled, and the control will
  42. /// retain the written size unless you write to it again.
  43. ///
  44. /// There is no dynamic layout system. Instead, container controls (PanelControl in particular)
  45. /// contain methods for positioning their child controls into rows, columns, or other arrangements.
  46. /// Client code should create the controls needed for a screen, then call one or more of these
  47. /// layout functions to position them.
  48. /// </summary>
  49. public class Control
  50. {
  51. #region Private fields
  52. private Vector2 position;
  53. private Vector2 size;
  54. private bool sizeValid = false;
  55. private bool autoSize = true;
  56. private List<Control> children = null;
  57. #endregion
  58. #region Properties
  59. /// <summary>
  60. /// Draw() is not called unless Control.Visible is true (the default).
  61. /// </summary>
  62. public bool Visible = true;
  63. /// <summary>
  64. /// Position of this control within its parent control.
  65. /// </summary>
  66. public Vector2 Position
  67. {
  68. get
  69. {
  70. return position;
  71. }
  72. set
  73. {
  74. position = value;
  75. if (Parent != null)
  76. {
  77. Parent.InvalidateAutoSize();
  78. }
  79. }
  80. }
  81. /// <summary>
  82. /// Size if this control. See above for a discussion of the layout system.
  83. /// </summary>
  84. public Vector2 Size
  85. {
  86. // Default behavior is for ComputeSize() to determine the size, and then cache it.
  87. get
  88. {
  89. if (!sizeValid)
  90. {
  91. size = ComputeSize();
  92. sizeValid = true;
  93. }
  94. return size;
  95. }
  96. // Setting the size overrides whatever ComputeSize() would return, and disables autoSize
  97. set
  98. {
  99. size = value;
  100. sizeValid = true;
  101. autoSize = false;
  102. if (Parent != null)
  103. {
  104. Parent.InvalidateAutoSize();
  105. }
  106. }
  107. }
  108. /// <summary>
  109. /// Call this method when a control's content changes so that its size needs to be recomputed. This has no
  110. /// effect if autoSize has been disabled.
  111. /// </summary>
  112. protected void InvalidateAutoSize()
  113. {
  114. if (autoSize)
  115. {
  116. sizeValid = false;
  117. if (Parent != null)
  118. {
  119. Parent.InvalidateAutoSize();
  120. }
  121. }
  122. }
  123. /// <summary>
  124. /// The control containing this control, if any
  125. /// </summary>
  126. public Control Parent { get; private set; }
  127. /// <summary>
  128. /// Number of child controls of this control
  129. /// </summary>
  130. public int ChildCount { get { return children == null ? 0 : children.Count; } }
  131. /// <summary>
  132. /// Indexed access to the children of this control.
  133. /// </summary>
  134. public Control this[int childIndex]
  135. {
  136. get
  137. {
  138. return children[childIndex];
  139. }
  140. }
  141. #endregion
  142. #region Child control API
  143. public void AddChild(Control child)
  144. {
  145. if (child.Parent != null)
  146. {
  147. child.Parent.RemoveChild(child);
  148. }
  149. AddChild(child, ChildCount);
  150. }
  151. public void AddChild(Control child, int index)
  152. {
  153. if (child.Parent != null)
  154. {
  155. child.Parent.RemoveChild(child);
  156. }
  157. child.Parent = this;
  158. if (children == null)
  159. {
  160. children = new List<Control>();
  161. }
  162. children.Insert(index, child);
  163. OnChildAdded(index, child);
  164. }
  165. public void RemoveChildAt(int index)
  166. {
  167. Control child = children[index];
  168. child.Parent = null;
  169. children.RemoveAt(index);
  170. OnChildRemoved(index, child);
  171. }
  172. /// <summary>
  173. /// Remove the given control from this control's list of children.
  174. /// </summary>
  175. public void RemoveChild(Control child)
  176. {
  177. if(child.Parent != this)
  178. throw new InvalidOperationException();
  179. RemoveChildAt(children.IndexOf(child));
  180. }
  181. #endregion
  182. #region Virtual methods for derived classes to override
  183. /// <summary>
  184. ///
  185. /// </summary>
  186. /// <param name="context"></param>
  187. public virtual void Draw(DrawContext context)
  188. {
  189. Vector2 origin = context.DrawOffset;
  190. for(int i=0; i<ChildCount; i++)
  191. {
  192. Control child = children[i];
  193. if (child.Visible)
  194. {
  195. context.DrawOffset = origin + child.Position;
  196. child.Draw(context);
  197. }
  198. }
  199. }
  200. /// <summary>
  201. /// Called once per frame to update the control; override this method if your control requires custom updates.
  202. /// Call base.Update() to update any child controls.
  203. /// </summary>
  204. public virtual void Update(GameTime gametime)
  205. {
  206. for (int i = 0; i < ChildCount; i++)
  207. {
  208. children[i].Update(gametime);
  209. }
  210. }
  211. /// <summary>
  212. /// Called once per frame to update the control; override this method if your control requires custom updates.
  213. /// Call base.Update() to update any child controls.
  214. /// </summary>
  215. public virtual void HandleInput(InputState input)
  216. {
  217. for (int i = 0; i < ChildCount; i++)
  218. {
  219. children[i].HandleInput(input);
  220. }
  221. }
  222. /// <summary>
  223. /// Called when the Size property is read and sizeValid is false. Call base.ComputeSize() to compute the
  224. /// size (actually the lower-right corner) of all child controls.
  225. /// </summary>
  226. public virtual Vector2 ComputeSize()
  227. {
  228. if (children == null || children.Count == 0)
  229. {
  230. return Vector2.Zero;
  231. }
  232. else
  233. {
  234. Vector2 bounds = children[0].Position + children[0].Size;
  235. for (int i = 1; i < children.Count; i++)
  236. {
  237. Vector2 corner = children[i].Position + children[i].Size;
  238. bounds.X = Math.Max(bounds.X, corner.X);
  239. bounds.Y = Math.Max(bounds.Y, corner.Y);
  240. }
  241. return bounds;
  242. }
  243. }
  244. /// <summary>
  245. /// Called after a child control is added to this control. The default behavior is to call InvalidateAutoSize().
  246. /// </summary>
  247. protected virtual void OnChildAdded(int index, Control child)
  248. {
  249. InvalidateAutoSize();
  250. }
  251. /// <summary>
  252. /// Called after a child control is removed from this control. The default behavior is to call InvalidateAutoSize().
  253. /// </summary>
  254. protected virtual void OnChildRemoved(int index, Control child)
  255. {
  256. InvalidateAutoSize();
  257. }
  258. #endregion
  259. #region Static methods
  260. // Call this method once per frame on the root of your control heirarchy to draw all the controls.
  261. // See ControlScreen for an example.
  262. public static void BatchDraw(Control control, GraphicsDevice device, SpriteBatch spriteBatch, Vector2 offset, GameTime gameTime)
  263. {
  264. if (control != null && control.Visible)
  265. {
  266. spriteBatch.Begin();
  267. control.Draw(new DrawContext
  268. {
  269. Device = device,
  270. SpriteBatch = spriteBatch,
  271. DrawOffset = offset + control.Position,
  272. GameTime = gameTime
  273. });
  274. spriteBatch.End();
  275. }
  276. }
  277. #endregion
  278. }
  279. }