/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/ComboMenu.cs

https://github.com/directhex/monodevelop · C# · 431 lines · 371 code · 59 blank · 1 comment · 26 complexity · 9f874cb28497dba40368c74856bc8a6e MD5 · raw file

  1. using MonoDevelop.Components.MainToolbar;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Data;
  10. using System.Windows.Media;
  11. using System.Windows.Shapes;
  12. namespace WindowsPlatform.MainToolbar
  13. {
  14. public class SelectionChangedEventArgs<T> : EventArgs
  15. {
  16. public T Added { get; private set; }
  17. public T Removed { get; private set; }
  18. public SelectionChangedEventArgs (T added, T removed)
  19. {
  20. Added = added;
  21. Removed = removed;
  22. }
  23. }
  24. public abstract class ComboMenu<T> : Menu
  25. {
  26. protected ComboMenu () : base ()
  27. {
  28. UseLayoutRounding = true;
  29. var bindingFgColor = new Binding {
  30. Path = new PropertyPath (typeof(Styles).GetProperty("MainToolbarForegroundBrush")),
  31. Mode = BindingMode.OneWay,
  32. };
  33. var bindingDisabledFgColor = new Binding {
  34. Path = new PropertyPath (typeof(Styles).GetProperty("MainToolbarDisabledForegroundBrush")),
  35. Mode = BindingMode.OneWay,
  36. };
  37. var content = new StackPanel {
  38. Orientation = Orientation.Horizontal,
  39. Height = 20,
  40. };
  41. var textBlock = new TextBlock
  42. {
  43. HorizontalAlignment = HorizontalAlignment.Left,
  44. VerticalAlignment = VerticalAlignment.Center,
  45. Margin = new Thickness(0, 0, 0, 2),
  46. };
  47. content.Children.Add (textBlock);
  48. var arrow = new Polygon {
  49. Margin = new Thickness (5, 0, 0, 2),
  50. HorizontalAlignment = HorizontalAlignment.Right,
  51. VerticalAlignment = VerticalAlignment.Center,
  52. };
  53. arrow.SetBinding (Shape.FillProperty, new Binding ("Foreground") { Source = this });
  54. IsEnabledChanged += (o, e) =>
  55. {
  56. textBlock.SetBinding(Control.ForegroundProperty, (bool)e.NewValue ? bindingFgColor : bindingDisabledFgColor);
  57. arrow.SetBinding(Polygon.FillProperty, (bool)e.NewValue ? bindingFgColor : bindingDisabledFgColor);
  58. };
  59. arrow.Points.Add (new Point (0, 3));
  60. arrow.Points.Add (new Point (3, 6));
  61. arrow.Points.Add (new Point (6, 3));
  62. content.Children.Add (arrow);
  63. Items.Add (new SimpleMenuItem {
  64. Header = content,
  65. UseLayoutRounding = true,
  66. });
  67. IsEnabled = false;
  68. DropMenuText = "Default";
  69. }
  70. protected MenuItem DropMenu
  71. {
  72. get { return (MenuItem)Items[0]; }
  73. }
  74. protected string DropMenuText
  75. {
  76. get { return ((TextBlock)((StackPanel)DropMenu.Header).Children[0]).Text; }
  77. set { ((TextBlock)((StackPanel)DropMenu.Header).Children[0]).Text = value; }
  78. }
  79. public abstract T Active
  80. {
  81. get; set;
  82. }
  83. public abstract IEnumerable<T> Model
  84. {
  85. get; set;
  86. }
  87. public abstract event EventHandler<SelectionChangedEventArgs<T>> SelectionChanged;
  88. }
  89. public class ConfigurationComboMenu : ComboMenu<IConfigurationModel>
  90. {
  91. public ConfigurationComboMenu ()
  92. {
  93. }
  94. IConfigurationModel active;
  95. public override IConfigurationModel Active
  96. {
  97. get
  98. {
  99. return active;
  100. }
  101. set
  102. {
  103. active = model.FirstOrDefault (cm => cm.OriginalId == value.OriginalId);
  104. if (active == null)
  105. {
  106. DropMenuText = "Default";
  107. IsEnabled = false;
  108. }
  109. else
  110. {
  111. DropMenuText = active.DisplayString;
  112. IsEnabled = true;
  113. }
  114. }
  115. }
  116. IEnumerable<IConfigurationModel> model;
  117. public override IEnumerable<IConfigurationModel> Model
  118. {
  119. get
  120. {
  121. return model;
  122. }
  123. set
  124. {
  125. int count = value.Count ();
  126. model = value;
  127. var dropMenu = DropMenu;
  128. var open = dropMenu.IsSubmenuOpen;
  129. if (open)
  130. dropMenu.IsSubmenuOpen = false;
  131. foreach (MenuItem item in dropMenu.Items)
  132. item.Click -= OnMenuItemClicked;
  133. dropMenu.Items.Clear ();
  134. foreach (var item in value) {
  135. var menuItem = new ConfigurationMenuItem (item);
  136. menuItem.Click += OnMenuItemClicked;
  137. dropMenu.Items.Add (menuItem);
  138. }
  139. IsEnabled = Focusable = IsHitTestVisible = dropMenu.Items.Count > 1;
  140. if (count == 0)
  141. DropMenuText = "Default";
  142. dropMenu.IsSubmenuOpen = open;
  143. }
  144. }
  145. void OnMenuItemClicked (object sender, RoutedEventArgs args)
  146. {
  147. var item = (ConfigurationMenuItem)sender;
  148. var old = active;
  149. SelectionChanged?.Invoke (this, new SelectionChangedEventArgs<IConfigurationModel> (item.Model, old));
  150. }
  151. class ConfigurationMenuItem : SimpleMenuItem
  152. {
  153. public ConfigurationMenuItem (IConfigurationModel model)
  154. {
  155. Model = model;
  156. Header = model.DisplayString;
  157. UseLayoutRounding = true;
  158. }
  159. public IConfigurationModel Model { get; private set; }
  160. }
  161. public override event EventHandler<SelectionChangedEventArgs<IConfigurationModel>> SelectionChanged;
  162. }
  163. public class RunConfigurationComboMenu : ComboMenu<IRunConfigurationModel>
  164. {
  165. public RunConfigurationComboMenu ()
  166. {
  167. }
  168. IRunConfigurationModel active;
  169. public override IRunConfigurationModel Active {
  170. get {
  171. return active;
  172. }
  173. set {
  174. active = model.FirstOrDefault (cm => cm.OriginalId == value.OriginalId);
  175. if (active == null) {
  176. DropMenuText = "Default";
  177. IsEnabled = false;
  178. } else {
  179. DropMenuText = active.DisplayString;
  180. IsEnabled = true;
  181. }
  182. }
  183. }
  184. IEnumerable<IRunConfigurationModel> model;
  185. public override IEnumerable<IRunConfigurationModel> Model {
  186. get {
  187. return model;
  188. }
  189. set {
  190. int count = value.Count ();
  191. model = value;
  192. var dropMenu = DropMenu;
  193. var open = dropMenu.IsSubmenuOpen;
  194. if (open)
  195. dropMenu.IsSubmenuOpen = false;
  196. foreach (MenuItem item in dropMenu.Items)
  197. item.Click -= OnMenuItemClicked;
  198. dropMenu.Items.Clear ();
  199. foreach (var item in value) {
  200. var menuItem = new ConfigurationMenuItem (item);
  201. menuItem.Click += OnMenuItemClicked;
  202. dropMenu.Items.Add (menuItem);
  203. }
  204. IsEnabled = Focusable = IsHitTestVisible = dropMenu.Items.Count > 1;
  205. if (count == 0)
  206. DropMenuText = "Default";
  207. dropMenu.IsSubmenuOpen = open;
  208. }
  209. }
  210. void OnMenuItemClicked (object sender, RoutedEventArgs args)
  211. {
  212. var item = (ConfigurationMenuItem)sender;
  213. var old = active;
  214. SelectionChanged?.Invoke (this, new SelectionChangedEventArgs<IRunConfigurationModel> (item.Model, old));
  215. }
  216. class ConfigurationMenuItem : SimpleMenuItem
  217. {
  218. public ConfigurationMenuItem (IRunConfigurationModel model)
  219. {
  220. Model = model;
  221. Header = model.DisplayString;
  222. UseLayoutRounding = true;
  223. }
  224. public IRunConfigurationModel Model { get; private set; }
  225. }
  226. public override event EventHandler<SelectionChangedEventArgs<IRunConfigurationModel>> SelectionChanged;
  227. }
  228. public class RuntimeComboMenu : ComboMenu<IRuntimeModel>
  229. {
  230. public RuntimeComboMenu ()
  231. {
  232. DropMenu.SubmenuOpened += (o, e) => {
  233. var menu = (MenuItem)o;
  234. foreach (var item in menu.Items.OfType<RuntimeMenuItem> ())
  235. item.Update ();
  236. };
  237. }
  238. IEnumerable<RuntimeMenuItem> Flatten(IEnumerable<RuntimeMenuItem> list)
  239. {
  240. foreach (var item in list)
  241. {
  242. yield return item;
  243. foreach (var child in Flatten(item.Items.OfType<RuntimeMenuItem>()))
  244. yield return child;
  245. }
  246. }
  247. IRuntimeModel active;
  248. public override IRuntimeModel Active
  249. {
  250. get
  251. {
  252. return active;
  253. }
  254. set
  255. {
  256. var menuItem = DropMenu.Items
  257. .OfType<RuntimeMenuItem>()
  258. .FirstOrDefault(it => it.Model == value);
  259. active = value;
  260. if (menuItem == null)
  261. {
  262. if (value != null)
  263. {
  264. //We have MultipleTargets runtime
  265. foreach (var item in value.Children)
  266. {
  267. menuItem = Flatten(DropMenu.Items
  268. .OfType<RuntimeMenuItem>())
  269. .FirstOrDefault(it => it.Model == item);
  270. if (menuItem != null)
  271. {
  272. menuItem.Margin = new Thickness(0, 0, 0, 0);
  273. menuItem.FontWeight = FontWeights.Normal;
  274. }
  275. }
  276. using (var mutableModel = active.GetMutableModel())
  277. {
  278. DropMenuText = mutableModel.FullDisplayString;
  279. IsEnabled = true;
  280. }
  281. }
  282. else
  283. {
  284. DropMenuText = "Default";
  285. IsEnabled = false;
  286. }
  287. return;
  288. }
  289. menuItem.Margin = new Thickness(0, 0, 0, 0);
  290. menuItem.FontWeight = FontWeights.Normal;
  291. using (var mutableModel = active.GetMutableModel())
  292. {
  293. DropMenuText = mutableModel.FullDisplayString;
  294. IsEnabled = true;
  295. }
  296. }
  297. }
  298. IEnumerable<IRuntimeModel> model;
  299. public override IEnumerable<IRuntimeModel> Model
  300. {
  301. get
  302. {
  303. return model;
  304. }
  305. set
  306. {
  307. model = value;
  308. var open = DropMenu.IsSubmenuOpen;
  309. if (open)
  310. DropMenu.IsSubmenuOpen = false;
  311. FillSource (DropMenu.Items, value);
  312. int count = DropMenu.Items.Count;
  313. IsEnabled = Focusable = IsHitTestVisible = count > 1;
  314. if (count == 0)
  315. DropMenuText = "Default";
  316. DropMenu.IsSubmenuOpen = open;
  317. }
  318. }
  319. void OnMenuItemClicked(object sender, RoutedEventArgs args)
  320. {
  321. var item = (RuntimeMenuItem)sender;
  322. args.Handled = true;
  323. SelectionChanged?.Invoke(this, new SelectionChangedEventArgs<IRuntimeModel>(item.Model, Active));
  324. }
  325. void FillSource(ItemCollection source, IEnumerable<IRuntimeModel> model)
  326. {
  327. Flatten(source.OfType<RuntimeMenuItem>()).ToList().ForEach(mi => mi.Click -= OnMenuItemClicked);
  328. source.Clear();
  329. RecursiveFillChildren(source, model);
  330. }
  331. void RecursiveFillChildren(ItemCollection source, IEnumerable<IRuntimeModel> children)
  332. {
  333. foreach (var item in children)
  334. {
  335. if (item.IsSeparator)
  336. {
  337. source.Add(new Separator { UseLayoutRounding = true, });
  338. }
  339. else
  340. {
  341. var menuItem = new RuntimeMenuItem(item);
  342. menuItem.Click += OnMenuItemClicked;
  343. source.Add(menuItem);
  344. RecursiveFillChildren(menuItem.Items, item.Children);
  345. }
  346. }
  347. }
  348. public override event EventHandler<SelectionChangedEventArgs<IRuntimeModel>> SelectionChanged;
  349. class RuntimeMenuItem : SimpleMenuItem
  350. {
  351. public IRuntimeModel Model { get; private set; }
  352. public RuntimeMenuItem (IRuntimeModel model)
  353. {
  354. Model = model;
  355. UseLayoutRounding = true;
  356. Margin = new Thickness (model.IsIndented ? 15 : 0, 0, 0, 0);
  357. if (model.Notable)
  358. FontWeight = FontWeights.Bold;
  359. }
  360. public void Update ()
  361. {
  362. using (var mutableModel = Model.GetMutableModel ()) {
  363. Header = mutableModel.DisplayString;
  364. IsEnabled = mutableModel.Enabled;
  365. Visibility = mutableModel.Visible ? Visibility.Visible : Visibility.Collapsed;
  366. }
  367. foreach (var item in Items.OfType<RuntimeMenuItem> ())
  368. item.Update ();
  369. }
  370. }
  371. }
  372. }