MenuScreen.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //-----------------------------------------------------------------------------
  2. // MenuScreen.cs
  3. //
  4. // 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. using Microsoft.Xna.Framework.Input.Touch;
  12. using Microsoft.Xna.Framework.Input;
  13. namespace GameStateManagement
  14. {
  15. /// <summary>
  16. /// Base class for screens that contain a menu of options. The user can
  17. /// move up and down to select an entry, or cancel to back out of the screen.
  18. /// </summary>
  19. abstract class MenuScreen : GameScreen
  20. {
  21. // the number of pixels to pad above and below menu entries for touch input
  22. //const int menuEntryPadding = 35;
  23. const int menuEntryPadding = 10;
  24. List<MenuEntry> menuEntries = new List<MenuEntry>();
  25. int selectedEntry = 0;
  26. string menuTitle;
  27. /// <summary>
  28. /// Gets the list of menu entries, so derived classes can add
  29. /// or change the menu contents.
  30. /// </summary>
  31. protected IList<MenuEntry> MenuEntries
  32. {
  33. get { return menuEntries; }
  34. }
  35. /// <summary>
  36. /// Constructor.
  37. /// </summary>
  38. public MenuScreen(string menuTitle)
  39. {
  40. // menus generally only need Tap for menu selection
  41. EnabledGestures = GestureType.Tap;
  42. this.menuTitle = menuTitle;
  43. TransitionOnTime = TimeSpan.FromSeconds(0.5);
  44. TransitionOffTime = TimeSpan.FromSeconds(0.5);
  45. }
  46. /// <summary>
  47. /// Allows the screen to create the hit bounds for a particular menu entry.
  48. /// </summary>
  49. protected virtual Rectangle GetMenuEntryHitBounds(MenuEntry entry)
  50. {
  51. // the hit bounds are the entire width of the screen, and the height of the entry
  52. // with some additional padding above and below.
  53. return new Rectangle(
  54. 0,
  55. (int)entry.Position.Y - menuEntryPadding,
  56. ScreenManager.GraphicsDevice.Viewport.Width,
  57. entry.GetHeight(this) + (menuEntryPadding * 2));
  58. }
  59. /// <summary>
  60. /// Responds to user input, changing the selected entry and accepting
  61. /// or cancelling the menu.
  62. /// </summary>
  63. public override void HandleInput(InputState input)
  64. {
  65. // we cancel the current menu screen if the user presses the back button
  66. PlayerIndex player;
  67. if (input.IsNewButtonPress(Buttons.Back, ControllingPlayer, out player))
  68. {
  69. OnCancel(player);
  70. }
  71. if (input.IsMenuDown(ControllingPlayer)) {
  72. if (selectedEntry < menuEntries.Count - 1)
  73. selectedEntry += 1;
  74. }
  75. if (input.IsMenuUp(ControllingPlayer)) {
  76. if (selectedEntry > 0)
  77. selectedEntry -= 1;
  78. }
  79. if (input.IsMenuSelect(ControllingPlayer, out player)) {
  80. menuEntries[selectedEntry].OnSelectEntry(player);
  81. }
  82. if (input.MouseGesture.HasFlag(MouseGestureType.LeftClick)) {
  83. Point clickLocation = new Point((int)input.CurrentMousePosition.X, (int)input.CurrentMousePosition.Y);
  84. // iterate the entries to see if any were tapped
  85. for (int i = 0; i < menuEntries.Count; i++)
  86. {
  87. MenuEntry menuEntry = menuEntries[i];
  88. if (GetMenuEntryHitBounds(menuEntry).Contains(clickLocation))
  89. {
  90. // select the entry. since gestures are only available on Windows Phone,
  91. // we can safely pass PlayerIndex.One to all entries since there is only
  92. // one player on Windows Phone.
  93. OnSelectEntry(i, PlayerIndex.One);
  94. }
  95. }
  96. }
  97. if (input.MouseGesture.HasFlag(MouseGestureType.Move)) {
  98. Point clickLocation = new Point((int)input.CurrentMousePosition.X, (int)input.CurrentMousePosition.Y);
  99. // iterate the entries to see if any were tapped
  100. for (int i = 0; i < menuEntries.Count; i++)
  101. {
  102. MenuEntry menuEntry = menuEntries[i];
  103. if (GetMenuEntryHitBounds(menuEntry).Contains(clickLocation))
  104. {
  105. // select the entry. since gestures are only available on Windows Phone,
  106. // we can safely pass PlayerIndex.One to all entries since there is only
  107. // one player on Windows Phone.
  108. //OnSelectEntry(i, PlayerIndex.One);
  109. selectedEntry = i;
  110. }
  111. }
  112. }
  113. // look for any taps that occurred and select any entries that were tapped
  114. foreach (GestureSample gesture in input.Gestures)
  115. {
  116. if (gesture.GestureType == GestureType.Tap)
  117. {
  118. // convert the position to a Point that we can test against a Rectangle
  119. Point tapLocation = new Point((int)gesture.Position.X, (int)gesture.Position.Y);
  120. // iterate the entries to see if any were tapped
  121. for (int i = 0; i < menuEntries.Count; i++)
  122. {
  123. MenuEntry menuEntry = menuEntries[i];
  124. if (GetMenuEntryHitBounds(menuEntry).Contains(tapLocation))
  125. {
  126. // select the entry. since gestures are only available on Windows Phone,
  127. // we can safely pass PlayerIndex.One to all entries since there is only
  128. // one player on Windows Phone.
  129. OnSelectEntry(i, PlayerIndex.One);
  130. }
  131. }
  132. }
  133. }
  134. }
  135. /// <summary>
  136. /// Handler for when the user has chosen a menu entry.
  137. /// </summary>
  138. protected virtual void OnSelectEntry(int entryIndex, PlayerIndex playerIndex)
  139. {
  140. menuEntries[entryIndex].OnSelectEntry(playerIndex);
  141. }
  142. /// <summary>
  143. /// Handler for when the user has cancelled the menu.
  144. /// </summary>
  145. protected virtual void OnCancel(PlayerIndex playerIndex)
  146. {
  147. ExitScreen();
  148. }
  149. /// <summary>
  150. /// Helper overload makes it easy to use OnCancel as a MenuEntry event handler.
  151. /// </summary>
  152. protected void OnCancel(object sender, PlayerIndexEventArgs e)
  153. {
  154. OnCancel(e.PlayerIndex);
  155. }
  156. /// <summary>
  157. /// Allows the screen the chance to position the menu entries. By default
  158. /// all menu entries are lined up in a vertical list, centered on the screen.
  159. /// </summary>
  160. protected virtual void UpdateMenuEntryLocations()
  161. {
  162. // Make the menu slide into place during transitions, using a
  163. // power curve to make things look more interesting (this makes
  164. // the movement slow down as it nears the end).
  165. float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
  166. // start at Y = 175; each X value is generated per entry
  167. Vector2 position = new Vector2(0f, 175f);
  168. // update each menu entry's location in turn
  169. for (int i = 0; i < menuEntries.Count; i++)
  170. {
  171. MenuEntry menuEntry = menuEntries[i];
  172. // each entry is to be centered horizontally
  173. position.X = ScreenManager.GraphicsDevice.Viewport.Width / 2 - menuEntry.GetWidth(this) / 2;
  174. if (ScreenState == ScreenState.TransitionOn)
  175. position.X -= transitionOffset * 256;
  176. else
  177. position.X += transitionOffset * 512;
  178. // set the entry's position
  179. menuEntry.Position = position;
  180. // move down for the next entry the size of this entry plus our padding
  181. position.Y += menuEntry.GetHeight(this) + (menuEntryPadding * 2);
  182. }
  183. }
  184. /// <summary>
  185. /// Updates the menu.
  186. /// </summary>
  187. public override void Update(GameTime gameTime, bool otherScreenHasFocus,
  188. bool coveredByOtherScreen)
  189. {
  190. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  191. // Update each nested MenuEntry object.
  192. for (int i = 0; i < menuEntries.Count; i++)
  193. {
  194. bool isSelected = IsActive && (i == selectedEntry);
  195. // if (!menuEntries[i].HasMouseEnteredAttached)
  196. // menuEntries [i].MouseEntered += MouseEntered;
  197. // if (!menuEntries[i].HasMouseClickedAttached)
  198. // menuEntries [i].MouseClicked += MouseClicked;
  199. menuEntries[i].Update(this, isSelected, gameTime);
  200. }
  201. }
  202. // void MouseClicked (object sender, MenuEntryEventArgs e)
  203. // {
  204. // e.MenuEntry.OnSelectEntry(PlayerIndex.One);
  205. // }
  206. //
  207. // void MouseEntered (object sender, MenuEntryEventArgs e)
  208. // {
  209. // //Console.WriteLine("Mouse Entered menu item " + e.MenuEntry.Text);
  210. // selectedEntry = menuEntries.IndexOf(e.MenuEntry);
  211. // }
  212. /// <summary>
  213. /// Draws the menu.
  214. /// </summary>
  215. public override void Draw(GameTime gameTime)
  216. {
  217. // make sure our entries are in the right place before we draw them
  218. UpdateMenuEntryLocations();
  219. GraphicsDevice graphics = ScreenManager.GraphicsDevice;
  220. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  221. SpriteFont font = ScreenManager.Font;
  222. spriteBatch.Begin();
  223. // Draw each menu entry in turn.
  224. for (int i = 0; i < menuEntries.Count; i++)
  225. {
  226. MenuEntry menuEntry = menuEntries[i];
  227. bool isSelected = IsActive && (i == selectedEntry);
  228. menuEntry.Draw(this, isSelected, gameTime);
  229. }
  230. // Make the menu slide into place during transitions, using a
  231. // power curve to make things look more interesting (this makes
  232. // the movement slow down as it nears the end).
  233. float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
  234. // Draw the menu title centered on the screen
  235. Vector2 titlePosition = new Vector2(graphics.Viewport.Width / 2, 80);
  236. Vector2 titleOrigin = font.MeasureString(menuTitle) / 2;
  237. Color titleColor = new Color(192, 192, 192) * TransitionAlpha;
  238. float titleScale = 1.25f;
  239. titlePosition.Y -= transitionOffset * 100;
  240. spriteBatch.DrawString(font, menuTitle, titlePosition, titleColor, 0,
  241. titleOrigin, titleScale, SpriteEffects.None, 0);
  242. spriteBatch.End();
  243. }
  244. }
  245. }