NetworkBusyScreen.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. using System;
  2. using System.Threading;
  3. using System.Threading.Tasks;
  4. using Microsoft.Xna.Framework;
  5. using Microsoft.Xna.Framework.Graphics;
  6. using Microsoft.Xna.Framework.Content;
  7. namespace NetRumble
  8. {
  9. // Modern async/await version using Task<T>
  10. class NetworkBusyScreen<T> : GameScreen
  11. {
  12. readonly Task<T> task;
  13. readonly CancellationTokenSource cts;
  14. bool completionRaised;
  15. Texture2D busyTexture;
  16. string message;
  17. event EventHandler<OperationCompletedEventArgs> operationCompleted;
  18. public event EventHandler<OperationCompletedEventArgs> OperationCompleted
  19. {
  20. add { operationCompleted += value; }
  21. remove { operationCompleted -= value; }
  22. }
  23. public NetworkBusyScreen(string message, Task<T> task)
  24. {
  25. this.message = message;
  26. this.task = task;
  27. this.cts = null;
  28. IsPopup = true;
  29. TransitionOnTime = TimeSpan.FromSeconds(0.1);
  30. TransitionOffTime = TimeSpan.FromSeconds(0.2);
  31. }
  32. public NetworkBusyScreen(string message, Task<T> task, CancellationTokenSource cts)
  33. {
  34. this.message = message;
  35. this.task = task;
  36. this.cts = cts;
  37. IsPopup = true;
  38. TransitionOnTime = TimeSpan.FromSeconds(0.1);
  39. TransitionOffTime = TimeSpan.FromSeconds(0.2);
  40. }
  41. public override void LoadContent()
  42. {
  43. ContentManager content = ScreenManager.Game.Content;
  44. busyTexture = content.Load<Texture2D>("Textures/chatTalking");
  45. }
  46. public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
  47. {
  48. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  49. // Optional: allow user to cancel (Esc or B)
  50. if (!completionRaised && cts != null)
  51. {
  52. var kb = Microsoft.Xna.Framework.Input.Keyboard.GetState();
  53. if (kb.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape))
  54. {
  55. cts.Cancel();
  56. }
  57. var gp = Microsoft.Xna.Framework.Input.GamePad.GetState(Microsoft.Xna.Framework.PlayerIndex.One);
  58. if (gp.IsConnected && gp.IsButtonDown(Microsoft.Xna.Framework.Input.Buttons.B))
  59. {
  60. cts.Cancel();
  61. }
  62. }
  63. // Has our asynchronous operation completed?
  64. if (!completionRaised && task != null && task.IsCompleted)
  65. {
  66. object resultObject = default(T);
  67. Exception error = null;
  68. try
  69. {
  70. resultObject = task.Result;
  71. }
  72. catch (Exception ex)
  73. {
  74. error = (task?.Exception?.GetBaseException()) ?? ex;
  75. }
  76. var handler = operationCompleted;
  77. if (handler != null)
  78. {
  79. handler(this, new OperationCompletedEventArgs(resultObject, error));
  80. }
  81. completionRaised = true;
  82. operationCompleted = null;
  83. ExitScreen();
  84. }
  85. }
  86. public override void Draw(GameTime gameTime)
  87. {
  88. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  89. SpriteFont font = ScreenManager.Font;
  90. const int hPad = 32;
  91. const int vPad = 16;
  92. // Center the message text in the viewport.
  93. Vector2 viewportSize = new Vector2(ScreenManager.BASE_BUFFER_WIDTH, ScreenManager.BASE_BUFFER_HEIGHT);
  94. Vector2 textSize = font.MeasureString(message);
  95. // Add enough room to spin a texture.
  96. Vector2 busyTextureSize = new Vector2(busyTexture.Width * 0.8f);
  97. Vector2 busyTextureOrigin = new Vector2(busyTexture.Width / 2, busyTexture.Height / 2);
  98. textSize.X = Math.Max(textSize.X, busyTextureSize.X);
  99. textSize.Y += busyTextureSize.Y + vPad;
  100. Vector2 textPosition = (viewportSize - textSize) / 2;
  101. // The background includes a border somewhat larger than the text itself.
  102. Rectangle backgroundRectangle = new Rectangle((int)textPosition.X - hPad,
  103. (int)textPosition.Y - vPad,
  104. (int)textSize.X + hPad * 2,
  105. (int)textSize.Y + vPad * 2);
  106. // Fade the popup alpha during transitions.
  107. Color color = new Color((byte)255, (byte)255, (byte)255, (byte)TransitionAlpha);
  108. // Draw the background rectangle.
  109. Rectangle backgroundRectangle2 = new Rectangle(backgroundRectangle.X - 1,
  110. backgroundRectangle.Y - 1, backgroundRectangle.Width + 2,
  111. backgroundRectangle.Height + 2);
  112. ScreenManager.DrawRectangle(backgroundRectangle2, new Color((byte)128, (byte)128, (byte)128,
  113. (byte)(192.0f * (float)TransitionAlpha / 255.0f)));
  114. ScreenManager.DrawRectangle(backgroundRectangle, new Color((byte)0, (byte)0, (byte)0,
  115. (byte)(232.0f * (float)TransitionAlpha / 255.0f)));
  116. spriteBatch.Begin();
  117. // Draw the message box text.
  118. spriteBatch.DrawString(font, message, textPosition, color);
  119. // Draw the spinning busy progress indicator.
  120. float busyTextureRotation = (float)gameTime.TotalGameTime.TotalSeconds * 3;
  121. Vector2 busyTexturePosition = new Vector2(textPosition.X + textSize.X / 2,
  122. textPosition.Y + textSize.Y - busyTextureSize.Y / 2);
  123. spriteBatch.Draw(busyTexture, busyTexturePosition, null, color,
  124. busyTextureRotation, busyTextureOrigin, 0.8f,
  125. SpriteEffects.None, 0);
  126. spriteBatch.End();
  127. }
  128. }
  129. }