|
@@ -1,4 +1,3 @@
|
|
|
-using System.Collections.ObjectModel;
|
|
|
|
|
using System.Diagnostics;
|
|
using System.Diagnostics;
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
|
|
@@ -6,7 +5,6 @@ namespace Terminal.Gui.ViewBase;
|
|
|
|
|
|
|
|
public partial class View // SuperView/SubView hierarchy management (SuperView, SubViews, Add, Remove, etc.)
|
|
public partial class View // SuperView/SubView hierarchy management (SuperView, SubViews, Add, Remove, etc.)
|
|
|
{
|
|
{
|
|
|
- [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
|
|
|
|
private static readonly IReadOnlyCollection<View> _empty = [];
|
|
private static readonly IReadOnlyCollection<View> _empty = [];
|
|
|
|
|
|
|
|
private readonly List<View>? _subviews = [];
|
|
private readonly List<View>? _subviews = [];
|
|
@@ -27,48 +25,73 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
/// Gets this Views SuperView (the View's container), or <see langword="null"/> if this view has not been added as a
|
|
/// Gets this Views SuperView (the View's container), or <see langword="null"/> if this view has not been added as a
|
|
|
/// SubView.
|
|
/// SubView.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
|
|
+ /// <seealso cref="OnSuperViewChanging"/>
|
|
|
|
|
+ /// <seealso cref="SuperViewChanging"/>
|
|
|
/// <seealso cref="OnSuperViewChanged"/>
|
|
/// <seealso cref="OnSuperViewChanged"/>
|
|
|
/// <seealso cref="SuperViewChanged"/>
|
|
/// <seealso cref="SuperViewChanged"/>
|
|
|
- public View? SuperView
|
|
|
|
|
- {
|
|
|
|
|
- get => _superView!;
|
|
|
|
|
- private set => SetSuperView (value);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public View? SuperView => _superView!;
|
|
|
|
|
|
|
|
- private void SetSuperView (View? value)
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// INTERNAL: Sets the SuperView of this View.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="value"></param>
|
|
|
|
|
+ /// <returns><see langword="true"/> if the SuperView was changed; otherwise, <see langword="false"/>.</returns>
|
|
|
|
|
+ private bool SetSuperView (View? value)
|
|
|
{
|
|
{
|
|
|
if (_superView == value)
|
|
if (_superView == value)
|
|
|
{
|
|
{
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- _superView = value;
|
|
|
|
|
- RaiseSuperViewChanged ();
|
|
|
|
|
|
|
+ return CWPPropertyHelper.ChangeProperty (
|
|
|
|
|
+ this,
|
|
|
|
|
+ ref _superView,
|
|
|
|
|
+ value,
|
|
|
|
|
+ OnSuperViewChanging,
|
|
|
|
|
+ SuperViewChanging,
|
|
|
|
|
+ newValue => _superView = newValue,
|
|
|
|
|
+ OnSuperViewChanged,
|
|
|
|
|
+ SuperViewChanged,
|
|
|
|
|
+ out View? _);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void RaiseSuperViewChanged ()
|
|
|
|
|
- {
|
|
|
|
|
- SuperViewChangedEventArgs args = new (SuperView, this);
|
|
|
|
|
- OnSuperViewChanged (args);
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Called when the SuperView of this View is about to be changed. This is called before the SuperView property
|
|
|
|
|
+ /// is updated, allowing access to the current SuperView and its resources (such as <see cref="App"/>) for
|
|
|
|
|
+ /// cleanup purposes.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="args">Hold the new SuperView that will be set, or <see langword="null"/> if being removed.</param>
|
|
|
|
|
+ /// <returns><see langword="true"/> to cancel the change; <see langword="false"/> to allow it.</returns>
|
|
|
|
|
+ protected virtual bool OnSuperViewChanging (ValueChangingEventArgs<View?> args) => false;
|
|
|
|
|
|
|
|
- SuperViewChanged?.Invoke (this, args);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Raised when the SuperView of this View is about to be changed. This is raised before the SuperView property
|
|
|
|
|
+ /// is updated, allowing access to the current SuperView and its resources (such as <see cref="App"/>) for
|
|
|
|
|
+ /// cleanup purposes.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <remarks>
|
|
|
|
|
+ /// <para>
|
|
|
|
|
+ /// This event follows the Cancellable Work Pattern (CWP). Set <see cref="ValueChangingEventArgs{T}.Handled"/>
|
|
|
|
|
+ /// to <see langword="true"/> in the event args to cancel the change.
|
|
|
|
|
+ /// </para>
|
|
|
|
|
+ /// </remarks>
|
|
|
|
|
+ public event EventHandler<ValueChangingEventArgs<View?>>? SuperViewChanging;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Called when the SuperView of this View has changed.
|
|
/// Called when the SuperView of this View has changed.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- /// <param name="e"></param>
|
|
|
|
|
- protected virtual void OnSuperViewChanged (SuperViewChangedEventArgs e) { }
|
|
|
|
|
|
|
+ protected virtual void OnSuperViewChanged (ValueChangedEventArgs<View?> args) { }
|
|
|
|
|
|
|
|
/// <summary>Raised when the SuperView of this View has changed.</summary>
|
|
/// <summary>Raised when the SuperView of this View has changed.</summary>
|
|
|
- public event EventHandler<SuperViewChangedEventArgs>? SuperViewChanged;
|
|
|
|
|
|
|
+ public event EventHandler<ValueChangedEventArgs<View?>>? SuperViewChanged;
|
|
|
|
|
|
|
|
#region AddRemove
|
|
#region AddRemove
|
|
|
|
|
|
|
|
|
|
+ // TODO: Make this non-virtual once WizardStep is refactored to use events
|
|
|
/// <summary>Adds a SubView (child) to this view.</summary>
|
|
/// <summary>Adds a SubView (child) to this view.</summary>
|
|
|
/// <remarks>
|
|
/// <remarks>
|
|
|
/// <para>
|
|
/// <para>
|
|
|
- /// The Views that have been added to this view can be retrieved via the <see cref="SubViews"/> property.
|
|
|
|
|
|
|
+ /// The Views that have been added to this view can be retrieved via the <see cref="SubViews"/> property.
|
|
|
/// </para>
|
|
/// </para>
|
|
|
/// <para>
|
|
/// <para>
|
|
|
/// To check if a View has been added to this View, compare it's <see cref="SuperView"/> property to this View.
|
|
/// To check if a View has been added to this View, compare it's <see cref="SuperView"/> property to this View.
|
|
@@ -90,7 +113,10 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
/// <seealso cref="RemoveAll"/>
|
|
/// <seealso cref="RemoveAll"/>
|
|
|
/// <seealso cref="OnSubViewAdded"/>
|
|
/// <seealso cref="OnSubViewAdded"/>
|
|
|
/// <seealso cref="SubViewAdded"/>
|
|
/// <seealso cref="SubViewAdded"/>
|
|
|
-
|
|
|
|
|
|
|
+ /// <seealso cref="OnSuperViewChanging"/>
|
|
|
|
|
+ /// <seealso cref="SuperViewChanging"/>
|
|
|
|
|
+ /// <seealso cref="OnSuperViewChanged"/>
|
|
|
|
|
+ /// <seealso cref="SuperViewChanged"/>
|
|
|
public virtual View? Add (View? view)
|
|
public virtual View? Add (View? view)
|
|
|
{
|
|
{
|
|
|
if (view is null)
|
|
if (view is null)
|
|
@@ -99,7 +125,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//Debug.Assert (view.SuperView is null, $"{view} already has a SuperView: {view.SuperView}.");
|
|
//Debug.Assert (view.SuperView is null, $"{view} already has a SuperView: {view.SuperView}.");
|
|
|
- if (view.SuperView is {})
|
|
|
|
|
|
|
+ if (view.SuperView is { })
|
|
|
{
|
|
{
|
|
|
Logging.Warning ($"{view} already has a SuperView: {view.SuperView}.");
|
|
Logging.Warning ($"{view} already has a SuperView: {view.SuperView}.");
|
|
|
}
|
|
}
|
|
@@ -110,12 +136,19 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
Logging.Warning ($"{view} has already been Added to {this}.");
|
|
Logging.Warning ($"{view} has already been Added to {this}.");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Ensure views don't have focus when being added
|
|
|
|
|
- view.HasFocus = false;
|
|
|
|
|
-
|
|
|
|
|
// TODO: Make this thread safe
|
|
// TODO: Make this thread safe
|
|
|
InternalSubViews.Add (view);
|
|
InternalSubViews.Add (view);
|
|
|
- view.SuperView = this;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Try to set the SuperView - this may be cancelled
|
|
|
|
|
+ if (!view.SetSuperView (this))
|
|
|
|
|
+ {
|
|
|
|
|
+ InternalSubViews.Remove (view);
|
|
|
|
|
+ // The change was cancelled
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Ensure views don't have focus when being added
|
|
|
|
|
+ view.HasFocus = false;
|
|
|
|
|
|
|
|
if (view is { Enabled: true, Visible: true, CanFocus: true })
|
|
if (view is { Enabled: true, Visible: true, CanFocus: true })
|
|
|
{
|
|
{
|
|
@@ -193,6 +226,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
|
public event EventHandler<SuperViewChangedEventArgs>? SubViewAdded;
|
|
public event EventHandler<SuperViewChangedEventArgs>? SubViewAdded;
|
|
|
|
|
|
|
|
|
|
+ // TODO: Make this non-virtual once WizardStep is refactored to use events
|
|
|
/// <summary>Removes a SubView added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.</summary>
|
|
/// <summary>Removes a SubView added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.</summary>
|
|
|
/// <remarks>
|
|
/// <remarks>
|
|
|
/// <para>
|
|
/// <para>
|
|
@@ -210,8 +244,15 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
/// <returns>
|
|
/// <returns>
|
|
|
/// The removed View. <see langword="null"/> if the View could not be removed.
|
|
/// The removed View. <see langword="null"/> if the View could not be removed.
|
|
|
/// </returns>
|
|
/// </returns>
|
|
|
|
|
+ /// <seealso cref="Add(View)"/>
|
|
|
|
|
+ /// <seealso cref="RemoveAll"/>
|
|
|
|
|
+ /// <seealso cref="OnSubViewAdded"/>
|
|
|
/// <seealso cref="OnSubViewRemoved"/>
|
|
/// <seealso cref="OnSubViewRemoved"/>
|
|
|
- /// <seealso cref="SubViewRemoved"/>"/>
|
|
|
|
|
|
|
+ /// <seealso cref="SubViewRemoved"/>
|
|
|
|
|
+ /// <seealso cref="OnSuperViewChanging"/>
|
|
|
|
|
+ /// <seealso cref="SuperViewChanging"/>
|
|
|
|
|
+ /// <seealso cref="OnSuperViewChanged"/>
|
|
|
|
|
+ /// <seealso cref="SuperViewChanged"/>
|
|
|
public virtual View? Remove (View? view)
|
|
public virtual View? Remove (View? view)
|
|
|
{
|
|
{
|
|
|
if (view is null)
|
|
if (view is null)
|
|
@@ -221,7 +262,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
|
|
|
|
|
if (InternalSubViews.Count == 0)
|
|
if (InternalSubViews.Count == 0)
|
|
|
{
|
|
{
|
|
|
- return view;
|
|
|
|
|
|
|
+ return view;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (view.SuperView is null)
|
|
if (view.SuperView is null)
|
|
@@ -256,18 +297,28 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
|
|
|
|
|
Debug.Assert (!view.HasFocus);
|
|
Debug.Assert (!view.HasFocus);
|
|
|
|
|
|
|
|
|
|
+ View? previousSuperView = view.SuperView;
|
|
|
|
|
+
|
|
|
|
|
+ // Try to clear the SuperView - this may be cancelled
|
|
|
|
|
+ if (!view.SetSuperView (null))
|
|
|
|
|
+ {
|
|
|
|
|
+ // The change was cancelled, restore state and return null
|
|
|
|
|
+ view.CanFocus = couldFocus;
|
|
|
|
|
+
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Debug.Assert(view.SuperView is null);
|
|
|
InternalSubViews.Remove (view);
|
|
InternalSubViews.Remove (view);
|
|
|
|
|
|
|
|
// Clean up focus stuff
|
|
// Clean up focus stuff
|
|
|
_previouslyFocused = null;
|
|
_previouslyFocused = null;
|
|
|
|
|
|
|
|
- if (view.SuperView is { } && view.SuperView._previouslyFocused == this)
|
|
|
|
|
|
|
+ if (previousSuperView is { } && previousSuperView._previouslyFocused == this)
|
|
|
{
|
|
{
|
|
|
- view.SuperView._previouslyFocused = null;
|
|
|
|
|
|
|
+ previousSuperView._previouslyFocused = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- view.SuperView = null;
|
|
|
|
|
-
|
|
|
|
|
SetNeedsLayout ();
|
|
SetNeedsLayout ();
|
|
|
SetNeedsDraw ();
|
|
SetNeedsDraw ();
|
|
|
|
|
|
|
@@ -306,6 +357,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
/// <summary>Raised when a SubView has been added to this View.</summary>
|
|
/// <summary>Raised when a SubView has been added to this View.</summary>
|
|
|
public event EventHandler<SuperViewChangedEventArgs>? SubViewRemoved;
|
|
public event EventHandler<SuperViewChangedEventArgs>? SubViewRemoved;
|
|
|
|
|
|
|
|
|
|
+ // TODO: Make this non-virtual once WizardStep is refactored to use events
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Removes all SubViews added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
|
|
/// Removes all SubViews added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -320,12 +372,23 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
/// <returns>
|
|
/// <returns>
|
|
|
/// A list of removed Views.
|
|
/// A list of removed Views.
|
|
|
/// </returns>
|
|
/// </returns>
|
|
|
|
|
+ /// <seealso cref="Add(View)"/>
|
|
|
|
|
+ /// <seealso cref="Remove(View)"/>
|
|
|
|
|
+ /// <seealso cref="OnSubViewAdded"/>
|
|
|
|
|
+ /// <seealso cref="OnSubViewRemoved"/>
|
|
|
|
|
+ /// <seealso cref="SubViewRemoved"/>
|
|
|
|
|
+ /// <seealso cref="OnSuperViewChanging"/>
|
|
|
|
|
+ /// <seealso cref="SuperViewChanging"/>
|
|
|
|
|
+ /// <seealso cref="OnSuperViewChanged"/>
|
|
|
|
|
+ /// <seealso cref="SuperViewChanged"/>
|
|
|
public virtual IReadOnlyCollection<View> RemoveAll ()
|
|
public virtual IReadOnlyCollection<View> RemoveAll ()
|
|
|
{
|
|
{
|
|
|
- List<View> removedList = new List<View> ();
|
|
|
|
|
|
|
+ List<View> removedList = new ();
|
|
|
|
|
+
|
|
|
while (InternalSubViews.Count > 0)
|
|
while (InternalSubViews.Count > 0)
|
|
|
{
|
|
{
|
|
|
View? removed = Remove (InternalSubViews [0]);
|
|
View? removed = Remove (InternalSubViews [0]);
|
|
|
|
|
+
|
|
|
if (removed is { })
|
|
if (removed is { })
|
|
|
{
|
|
{
|
|
|
removedList.Add (removed);
|
|
removedList.Add (removed);
|
|
@@ -349,14 +412,16 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
|
|
|
/// <returns>
|
|
/// <returns>
|
|
|
/// A list of removed Views.
|
|
/// A list of removed Views.
|
|
|
/// </returns>
|
|
/// </returns>
|
|
|
- public virtual IReadOnlyCollection<TView> RemoveAll<TView> () where TView : View
|
|
|
|
|
|
|
+ public IReadOnlyCollection<TView> RemoveAll<TView> () where TView : View
|
|
|
{
|
|
{
|
|
|
- List<TView> removedList = new List<TView> ();
|
|
|
|
|
|
|
+ List<TView> removedList = new ();
|
|
|
|
|
+
|
|
|
foreach (TView view in InternalSubViews.OfType<TView> ().ToList ())
|
|
foreach (TView view in InternalSubViews.OfType<TView> ().ToList ())
|
|
|
{
|
|
{
|
|
|
Remove (view);
|
|
Remove (view);
|
|
|
removedList.Add (view);
|
|
removedList.Add (view);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
return removedList.AsReadOnly ();
|
|
return removedList.AsReadOnly ();
|
|
|
}
|
|
}
|
|
|
|
|
|