PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/OpenRA.Game/Widgets/Widget.cs

http://github.com/chrisforbes/OpenRA
C# | 414 lines | 316 code | 80 blank | 18 comment | 58 complexity | 48b8b32072e8aa2c089e49e8c0c75fae MD5 | raw file
Possible License(s): GPL-3.0
  1. #region Copyright & License Information
  2. /*
  3. * Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
  4. * This file is part of OpenRA, which is free software. It is made
  5. * available to you under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation. For more information,
  7. * see COPYING.
  8. */
  9. #endregion
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Drawing;
  13. using System.Linq;
  14. using OpenRA.FileFormats;
  15. using OpenRA.Graphics;
  16. namespace OpenRA.Widgets
  17. {
  18. public static class Ui
  19. {
  20. public static Widget Root = new ContainerWidget();
  21. static Stack<Widget> WindowList = new Stack<Widget>();
  22. public static Widget SelectedWidget;
  23. public static Widget MouseOverWidget;
  24. public static void CloseWindow()
  25. {
  26. if (WindowList.Count > 0)
  27. Root.RemoveChild(WindowList.Pop());
  28. if (WindowList.Count > 0)
  29. Root.AddChild(WindowList.Peek());
  30. }
  31. public static Widget OpenWindow(string id)
  32. {
  33. return OpenWindow(id, new WidgetArgs());
  34. }
  35. public static Widget OpenWindow(string id, WidgetArgs args)
  36. {
  37. var window = Game.modData.WidgetLoader.LoadWidget(args, Root, id);
  38. if (WindowList.Count > 0)
  39. Root.RemoveChild(WindowList.Peek());
  40. WindowList.Push(window);
  41. return window;
  42. }
  43. public static Widget LoadWidget(string id, Widget parent, WidgetArgs args)
  44. {
  45. return Game.modData.WidgetLoader.LoadWidget(args, parent, id);
  46. }
  47. public static void Tick() { Root.TickOuter(); }
  48. public static void Draw() { Root.DrawOuter(); }
  49. public static bool HandleInput(MouseInput mi)
  50. {
  51. var wasMouseOver = MouseOverWidget;
  52. if (mi.Event == MouseInputEvent.Move)
  53. MouseOverWidget = null;
  54. bool handled = false;
  55. if (SelectedWidget != null && SelectedWidget.HandleMouseInputOuter(mi))
  56. handled = true;
  57. if (!handled && Root.HandleMouseInputOuter(mi))
  58. handled = true;
  59. if (mi.Event == MouseInputEvent.Move)
  60. {
  61. Viewport.LastMousePos = mi.Location;
  62. Viewport.TicksSinceLastMove = 0;
  63. }
  64. if (wasMouseOver != MouseOverWidget)
  65. {
  66. if (wasMouseOver != null)
  67. wasMouseOver.MouseExited();
  68. if (MouseOverWidget != null)
  69. MouseOverWidget.MouseEntered();
  70. }
  71. return handled;
  72. }
  73. public static bool HandleKeyPress(KeyInput e)
  74. {
  75. if (SelectedWidget != null)
  76. return SelectedWidget.HandleKeyPressOuter(e);
  77. if (Root.HandleKeyPressOuter(e))
  78. return true;
  79. return false;
  80. }
  81. public static void ResetAll()
  82. {
  83. Root.RemoveChildren();
  84. while (WindowList.Count > 0)
  85. CloseWindow();
  86. }
  87. }
  88. public abstract class Widget
  89. {
  90. // Info defined in YAML
  91. public string Id = null;
  92. public string X = "0";
  93. public string Y = "0";
  94. public string Width = "0";
  95. public string Height = "0";
  96. public string Logic = null;
  97. public object LogicObject { get; private set; }
  98. public bool Visible = true;
  99. public bool IgnoreMouseOver;
  100. public bool IgnoreChildMouseOver;
  101. // Calculated internally
  102. public Rectangle Bounds;
  103. public Widget Parent = null;
  104. public Func<bool> IsVisible;
  105. public Widget() { IsVisible = () => Visible; }
  106. public readonly List<Widget> Children = new List<Widget>();
  107. public Widget(Widget widget)
  108. {
  109. Id = widget.Id;
  110. X = widget.X;
  111. Y = widget.Y;
  112. Width = widget.Width;
  113. Height = widget.Height;
  114. Logic = widget.Logic;
  115. Visible = widget.Visible;
  116. Bounds = widget.Bounds;
  117. Parent = widget.Parent;
  118. IsVisible = widget.IsVisible;
  119. IgnoreChildMouseOver = widget.IgnoreChildMouseOver;
  120. foreach (var child in widget.Children)
  121. AddChild(child.Clone());
  122. }
  123. public virtual Widget Clone()
  124. {
  125. throw new InvalidOperationException("Widget type `{0}` is not cloneable.".F(GetType().Name));
  126. }
  127. public virtual int2 RenderOrigin
  128. {
  129. get
  130. {
  131. var offset = (Parent == null) ? int2.Zero : Parent.ChildOrigin;
  132. return new int2(Bounds.X, Bounds.Y) + offset;
  133. }
  134. }
  135. public virtual int2 ChildOrigin { get { return RenderOrigin; } }
  136. public virtual Rectangle RenderBounds
  137. {
  138. get
  139. {
  140. var ro = RenderOrigin;
  141. return new Rectangle(ro.X, ro.Y, Bounds.Width, Bounds.Height);
  142. }
  143. }
  144. public virtual void Initialize(WidgetArgs args)
  145. {
  146. // Parse the YAML equations to find the widget bounds
  147. var parentBounds = (Parent == null)
  148. ? new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height)
  149. : Parent.Bounds;
  150. var substitutions = args.ContainsKey("substitutions") ?
  151. new Dictionary<string, int>((Dictionary<string, int>)args["substitutions"]) :
  152. new Dictionary<string, int>();
  153. substitutions.Add("WINDOW_RIGHT", Game.viewport.Width);
  154. substitutions.Add("WINDOW_BOTTOM", Game.viewport.Height);
  155. substitutions.Add("PARENT_RIGHT", parentBounds.Width);
  156. substitutions.Add("PARENT_LEFT", parentBounds.Left);
  157. substitutions.Add("PARENT_TOP", parentBounds.Top);
  158. substitutions.Add("PARENT_BOTTOM", parentBounds.Height);
  159. int width = Evaluator.Evaluate(Width, substitutions);
  160. int height = Evaluator.Evaluate(Height, substitutions);
  161. substitutions.Add("WIDTH", width);
  162. substitutions.Add("HEIGHT", height);
  163. Bounds = new Rectangle(Evaluator.Evaluate(X, substitutions),
  164. Evaluator.Evaluate(Y, substitutions),
  165. width,
  166. height);
  167. }
  168. public void PostInit(WidgetArgs args)
  169. {
  170. if (Logic == null)
  171. return;
  172. args["widget"] = this;
  173. LogicObject = Game.modData.ObjectCreator.CreateObject<object>(Logic, args);
  174. args.Remove("widget");
  175. }
  176. public virtual Rectangle EventBounds { get { return RenderBounds; } }
  177. public virtual Rectangle GetEventBounds()
  178. {
  179. return Children
  180. .Where(c => c.IsVisible())
  181. .Select(c => c.GetEventBounds())
  182. .Aggregate(EventBounds, Rectangle.Union);
  183. }
  184. public bool Focused { get { return Ui.SelectedWidget == this; } }
  185. public virtual bool TakeFocus(MouseInput mi)
  186. {
  187. if (Focused)
  188. return true;
  189. if (Ui.SelectedWidget != null && !Ui.SelectedWidget.LoseFocus(mi))
  190. return false;
  191. Ui.SelectedWidget = this;
  192. return true;
  193. }
  194. // Remove focus from this widget; return false if you don't want to give it up
  195. public virtual bool LoseFocus(MouseInput mi)
  196. {
  197. // Some widgets may need to override focus depending on mouse click
  198. return LoseFocus();
  199. }
  200. public virtual bool LoseFocus()
  201. {
  202. if (Ui.SelectedWidget == this)
  203. Ui.SelectedWidget = null;
  204. return true;
  205. }
  206. public virtual string GetCursor(int2 pos) { return "default"; }
  207. public string GetCursorOuter(int2 pos)
  208. {
  209. // Is the cursor on top of us?
  210. if (!(IsVisible() && GetEventBounds().Contains(pos)))
  211. return null;
  212. // Do any of our children specify a cursor?
  213. foreach (var child in Children.OfType<Widget>().Reverse())
  214. {
  215. var cc = child.GetCursorOuter(pos);
  216. if (cc != null)
  217. return cc;
  218. }
  219. return EventBounds.Contains(pos) ? GetCursor(pos) : null;
  220. }
  221. public virtual void MouseEntered() {}
  222. public virtual void MouseExited() {}
  223. public virtual bool HandleMouseInput(MouseInput mi) { return false; }
  224. public bool HandleMouseInputOuter(MouseInput mi)
  225. {
  226. // Are we able to handle this event?
  227. if (!(Focused || (IsVisible() && GetEventBounds().Contains(mi.Location))))
  228. return false;
  229. var oldMouseOver = Ui.MouseOverWidget;
  230. // Send the event to the deepest children first and bubble up if unhandled
  231. foreach (var child in Children.OfType<Widget>().Reverse())
  232. if (child.HandleMouseInputOuter(mi))
  233. return true;
  234. if (IgnoreChildMouseOver)
  235. Ui.MouseOverWidget = oldMouseOver;
  236. if (mi.Event == MouseInputEvent.Move && Ui.MouseOverWidget == null && !IgnoreMouseOver)
  237. Ui.MouseOverWidget = this;
  238. return HandleMouseInput(mi);
  239. }
  240. public virtual bool HandleKeyPress(KeyInput e) { return false; }
  241. public virtual bool HandleKeyPressOuter(KeyInput e)
  242. {
  243. if (!IsVisible())
  244. return false;
  245. // Can any of our children handle this?
  246. foreach (var child in Children.OfType<Widget>().Reverse())
  247. if (child.HandleKeyPressOuter(e))
  248. return true;
  249. // Do any widgety behavior (enter text etc)
  250. var handled = HandleKeyPress(e);
  251. return handled;
  252. }
  253. public virtual void Draw() {}
  254. public virtual void DrawOuter()
  255. {
  256. if (IsVisible())
  257. {
  258. Draw();
  259. foreach (var child in Children)
  260. child.DrawOuter();
  261. }
  262. }
  263. public virtual void Tick() {}
  264. public virtual void TickOuter()
  265. {
  266. if (IsVisible())
  267. {
  268. Tick();
  269. foreach (var child in Children)
  270. child.TickOuter();
  271. }
  272. }
  273. public virtual void AddChild(Widget child)
  274. {
  275. child.Parent = this;
  276. Children.Add(child);
  277. }
  278. public virtual void RemoveChild(Widget child)
  279. {
  280. Children.Remove(child);
  281. child.Removed();
  282. }
  283. public virtual void RemoveChildren()
  284. {
  285. while (Children.Count > 0)
  286. RemoveChild(Children[Children.Count-1]);
  287. }
  288. public virtual void Removed()
  289. {
  290. foreach (var c in Children.OfType<Widget>().Reverse())
  291. c.Removed();
  292. }
  293. public Widget GetOrNull(string id)
  294. {
  295. if (this.Id == id)
  296. return this;
  297. foreach (var child in Children)
  298. {
  299. var w = child.GetOrNull(id);
  300. if (w != null)
  301. return w;
  302. }
  303. return null;
  304. }
  305. public T GetOrNull<T>(string id) where T : Widget
  306. {
  307. return (T) GetOrNull(id);
  308. }
  309. public T Get<T>(string id) where T : Widget
  310. {
  311. var t = GetOrNull<T>(id);
  312. if (t == null)
  313. throw new InvalidOperationException(
  314. "Widget {0} has no child {1} of type {2}".F(
  315. Id, id, typeof(T).Name));
  316. return t;
  317. }
  318. public Widget Get(string id) { return Get<Widget>(id); }
  319. }
  320. public class ContainerWidget : Widget
  321. {
  322. public ContainerWidget() : base() { IgnoreMouseOver = true; }
  323. public ContainerWidget(ContainerWidget other)
  324. : base(other) { IgnoreMouseOver = true; }
  325. public override string GetCursor(int2 pos) { return null; }
  326. public override Widget Clone() { return new ContainerWidget(this); }
  327. }
  328. public class WidgetArgs : Dictionary<string, object>
  329. {
  330. public WidgetArgs() : base() { }
  331. public WidgetArgs(Dictionary<string, object> args) : base(args) { }
  332. public void Add(string key, Action val) { base.Add(key, val); }
  333. }
  334. }