View.Hierarchy.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. using System.Diagnostics;
  2. namespace Terminal.Gui.ViewBase;
  3. public partial class View // SuperView/SubView hierarchy management (SuperView, SubViews, Add, Remove, etc.)
  4. {
  5. private static readonly IReadOnlyCollection<View> _empty = [];
  6. private readonly List<View>? _subviews = [];
  7. // Internally, we use InternalSubViews rather than subviews, as we do not expect us
  8. // to make the same mistakes our users make when they poke at the SubViews.
  9. internal IList<View> InternalSubViews => _subviews ?? [];
  10. /// <summary>Gets the list of SubViews.</summary>
  11. /// <remarks>
  12. /// Use <see cref="Add(View?)"/> and <see cref="Remove(View?)"/> to add or remove subviews.
  13. /// </remarks>
  14. public IReadOnlyCollection<View> SubViews => InternalSubViews?.AsReadOnly () ?? _empty;
  15. /// <summary>
  16. /// Gets all SubViews of this View, optionally including SubViews of the View's Adornments
  17. /// (Margin, Border, and Padding).
  18. /// </summary>
  19. /// <param name="includeMargin">
  20. /// If <see langword="true"/>, includes SubViews from <see cref="Margin"/>. If <see langword="false"/> (default),
  21. /// returns only the direct SubViews
  22. /// of this View.
  23. /// </param>
  24. /// <param name="includeBorder">
  25. /// If <see langword="true"/>, includes SubViews from <see cref="Border"/>. If <see langword="false"/> (default),
  26. /// returns only the direct SubViews
  27. /// of this View.
  28. /// </param>
  29. /// <param name="includePadding">
  30. /// If <see langword="true"/>, includes SubViews from <see cref="Padding"/>. If <see langword="false"/> (default),
  31. /// returns only the direct SubViews
  32. /// of this View.
  33. /// </param>
  34. /// <returns>
  35. /// A read-only collection containing all SubViews. If <paramref name="includeMargin"/> is
  36. /// <see langword="true"/>, the collection includes SubViews from this View's direct SubViews as well
  37. /// as SubViews from the Margin, Border, and Padding adornments.
  38. /// </returns>
  39. /// <remarks>
  40. /// <para>
  41. /// This method returns a snapshot of the SubViews at the time of the call. The collection is
  42. /// safe to iterate even if SubViews are added or removed during iteration.
  43. /// </para>
  44. /// <para>
  45. /// The order of SubViews in the returned collection is:
  46. /// <list type="number">
  47. /// <item>Direct SubViews of this View</item>
  48. /// <item>SubViews of Margin (if <paramref name="includeMargin"/> is <see langword="true"/>)</item>
  49. /// <item>SubViews of Border (if <paramref name="includeBorder"/> is <see langword="true"/>)</item>
  50. /// <item>SubViews of Padding (if <paramref name="includePadding"/> is <see langword="true"/>)</item>
  51. /// </list>
  52. /// </para>
  53. /// </remarks>
  54. public virtual IReadOnlyCollection<View> GetSubViews (bool includeMargin = false, bool includeBorder = false, bool includePadding = false)
  55. {
  56. List<View> result = [];
  57. // Add direct SubViews
  58. result.AddRange (InternalSubViews);
  59. if (includeMargin && Margin is { SubViews: { Count: > 0 } } && Margin.Thickness != Thickness.Empty)
  60. {
  61. // Add Margin SubViews
  62. result.AddRange (Margin.SubViews);
  63. }
  64. if (includeBorder && Border is { SubViews: { Count: > 0 } } && Border.Thickness != Thickness.Empty)
  65. {
  66. // Add Border SubViews
  67. result.AddRange (Border.SubViews);
  68. }
  69. if (includePadding && Padding is { SubViews: { Count: > 0 } } && Padding.Thickness != Thickness.Empty)
  70. {
  71. // Add Padding SubViews
  72. result.AddRange (Padding.SubViews);
  73. }
  74. return result.AsReadOnly ();
  75. }
  76. private View? _superView;
  77. /// <summary>
  78. /// Gets this Views SuperView (the View's container), or <see langword="null"/> if this view has not been added as a
  79. /// SubView.
  80. /// </summary>
  81. /// <seealso cref="OnSuperViewChanging"/>
  82. /// <seealso cref="SuperViewChanging"/>
  83. /// <seealso cref="OnSuperViewChanged"/>
  84. /// <seealso cref="SuperViewChanged"/>
  85. public View? SuperView => _superView!;
  86. /// <summary>
  87. /// INTERNAL: Sets the SuperView of this View.
  88. /// </summary>
  89. /// <param name="value"></param>
  90. /// <returns><see langword="true"/> if the SuperView was changed; otherwise, <see langword="false"/>.</returns>
  91. private bool SetSuperView (View? value)
  92. {
  93. if (_superView == value)
  94. {
  95. return true;
  96. }
  97. return CWPPropertyHelper.ChangeProperty (
  98. this,
  99. ref _superView,
  100. value,
  101. OnSuperViewChanging,
  102. SuperViewChanging,
  103. newValue => _superView = newValue,
  104. OnSuperViewChanged,
  105. SuperViewChanged,
  106. out View? _);
  107. }
  108. /// <summary>
  109. /// Called when the SuperView of this View is about to be changed. This is called before the SuperView property
  110. /// is updated, allowing access to the current SuperView and its resources (such as <see cref="App"/>) for
  111. /// cleanup purposes.
  112. /// </summary>
  113. /// <param name="args">Hold the new SuperView that will be set, or <see langword="null"/> if being removed.</param>
  114. /// <returns><see langword="true"/> to cancel the change; <see langword="false"/> to allow it.</returns>
  115. protected virtual bool OnSuperViewChanging (ValueChangingEventArgs<View?> args) => false;
  116. /// <summary>
  117. /// Raised when the SuperView of this View is about to be changed. This is raised before the SuperView property
  118. /// is updated, allowing access to the current SuperView and its resources (such as <see cref="App"/>) for
  119. /// cleanup purposes.
  120. /// </summary>
  121. /// <remarks>
  122. /// <para>
  123. /// This event follows the Cancellable Work Pattern (CWP). Set <see cref="ValueChangingEventArgs{T}.Handled"/>
  124. /// to <see langword="true"/> in the event args to cancel the change.
  125. /// </para>
  126. /// </remarks>
  127. public event EventHandler<ValueChangingEventArgs<View?>>? SuperViewChanging;
  128. /// <summary>
  129. /// Called when the SuperView of this View has changed.
  130. /// </summary>
  131. protected virtual void OnSuperViewChanged (ValueChangedEventArgs<View?> args) { }
  132. /// <summary>Raised when the SuperView of this View has changed.</summary>
  133. public event EventHandler<ValueChangedEventArgs<View?>>? SuperViewChanged;
  134. #region AddRemove
  135. /// <summary>Adds a SubView (child) to this view.</summary>
  136. /// <remarks>
  137. /// <para>
  138. /// The Views that have been added to this view can be retrieved via the <see cref="SubViews"/> property.
  139. /// </para>
  140. /// <para>
  141. /// To check if a View has been added to this View, compare it's <see cref="SuperView"/> property to this View.
  142. /// </para>
  143. /// <para>
  144. /// SubViews will be disposed when this View is disposed. In other-words, calling this method causes
  145. /// the lifecycle of the subviews to be transferred to this View.
  146. /// </para>
  147. /// <para>
  148. /// Calls/Raises the <see cref="OnSubViewAdded"/>/<see cref="SubViewAdded"/> event.
  149. /// </para>
  150. /// <para>
  151. /// The <see cref="OnSuperViewChanged"/>/<see cref="SuperViewChanged"/> event will be raised on the added View.
  152. /// </para>
  153. /// </remarks>
  154. /// <param name="view">The view to add.</param>
  155. /// <returns>The view that was added.</returns>
  156. /// <seealso cref="Remove(View)"/>
  157. /// <seealso cref="RemoveAll"/>
  158. /// <seealso cref="OnSubViewAdded"/>
  159. /// <seealso cref="SubViewAdded"/>
  160. /// <seealso cref="OnSuperViewChanging"/>
  161. /// <seealso cref="SuperViewChanging"/>
  162. /// <seealso cref="OnSuperViewChanged"/>
  163. /// <seealso cref="SuperViewChanged"/>
  164. public View? Add (View? view)
  165. {
  166. if (view is null)
  167. {
  168. return null;
  169. }
  170. //Debug.Assert (view.SuperView is null, $"{view} already has a SuperView: {view.SuperView}.");
  171. if (view.SuperView is { })
  172. {
  173. Logging.Warning ($"{view} already has a SuperView: {view.SuperView}.");
  174. }
  175. //Debug.Assert (!InternalSubViews.Contains (view), $"{view} has already been Added to {this}.");
  176. if (InternalSubViews.Contains (view))
  177. {
  178. Logging.Warning ($"{view} has already been Added to {this}.");
  179. }
  180. // TODO: Add AddingSubView event
  181. if (this is Margin)
  182. {
  183. if (view is not ShadowView)
  184. {
  185. throw new InvalidOperationException ("SubViews of Margin are not supported.");
  186. }
  187. }
  188. // TODO: Make this thread safe
  189. InternalSubViews.Add (view);
  190. // Try to set the SuperView - this may be cancelled
  191. if (!view.SetSuperView (this))
  192. {
  193. InternalSubViews.Remove (view);
  194. // The change was cancelled
  195. return null;
  196. }
  197. // Ensure views don't have focus when being added
  198. view.HasFocus = false;
  199. if (view is { Enabled: true, Visible: true, CanFocus: true })
  200. {
  201. // Add will cause the newly added subview to gain focus if it's focusable
  202. if (HasFocus)
  203. {
  204. view.SetFocus ();
  205. }
  206. }
  207. if (view.Enabled && !Enabled)
  208. {
  209. view.Enabled = false;
  210. }
  211. // Raise event indicating a subview has been added
  212. // We do this before Init.
  213. RaiseSubViewAdded (view);
  214. if (IsInitialized && !view.IsInitialized)
  215. {
  216. view.BeginInit ();
  217. view.EndInit ();
  218. }
  219. SetNeedsDraw ();
  220. SetNeedsLayout ();
  221. return view;
  222. }
  223. /// <summary>Adds the specified SubView (children) to the view.</summary>
  224. /// <param name="views">Array of one or more views (can be optional parameter).</param>
  225. /// <remarks>
  226. /// <para>
  227. /// The Views that have been added to this view can be retrieved via the <see cref="SubViews"/> property. See also
  228. /// <seealso cref="Remove(View)"/> and <seealso cref="RemoveAll"/>.
  229. /// </para>
  230. /// <para>
  231. /// SubViews will be disposed when this View is disposed. In other-words, calling this method causes
  232. /// the lifecycle of the subviews to be transferred to this View.
  233. /// </para>
  234. /// </remarks>
  235. public void Add (params View []? views)
  236. {
  237. if (views is null)
  238. {
  239. return;
  240. }
  241. foreach (View view in views)
  242. {
  243. Add (view);
  244. }
  245. }
  246. internal void RaiseSubViewAdded (View view)
  247. {
  248. OnSubViewAdded (view);
  249. SubViewAdded?.Invoke (this, new (this, view));
  250. }
  251. /// <summary>
  252. /// Called when a SubView has been added to this View.
  253. /// </summary>
  254. /// <remarks>
  255. /// If the SubView has not been initialized, this happens before BeginInit/EndInit is called.
  256. /// </remarks>
  257. /// <param name="view"></param>
  258. protected virtual void OnSubViewAdded (View view) { }
  259. /// <summary>Raised when a SubView has been added to this View.</summary>
  260. /// <remarks>
  261. /// If the SubView has not been initialized, this happens before BeginInit/EndInit is called.
  262. /// </remarks>
  263. public event EventHandler<SuperViewChangedEventArgs>? SubViewAdded;
  264. /// <summary>Removes a SubView added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.</summary>
  265. /// <remarks>
  266. /// <para>
  267. /// Normally SubViews will be disposed when this View is disposed. Removing a SubView causes ownership of the
  268. /// SubView's
  269. /// lifecycle to be transferred to the caller; the caller must call <see cref="Dispose()"/>.
  270. /// </para>
  271. /// <para>
  272. /// Calls/Raises the <see cref="OnSubViewRemoved"/>/<see cref="SubViewRemoved"/> event.
  273. /// </para>
  274. /// <para>
  275. /// The <see cref="OnSuperViewChanged"/>/<see cref="SuperViewChanged"/> event will be raised on the removed View.
  276. /// </para>
  277. /// </remarks>
  278. /// <returns>
  279. /// The removed View. <see langword="null"/> if the View could not be removed.
  280. /// </returns>
  281. /// <seealso cref="Add(View)"/>
  282. /// <seealso cref="RemoveAll"/>
  283. /// <seealso cref="OnSubViewAdded"/>
  284. /// <seealso cref="OnSubViewRemoved"/>
  285. /// <seealso cref="SubViewRemoved"/>
  286. /// <seealso cref="OnSuperViewChanging"/>
  287. /// <seealso cref="SuperViewChanging"/>
  288. /// <seealso cref="OnSuperViewChanged"/>
  289. /// <seealso cref="SuperViewChanged"/>
  290. public View? Remove (View? view)
  291. {
  292. if (view is null)
  293. {
  294. return null;
  295. }
  296. if (InternalSubViews.Count == 0)
  297. {
  298. return view;
  299. }
  300. if (view.SuperView is null)
  301. {
  302. Logging.Warning ($"{view} cannot be Removed. SuperView is null.");
  303. }
  304. if (view.SuperView != this)
  305. {
  306. Logging.Warning ($"{view} cannot be Removed. SuperView is not this ({view.SuperView}.");
  307. }
  308. if (!InternalSubViews.Contains (view))
  309. {
  310. Logging.Warning ($"{view} cannot be Removed. It has not been added to {this}.");
  311. }
  312. if (App?.Mouse.MouseGrabView == view)
  313. {
  314. App.Mouse.UngrabMouse ();
  315. }
  316. Rectangle touched = view.Frame;
  317. bool hadFocus = view.HasFocus;
  318. bool couldFocus = view.CanFocus;
  319. if (hadFocus)
  320. {
  321. view.CanFocus = false; // If view had focus, this will ensure it doesn't and it stays that way
  322. }
  323. Debug.Assert (!view.HasFocus);
  324. View? previousSuperView = view.SuperView;
  325. // Try to clear the SuperView - this may be cancelled
  326. if (!view.SetSuperView (null))
  327. {
  328. // The change was cancelled, restore state and return null
  329. view.CanFocus = couldFocus;
  330. return null;
  331. }
  332. Debug.Assert (view.SuperView is null);
  333. InternalSubViews.Remove (view);
  334. // Clean up focus stuff
  335. _previouslyFocused = null;
  336. if (previousSuperView is { } && previousSuperView._previouslyFocused == this)
  337. {
  338. previousSuperView._previouslyFocused = null;
  339. }
  340. SetNeedsLayout ();
  341. SetNeedsDraw ();
  342. foreach (View v in InternalSubViews)
  343. {
  344. if (v.Frame.IntersectsWith (touched))
  345. {
  346. view.SetNeedsDraw ();
  347. }
  348. }
  349. view.CanFocus = couldFocus; // Restore to previous value
  350. if (_previouslyFocused == view)
  351. {
  352. _previouslyFocused = null;
  353. }
  354. RaiseSubViewRemoved (view);
  355. return view;
  356. }
  357. internal void RaiseSubViewRemoved (View view)
  358. {
  359. OnSubViewRemoved (view);
  360. SubViewRemoved?.Invoke (this, new (this, view));
  361. }
  362. /// <summary>
  363. /// Called when a SubView has been removed from this View.
  364. /// </summary>
  365. /// <param name="view"></param>
  366. protected virtual void OnSubViewRemoved (View view) { }
  367. /// <summary>Raised when a SubView has been added to this View.</summary>
  368. public event EventHandler<SuperViewChangedEventArgs>? SubViewRemoved;
  369. // TODO: Make this non-virtual once WizardStep is refactored to use events
  370. /// <summary>
  371. /// Removes all SubViews added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
  372. /// </summary>
  373. /// <remarks>
  374. /// <para>
  375. /// Normally SubViews will be disposed when this View is disposed. Removing a SubView causes ownership of the
  376. /// SubView's
  377. /// lifecycle to be transferred to the caller; the caller must call <see cref="Dispose()"/> on any Views that were
  378. /// added.
  379. /// </para>
  380. /// </remarks>
  381. /// <returns>
  382. /// A list of removed Views.
  383. /// </returns>
  384. /// <seealso cref="Add(View)"/>
  385. /// <seealso cref="Remove(View)"/>
  386. /// <seealso cref="OnSubViewAdded"/>
  387. /// <seealso cref="OnSubViewRemoved"/>
  388. /// <seealso cref="SubViewRemoved"/>
  389. /// <seealso cref="OnSuperViewChanging"/>
  390. /// <seealso cref="SuperViewChanging"/>
  391. /// <seealso cref="OnSuperViewChanged"/>
  392. /// <seealso cref="SuperViewChanged"/>
  393. public virtual IReadOnlyCollection<View> RemoveAll ()
  394. {
  395. List<View> removedList = new ();
  396. while (InternalSubViews.Count > 0)
  397. {
  398. View? removed = Remove (InternalSubViews [0]);
  399. if (removed is { })
  400. {
  401. removedList.Add (removed);
  402. }
  403. }
  404. return removedList.AsReadOnly ();
  405. }
  406. /// <summary>
  407. /// Removes all SubViews of a type added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
  408. /// </summary>
  409. /// <remarks>
  410. /// <para>
  411. /// Normally SubViews will be disposed when this View is disposed. Removing a SubView causes ownership of the
  412. /// SubView's
  413. /// lifecycle to be transferred to the caller; the caller must call <see cref="Dispose()"/> on any Views that were
  414. /// added.
  415. /// </para>
  416. /// </remarks>
  417. /// <returns>
  418. /// A list of removed Views.
  419. /// </returns>
  420. public IReadOnlyCollection<TView> RemoveAll<TView> () where TView : View
  421. {
  422. List<TView> removedList = new ();
  423. foreach (TView view in InternalSubViews.OfType<TView> ().ToList ())
  424. {
  425. Remove (view);
  426. removedList.Add (view);
  427. }
  428. return removedList.AsReadOnly ();
  429. }
  430. #pragma warning disable CS0067 // The event is never used
  431. /// <summary>Raised when a SubView has been removed from this View.</summary>
  432. public event EventHandler<SuperViewChangedEventArgs>? Removed;
  433. #pragma warning restore CS0067 // The event is never used
  434. #endregion AddRemove
  435. // TODO: This drives a weird coupling of Application.TopRunnable and View. It's not clear why this is needed.
  436. /// <summary>Get the top superview of a given <see cref="View"/>.</summary>
  437. /// <returns>The superview view.</returns>
  438. internal View? GetTopSuperView (View? view = null, View? superview = null)
  439. {
  440. View? top = superview ?? App?.TopRunnableView;
  441. for (View? v = view?.SuperView ?? this?.SuperView; v != null; v = v.SuperView)
  442. {
  443. top = v;
  444. if (top == superview)
  445. {
  446. break;
  447. }
  448. }
  449. return top;
  450. }
  451. /// <summary>
  452. /// Gets whether <paramref name="view"/> is in the View hierarchy of <paramref name="start"/>.
  453. /// </summary>
  454. /// <param name="start">The View at the start of the hierarchy.</param>
  455. /// <param name="view">The View to test.</param>
  456. /// <param name="includeAdornments">Will include all <see cref="Adornment"/>s in addition to Subviews if true.</param>
  457. /// <returns></returns>
  458. public static bool IsInHierarchy (View? start, View? view, bool includeAdornments = false)
  459. {
  460. if (view is null || start is null)
  461. {
  462. return false;
  463. }
  464. if (view == start)
  465. {
  466. return true;
  467. }
  468. foreach (View subView in start.InternalSubViews)
  469. {
  470. if (view == subView)
  471. {
  472. return true;
  473. }
  474. bool found = IsInHierarchy (subView, view, includeAdornments);
  475. if (found)
  476. {
  477. return found;
  478. }
  479. }
  480. if (includeAdornments)
  481. {
  482. bool found = IsInHierarchy (start.Padding, view, includeAdornments);
  483. if (found)
  484. {
  485. return found;
  486. }
  487. found = IsInHierarchy (start.Border, view, includeAdornments);
  488. if (found)
  489. {
  490. return found;
  491. }
  492. found = IsInHierarchy (start.Margin, view, includeAdornments);
  493. if (found)
  494. {
  495. return found;
  496. }
  497. }
  498. return false;
  499. }
  500. #region SubViewOrdering
  501. /// <summary>
  502. /// Moves <paramref name="subview"/> one position towards the end of the <see cref="SubViews"/> list.
  503. /// </summary>
  504. /// <param name="subview">The subview to move.</param>
  505. public void MoveSubViewTowardsEnd (View subview)
  506. {
  507. PerformActionForSubView (
  508. subview,
  509. x =>
  510. {
  511. int idx = InternalSubViews!.IndexOf (x);
  512. if (idx + 1 < InternalSubViews.Count)
  513. {
  514. InternalSubViews.Remove (x);
  515. InternalSubViews.Insert (idx + 1, x);
  516. }
  517. }
  518. );
  519. }
  520. /// <summary>
  521. /// Moves <paramref name="subview"/> to the end of the <see cref="SubViews"/> list.
  522. /// If the <see cref="Arrangement"/> is <see cref="ViewArrangement.Overlapped"/>, keeps the original sorting.
  523. /// </summary>
  524. /// <param name="subview">The subview to move.</param>
  525. public void MoveSubViewToEnd (View subview)
  526. {
  527. if (Arrangement.HasFlag (ViewArrangement.Overlapped))
  528. {
  529. PerformActionForSubView (
  530. subview,
  531. x =>
  532. {
  533. while (InternalSubViews!.IndexOf (x) != InternalSubViews.Count - 1)
  534. {
  535. View v = InternalSubViews [0];
  536. InternalSubViews!.Remove (v);
  537. InternalSubViews.Add (v);
  538. }
  539. }
  540. );
  541. return;
  542. }
  543. PerformActionForSubView (
  544. subview,
  545. x =>
  546. {
  547. InternalSubViews!.Remove (x);
  548. InternalSubViews.Add (x);
  549. }
  550. );
  551. }
  552. /// <summary>
  553. /// Moves <paramref name="subview"/> one position towards the start of the <see cref="SubViews"/> list.
  554. /// </summary>
  555. /// <param name="subview">The subview to move.</param>
  556. public void MoveSubViewTowardsStart (View subview)
  557. {
  558. PerformActionForSubView (
  559. subview,
  560. x =>
  561. {
  562. int idx = InternalSubViews!.IndexOf (x);
  563. if (idx > 0)
  564. {
  565. InternalSubViews.Remove (x);
  566. InternalSubViews.Insert (idx - 1, x);
  567. }
  568. }
  569. );
  570. }
  571. /// <summary>
  572. /// Moves <paramref name="subview"/> to the start of the <see cref="SubViews"/> list.
  573. /// </summary>
  574. /// <param name="subview">The subview to move.</param>
  575. public void MoveSubViewToStart (View subview)
  576. {
  577. PerformActionForSubView (
  578. subview,
  579. x =>
  580. {
  581. InternalSubViews!.Remove (x);
  582. InternalSubViews.Insert (0, subview);
  583. }
  584. );
  585. }
  586. /// <summary>
  587. /// Internal API that runs <paramref name="action"/> on a subview if it is part of the <see cref="SubViews"/> list.
  588. /// </summary>
  589. /// <param name="subview"></param>
  590. /// <param name="action"></param>
  591. private void PerformActionForSubView (View subview, Action<View> action)
  592. {
  593. if (InternalSubViews.Contains (subview))
  594. {
  595. action (subview);
  596. }
  597. // BUGBUG: this is odd. Why is this needed?
  598. SetNeedsDraw ();
  599. subview.SetNeedsDraw ();
  600. }
  601. #endregion SubViewOrdering
  602. }