Menuv2.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. /// <summary>
  4. /// A <see cref="Bar"/>-derived object to be used as a vertically-oriented menu. Each subview is a <see cref="MenuItemv2"/>.
  5. /// </summary>
  6. public class Menuv2 : Bar
  7. {
  8. /// <inheritdoc/>
  9. public Menuv2 () : this ([]) { }
  10. /// <inheritdoc/>
  11. public Menuv2 (IEnumerable<MenuItemv2>? menuItems) : this (menuItems?.Cast<View> ()) { }
  12. /// <inheritdoc/>
  13. public Menuv2 (IEnumerable<View>? shortcuts) : base (shortcuts)
  14. {
  15. Orientation = Orientation.Vertical;
  16. Width = Dim.Auto ();
  17. Height = Dim.Auto (DimAutoStyle.Content, 1);
  18. Border!.Thickness = new Thickness (1, 1, 1, 1);
  19. Border.LineStyle = LineStyle.Single;
  20. }
  21. /// <summary>
  22. /// Gets or sets the menu item that opened this menu as a sub-menu.
  23. /// </summary>
  24. public MenuItemv2? SuperMenuItem { get; set; }
  25. /// <inheritdoc />
  26. protected override void OnVisibleChanged ()
  27. {
  28. if (Visible)
  29. {
  30. SelectedMenuItem = SubViews.Where (mi => mi is MenuItemv2).ElementAtOrDefault (0) as MenuItemv2;
  31. }
  32. }
  33. /// <inheritdoc />
  34. public override void EndInit ()
  35. {
  36. base.EndInit ();
  37. if (Border is { })
  38. {
  39. }
  40. }
  41. /// <inheritdoc />
  42. protected override void OnSubViewAdded (View view)
  43. {
  44. base.OnSubViewAdded (view);
  45. switch (view)
  46. {
  47. case MenuItemv2 menuItem:
  48. {
  49. menuItem.CanFocus = true;
  50. AddCommand (menuItem.Command, RaiseAccepted);
  51. menuItem.Accepted += MenuItemOnAccepted;
  52. break;
  53. void MenuItemOnAccepted (object? sender, CommandEventArgs e)
  54. {
  55. Logging.Trace ($"MenuItemOnAccepted: {e.Context?.Source?.Title}");
  56. RaiseAccepted (e.Context);
  57. }
  58. }
  59. case Line line:
  60. // Grow line so we get auto-join line
  61. line.X = Pos.Func (() => -Border!.Thickness.Left);
  62. line.Width = Dim.Fill ()! + Dim.Func (() => Border!.Thickness.Right);
  63. break;
  64. }
  65. }
  66. /// <inheritdoc />
  67. protected override bool OnAccepting (CommandEventArgs args)
  68. {
  69. Logging.Trace ($"{args.Context}");
  70. if (SuperMenuItem is { })
  71. {
  72. Logging.Trace ($"Invoking Accept on SuperMenuItem: {SuperMenuItem.Title}...");
  73. return SuperMenuItem?.SuperView?.InvokeCommand (Command.Accept, args.Context) is true;
  74. }
  75. return false;
  76. }
  77. // TODO: Consider moving Accepted to Bar?
  78. /// <summary>
  79. /// Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating an item in this menu (or submenu)
  80. /// was accepted. This is used to determine when to hide the menu.
  81. /// </summary>
  82. /// <param name="ctx"></param>
  83. /// <returns></returns>
  84. protected bool? RaiseAccepted (ICommandContext? ctx)
  85. {
  86. //Logging.Trace ($"RaiseAccepted: {ctx}");
  87. CommandEventArgs args = new () { Context = ctx };
  88. OnAccepted (args);
  89. Accepted?.Invoke (this, args);
  90. return true;
  91. }
  92. /// <summary>
  93. /// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu.
  94. /// </summary>
  95. /// <remarks>
  96. /// </remarks>
  97. /// <param name="args"></param>
  98. protected virtual void OnAccepted (CommandEventArgs args) { }
  99. /// <summary>
  100. /// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu.
  101. /// </summary>
  102. /// <remarks>
  103. /// <para>
  104. /// See <see cref="RaiseAccepted"/> for more information.
  105. /// </para>
  106. /// </remarks>
  107. public event EventHandler<CommandEventArgs>? Accepted;
  108. /// <inheritdoc />
  109. protected override void OnFocusedChanged (View? previousFocused, View? focused)
  110. {
  111. base.OnFocusedChanged (previousFocused, focused);
  112. SelectedMenuItem = focused as MenuItemv2;
  113. RaiseSelectedMenuItemChanged (SelectedMenuItem);
  114. }
  115. /// <summary>
  116. /// Gets or set the currently selected menu item. This is a helper that
  117. /// tracks <see cref="View.Focused"/>.
  118. /// </summary>
  119. public MenuItemv2? SelectedMenuItem
  120. {
  121. get => Focused as MenuItemv2;
  122. set
  123. {
  124. if (value == Focused)
  125. {
  126. return;
  127. }
  128. // Note we DO NOT set focus here; This property tracks Focused
  129. }
  130. }
  131. internal void RaiseSelectedMenuItemChanged (MenuItemv2? selected)
  132. {
  133. //Logging.Trace ($"RaiseSelectedMenuItemChanged: {selected?.Title}");
  134. OnSelectedMenuItemChanged (selected);
  135. SelectedMenuItemChanged?.Invoke (this, selected);
  136. }
  137. /// <summary>
  138. /// Called when the selected menu item has changed.
  139. /// </summary>
  140. /// <param name="selected"></param>
  141. protected virtual void OnSelectedMenuItemChanged (MenuItemv2? selected)
  142. {
  143. }
  144. /// <summary>
  145. /// Raised when the selected menu item has changed.
  146. /// </summary>
  147. public event EventHandler<MenuItemv2?>? SelectedMenuItemChanged;
  148. }