Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #2663 - No more nested classes #2664

Merged
merged 11 commits into from
May 23, 2023
75 changes: 0 additions & 75 deletions Terminal.Gui/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,81 +324,6 @@ static void ResetState ()
/// </remarks>
public static event EventHandler<ToplevelEventArgs> NotifyStopRunState;

/// <summary>
/// The execution state for a <see cref="Toplevel"/> view.
/// </summary>
public class RunState : IDisposable {
/// <summary>
/// Initializes a new <see cref="RunState"/> class.
/// </summary>
/// <param name="view"></param>
public RunState (Toplevel view)
{
Toplevel = view;
}
/// <summary>
/// The <see cref="Toplevel"/> belonging to this <see cref="RunState"/>.
/// </summary>
public Toplevel Toplevel { get; internal set; }

#if DEBUG_IDISPOSABLE
/// <summary>
/// For debug (see DEBUG_IDISPOSABLE define) purposes to verify objects are being disposed properly
/// </summary>
public bool WasDisposed = false;

/// <summary>
/// For debug (see DEBUG_IDISPOSABLE define) purposes to verify objects are being disposed properly
/// </summary>
public int DisposedCount = 0;

/// <summary>
/// For debug (see DEBUG_IDISPOSABLE define) purposes; the runstate instances that have been created
/// </summary>
public static List<RunState> Instances = new List<RunState> ();

/// <summary>
/// Creates a new RunState object.
/// </summary>
public RunState ()
{
Instances.Add (this);
}
#endif

/// <summary>
/// Releases all resource used by the <see cref="Application.RunState"/> object.
/// </summary>
/// <remarks>
/// Call <see cref="Dispose()"/> when you are finished using the <see cref="Application.RunState"/>.
/// </remarks>
/// <remarks>
/// <see cref="Dispose()"/> method leaves the <see cref="Application.RunState"/> in an unusable state. After
/// calling <see cref="Dispose()"/>, you must release all references to the
/// <see cref="Application.RunState"/> so the garbage collector can reclaim the memory that the
/// <see cref="Application.RunState"/> was occupying.
/// </remarks>
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
#if DEBUG_IDISPOSABLE
WasDisposed = true;
#endif
}

/// <summary>
/// Releases all resource used by the <see cref="Application.RunState"/> object.
/// </summary>
/// <param name="disposing">If set to <see langword="true"/> we are disposing and should dispose held objects.</param>
protected virtual void Dispose (bool disposing)
{
if (Toplevel != null && disposing) {
throw new InvalidOperationException ("You must clean up (Dispose) the Toplevel before calling Application.RunState.Dispose");
}
}
}

/// <summary>
/// Building block API: Prepares the provided <see cref="Toplevel"/> for execution.
/// </summary>
Expand Down
61 changes: 29 additions & 32 deletions Terminal.Gui/Configuration/AppScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,34 @@

#nullable enable

namespace Terminal.Gui {
namespace Terminal.Gui;

public static partial class ConfigurationManager {
/// <summary>
/// The <see cref="Scope{T}"/> class for application-defined configuration settings.
/// </summary>
/// <remarks>
/// </remarks>
/// <example>
/// <para>
/// Use the <see cref="SerializableConfigurationProperty"/> attribute to mark properties that should be serialized as part
/// of application-defined configuration settings.
/// </para>
/// <code>
/// public class MyAppSettings {
/// [SerializableConfigurationProperty (Scope = typeof (AppScope))]
/// public static bool? MyProperty { get; set; } = true;
/// }
/// </code>
/// <para>
/// THe resultant Json will look like this:
/// </para>
/// <code>
/// "AppSettings": {
/// "MyAppSettings.MyProperty": true,
/// "UICatalog.ShowStatusBar": true
/// },
/// </code>
/// </example>
[JsonConverter (typeof (ScopeJsonConverter<AppScope>))]
public class AppScope : Scope<AppScope> {
}
}
/// <summary>
/// The <see cref="Scope{T}"/> class for application-defined configuration settings.
/// </summary>
/// <remarks>
/// </remarks>
/// <example>
/// <para>
/// Use the <see cref="SerializableConfigurationProperty"/> attribute to mark properties that should be serialized as part
/// of application-defined configuration settings.
/// </para>
/// <code>
/// public class MyAppSettings {
/// [SerializableConfigurationProperty (Scope = typeof (AppScope))]
/// public static bool? MyProperty { get; set; } = true;
/// }
/// </code>
/// <para>
/// THe resultant Json will look like this:
/// </para>
/// <code>
/// "AppSettings": {
/// "MyAppSettings.MyProperty": true,
/// "UICatalog.ShowStatusBar": true
/// },
/// </code>
/// </example>
[JsonConverter (typeof (ScopeJsonConverter<AppScope>))]
public class AppScope : Scope<AppScope> {
}
97 changes: 97 additions & 0 deletions Terminal.Gui/Configuration/ConfigProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System;
using System.Reflection;
using System.Text.Json.Serialization;

#nullable enable

namespace Terminal.Gui;

/// <summary>
/// Holds a property's value and the <see cref="PropertyInfo"/> that allows <see cref="ConfigurationManager"/>
/// to get and set the property's value.
/// </summary>
/// <remarks>
/// Configuration properties must be <see langword="public"/> and <see langword="static"/>
/// and have the <see cref="SerializableConfigurationProperty"/>
/// attribute. If the type of the property requires specialized JSON serialization,
/// a <see cref="JsonConverter"/> must be provided using
/// the <see cref="JsonConverterAttribute"/> attribute.
/// </remarks>
public class ConfigProperty {
private object? propertyValue;

/// <summary>
/// Describes the property.
/// </summary>
public PropertyInfo? PropertyInfo { get; set; }

/// <summary>
/// Helper to get either the Json property named (specified by [JsonPropertyName(name)]
/// or the actual property name.
/// </summary>
/// <param name="pi"></param>
/// <returns></returns>
public static string GetJsonPropertyName (PropertyInfo pi)
{
var jpna = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute;
return jpna?.Name ?? pi.Name;
}

/// <summary>
/// Holds the property's value as it was either read from the class's implementation or from a config file.
/// If the property has not been set (e.g. because no configuration file specified a value),
/// this will be <see langword="null"/>.
/// </summary>
/// <remarks>
/// On <see langword="set"/>, performs a sparse-copy of the new value to the existing value (only copies elements of
/// the object that are non-null).
/// </remarks>
public object? PropertyValue {
get => propertyValue;
set {
propertyValue = value;
}
}

internal object? UpdateValueFrom (object source)
{
if (source == null) {
return PropertyValue;
}

var ut = Nullable.GetUnderlyingType (PropertyInfo!.PropertyType);
if (source.GetType () != PropertyInfo!.PropertyType && (ut != null && source.GetType () != ut)) {
throw new ArgumentException ($"The source object ({PropertyInfo!.DeclaringType}.{PropertyInfo!.Name}) is not of type {PropertyInfo!.PropertyType}.");
}
if (PropertyValue != null && source != null) {
PropertyValue = ConfigurationManager.DeepMemberwiseCopy (source, PropertyValue);
} else {
PropertyValue = source;
}

return PropertyValue;
}

/// <summary>
/// Retrieves (using reflection) the value of the static property described in <see cref="PropertyInfo"/>
/// into <see cref="PropertyValue"/>.
/// </summary>
/// <returns></returns>
public object? RetrieveValue ()
{
return PropertyValue = PropertyInfo!.GetValue (null);
}

/// <summary>
/// Applies the <see cref="PropertyValue"/> to the property described by <see cref="PropertyInfo"/>.
/// </summary>
/// <returns></returns>
public bool Apply ()
{
if (PropertyValue != null) {
PropertyInfo?.SetValue (null, ConfigurationManager.DeepMemberwiseCopy (PropertyValue, PropertyInfo?.GetValue (null)));
}
return PropertyValue != null;
}

}
116 changes: 2 additions & 114 deletions Terminal.Gui/Configuration/ConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,118 +69,6 @@ public static partial class ConfigurationManager {
},
};

/// <summary>
/// An attribute that can be applied to a property to indicate that it should included in the configuration file.
/// </summary>
/// <example>
/// [SerializableConfigurationProperty(Scope = typeof(Configuration.ThemeManager.ThemeScope)), JsonConverter (typeof (JsonStringEnumConverter))]
/// public static LineStyle DefaultBorderStyle {
/// ...
/// </example>
[AttributeUsage (AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class SerializableConfigurationProperty : System.Attribute {
/// <summary>
/// Specifies the scope of the property.
/// </summary>
public Type? Scope { get; set; }

/// <summary>
/// If <see langword="true"/>, the property will be serialized to the configuration file using only the property name
/// as the key. If <see langword="false"/>, the property will be serialized to the configuration file using the
/// property name pre-pended with the classname (e.g. <c>Application.UseSystemConsole</c>).
/// </summary>
public bool OmitClassName { get; set; }
}

/// <summary>
/// Holds a property's value and the <see cref="PropertyInfo"/> that allows <see cref="ConfigurationManager"/>
/// to get and set the property's value.
/// </summary>
/// <remarks>
/// Configuration properties must be <see langword="public"/> and <see langword="static"/>
/// and have the <see cref="SerializableConfigurationProperty"/>
/// attribute. If the type of the property requires specialized JSON serialization,
/// a <see cref="JsonConverter"/> must be provided using
/// the <see cref="JsonConverterAttribute"/> attribute.
/// </remarks>
public class ConfigProperty {
private object? propertyValue;

/// <summary>
/// Describes the property.
/// </summary>
public PropertyInfo? PropertyInfo { get; set; }

/// <summary>
/// Helper to get either the Json property named (specified by [JsonPropertyName(name)]
/// or the actual property name.
/// </summary>
/// <param name="pi"></param>
/// <returns></returns>
public static string GetJsonPropertyName (PropertyInfo pi)
{
var jpna = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute;
return jpna?.Name ?? pi.Name;
}

/// <summary>
/// Holds the property's value as it was either read from the class's implementation or from a config file.
/// If the property has not been set (e.g. because no configuration file specified a value),
/// this will be <see langword="null"/>.
/// </summary>
/// <remarks>
/// On <see langword="set"/>, performs a sparse-copy of the new value to the existing value (only copies elements of
/// the object that are non-null).
/// </remarks>
public object? PropertyValue {
get => propertyValue;
set {
propertyValue = value;
}
}

internal object? UpdateValueFrom (object source)
{
if (source == null) {
return PropertyValue;
}

var ut = Nullable.GetUnderlyingType (PropertyInfo!.PropertyType);
if (source.GetType () != PropertyInfo!.PropertyType && (ut != null && source.GetType () != ut)) {
throw new ArgumentException ($"The source object ({PropertyInfo!.DeclaringType}.{PropertyInfo!.Name}) is not of type {PropertyInfo!.PropertyType}.");
}
if (PropertyValue != null && source != null) {
PropertyValue = DeepMemberwiseCopy (source, PropertyValue);
} else {
PropertyValue = source;
}

return PropertyValue;
}

/// <summary>
/// Retrieves (using reflection) the value of the static property described in <see cref="PropertyInfo"/>
/// into <see cref="PropertyValue"/>.
/// </summary>
/// <returns></returns>
public object? RetrieveValue ()
{
return PropertyValue = PropertyInfo!.GetValue (null);
}

/// <summary>
/// Applies the <see cref="PropertyValue"/> to the property described by <see cref="PropertyInfo"/>.
/// </summary>
/// <returns></returns>
public bool Apply ()
{
if (PropertyValue != null) {
PropertyInfo?.SetValue (null, DeepMemberwiseCopy (PropertyValue, PropertyInfo?.GetValue (null)));
}
return PropertyValue != null;
}
}

/// <summary>
/// A dictionary of all properties in the Terminal.Gui project that are decorated with the <see cref="SerializableConfigurationProperty"/> attribute.
/// The keys are the property names pre-pended with the class that implements the property (e.g. <c>Application.UseSystemConsole</c>).
Expand All @@ -190,7 +78,7 @@ public bool Apply ()
/// <remarks>
/// Is <see langword="null"/> until <see cref="Initialize"/> is called.
/// </remarks>
private static Dictionary<string, ConfigProperty>? _allConfigProperties;
internal static Dictionary<string, ConfigProperty>? _allConfigProperties;

/// <summary>
/// The backing property for <see cref="Settings"/>.
Expand Down Expand Up @@ -319,7 +207,7 @@ internal static Stream ToStream ()

internal static StringBuilder jsonErrors = new StringBuilder ();

private static void AddJsonError (string error)
internal static void AddJsonError (string error)
{
Debug.WriteLine ($"ConfigurationManager: {error}");
jsonErrors.AppendLine (error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public ConfigurationManagerEventArgs ()
}

/// <summary>
/// Event arguments for the <see cref="ConfigurationManager.ThemeManager"/> events.
/// Event arguments for the <see cref="ThemeManager"/> events.
/// </summary>
public class ThemeManagerEventArgs : EventArgs {
/// <summary>
Expand Down
Loading
Loading