mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 07:47:54 +01:00
Adds Logging level control to UICatalog (#3938)
* Tons of API doc updates * Added logging control to UICatalog * Added logging control to UICatalog - more * fixed minor issues * removed logs from .gitignore * Fixed log file path * Fixed app desc
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -58,3 +58,5 @@ demo.*
|
||||
*.tui/
|
||||
|
||||
*.dotCover
|
||||
|
||||
logs/
|
||||
|
||||
@@ -215,6 +215,7 @@ public class ApplicationImpl : IApplication
|
||||
|
||||
bool wasInitialized = Application.Initialized;
|
||||
Application.ResetState ();
|
||||
LogJsonErrors ();
|
||||
PrintJsonErrors ();
|
||||
|
||||
if (wasInitialized)
|
||||
|
||||
@@ -57,11 +57,11 @@ internal class AttributeJsonConverter : JsonConverter<Attribute>
|
||||
switch (propertyName?.ToLower ())
|
||||
{
|
||||
case "foreground":
|
||||
foreground = JsonSerializer.Deserialize (color, _serializerContext.Color);
|
||||
foreground = JsonSerializer.Deserialize (color, SerializerContext.Color);
|
||||
|
||||
break;
|
||||
case "background":
|
||||
background = JsonSerializer.Deserialize (color, _serializerContext.Color);
|
||||
background = JsonSerializer.Deserialize (color, SerializerContext.Color);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ internal class ColorSchemeJsonConverter : JsonConverter<ColorScheme>
|
||||
|
||||
string propertyName = reader.GetString ();
|
||||
reader.Read ();
|
||||
var attribute = JsonSerializer.Deserialize (ref reader, _serializerContext.Attribute);
|
||||
var attribute = JsonSerializer.Deserialize (ref reader, SerializerContext.Attribute);
|
||||
|
||||
switch (propertyName.ToLower ())
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Runtime.Versioning;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
#nullable enable
|
||||
|
||||
@@ -65,7 +66,7 @@ public static class ConfigurationManager
|
||||
internal static Dictionary<string, ConfigProperty>? _allConfigProperties;
|
||||
|
||||
[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
||||
internal static readonly JsonSerializerOptions _serializerOptions = new ()
|
||||
internal static readonly JsonSerializerOptions SerializerOptions = new ()
|
||||
{
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
@@ -87,7 +88,7 @@ public static class ConfigurationManager
|
||||
};
|
||||
|
||||
[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
||||
internal static readonly SourceGenerationContext _serializerContext = new (_serializerOptions);
|
||||
internal static readonly SourceGenerationContext SerializerContext = new (SerializerOptions);
|
||||
|
||||
[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
||||
internal static StringBuilder _jsonErrors = new ();
|
||||
@@ -209,7 +210,7 @@ public static class ConfigurationManager
|
||||
var emptyScope = new SettingsScope ();
|
||||
emptyScope.Clear ();
|
||||
|
||||
return JsonSerializer.Serialize (emptyScope, typeof (SettingsScope), _serializerContext);
|
||||
return JsonSerializer.Serialize (emptyScope, typeof (SettingsScope), SerializerContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -235,7 +236,7 @@ public static class ConfigurationManager
|
||||
[RequiresDynamicCode ("AOT")]
|
||||
public static void Load (bool reset = false)
|
||||
{
|
||||
Debug.WriteLine ("ConfigurationManager.Load()");
|
||||
Logging.Trace ($"reset = {reset}");
|
||||
|
||||
if (reset)
|
||||
{
|
||||
@@ -292,7 +293,7 @@ public static class ConfigurationManager
|
||||
/// </summary>
|
||||
public static void OnApplied ()
|
||||
{
|
||||
Debug.WriteLine ("ConfigurationManager.OnApplied()");
|
||||
//Logging.Trace ("");
|
||||
|
||||
Applied?.Invoke (null, new ());
|
||||
|
||||
@@ -308,7 +309,7 @@ public static class ConfigurationManager
|
||||
/// </summary>
|
||||
public static void OnUpdated ()
|
||||
{
|
||||
Debug.WriteLine (@"ConfigurationManager.OnUpdated()");
|
||||
//Logging.Trace (@"");
|
||||
Updated?.Invoke (null, new ());
|
||||
}
|
||||
|
||||
@@ -324,6 +325,18 @@ public static class ConfigurationManager
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void LogJsonErrors ()
|
||||
{
|
||||
if (_jsonErrors.Length > 0)
|
||||
{
|
||||
Logging.Warning (
|
||||
@"Encountered the following errors while deserializing configuration files:"
|
||||
);
|
||||
Logging.Warning (_jsonErrors.ToString ());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the state of <see cref="ConfigurationManager"/>. Should be called whenever a new app session (e.g. in
|
||||
/// <see cref="Application.Init"/> starts. Called by <see cref="Load"/> if the <c>reset</c> parameter is
|
||||
@@ -334,7 +347,7 @@ public static class ConfigurationManager
|
||||
[RequiresDynamicCode ("AOT")]
|
||||
public static void Reset ()
|
||||
{
|
||||
Debug.WriteLine (@"ConfigurationManager.Reset()");
|
||||
Logging.Trace ($"_allConfigProperties = {_allConfigProperties}");
|
||||
|
||||
if (_allConfigProperties is null)
|
||||
{
|
||||
@@ -369,7 +382,7 @@ public static class ConfigurationManager
|
||||
|
||||
internal static void AddJsonError (string error)
|
||||
{
|
||||
Debug.WriteLine ($"ConfigurationManager: {error}");
|
||||
Logging.Trace ($"error = {error}");
|
||||
_jsonErrors.AppendLine (error);
|
||||
}
|
||||
|
||||
@@ -541,8 +554,8 @@ public static class ConfigurationManager
|
||||
classesWithConfigProps.Add (classWithConfig.Name, classWithConfig);
|
||||
}
|
||||
|
||||
//Debug.WriteLine ($"ConfigManager.getConfigProperties found {classesWithConfigProps.Count} classes:");
|
||||
classesWithConfigProps.ToList ().ForEach (x => Debug.WriteLine ($" Class: {x.Key}"));
|
||||
//Logging.Trace ($"ConfigManager.getConfigProperties found {classesWithConfigProps.Count} classes:");
|
||||
classesWithConfigProps.ToList ().ForEach (x => Logging.Trace ($" Class: {x.Key}"));
|
||||
|
||||
foreach (PropertyInfo? p in from c in classesWithConfigProps
|
||||
let props = c.Value
|
||||
@@ -595,9 +608,9 @@ public static class ConfigurationManager
|
||||
StringComparer.InvariantCultureIgnoreCase
|
||||
);
|
||||
|
||||
//Debug.WriteLine ($"ConfigManager.Initialize found {_allConfigProperties.Count} properties:");
|
||||
//Logging.Trace ($"Found {_allConfigProperties.Count} properties:");
|
||||
|
||||
//_allConfigProperties.ToList ().ForEach (x => Debug.WriteLine ($" Property: {x.Key}"));
|
||||
//_allConfigProperties.ToList ().ForEach (x => Logging.Trace ($" Property: {x.Key}"));
|
||||
|
||||
AppSettings = new ();
|
||||
}
|
||||
@@ -608,16 +621,16 @@ public static class ConfigurationManager
|
||||
[RequiresDynamicCode ("AOT")]
|
||||
internal static string ToJson ()
|
||||
{
|
||||
//Debug.WriteLine ("ConfigurationManager.ToJson()");
|
||||
//Logging.Trace ("ConfigurationManager.ToJson()");
|
||||
|
||||
return JsonSerializer.Serialize (Settings!, typeof (SettingsScope), _serializerContext);
|
||||
return JsonSerializer.Serialize (Settings!, typeof (SettingsScope), SerializerContext);
|
||||
}
|
||||
|
||||
[RequiresUnreferencedCode ("AOT")]
|
||||
[RequiresDynamicCode ("AOT")]
|
||||
internal static Stream ToStream ()
|
||||
{
|
||||
string json = JsonSerializer.Serialize (Settings!, typeof (SettingsScope), _serializerContext);
|
||||
string json = JsonSerializer.Serialize (Settings!, typeof (SettingsScope), SerializerContext);
|
||||
|
||||
// turn it into a stream
|
||||
var stream = new MemoryStream ();
|
||||
|
||||
@@ -28,7 +28,7 @@ internal class DictionaryJsonConverter<T> : JsonConverter<Dictionary<string, T>>
|
||||
{
|
||||
string key = reader.GetString ();
|
||||
reader.Read ();
|
||||
var value = JsonSerializer.Deserialize (ref reader, typeof (T), _serializerContext);
|
||||
var value = JsonSerializer.Deserialize (ref reader, typeof (T), SerializerContext);
|
||||
dictionary.Add (key, (T)value);
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ internal class DictionaryJsonConverter<T> : JsonConverter<Dictionary<string, T>>
|
||||
|
||||
//writer.WriteString (item.Key, item.Key);
|
||||
writer.WritePropertyName (item.Key);
|
||||
JsonSerializer.Serialize (writer, item.Value, typeof (T), _serializerContext);
|
||||
JsonSerializer.Serialize (writer, item.Value, typeof (T), SerializerContext);
|
||||
writer.WriteEndObject ();
|
||||
}
|
||||
|
||||
|
||||
@@ -89,11 +89,11 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
|
||||
try
|
||||
{
|
||||
scope! [propertyName].PropertyValue =
|
||||
JsonSerializer.Deserialize (ref reader, propertyType!, _serializerContext);
|
||||
JsonSerializer.Deserialize (ref reader, propertyType!, SerializerContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine ($"scopeT Read: {ex}");
|
||||
// Logging.Trace ($"scopeT Read: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
|
||||
if (property is { })
|
||||
{
|
||||
PropertyInfo prop = scope.GetType ().GetProperty (propertyName!)!;
|
||||
prop.SetValue (scope, JsonSerializer.Deserialize (ref reader, prop.PropertyType, _serializerContext));
|
||||
prop.SetValue (scope, JsonSerializer.Deserialize (ref reader, prop.PropertyType, SerializerContext));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -165,7 +165,7 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
|
||||
{
|
||||
writer.WritePropertyName (ConfigProperty.GetJsonPropertyName (p));
|
||||
object? prop = scope.GetType ().GetProperty (p.Name)?.GetValue (scope);
|
||||
JsonSerializer.Serialize (writer, prop, prop!.GetType (), _serializerContext);
|
||||
JsonSerializer.Serialize (writer, prop, prop!.GetType (), SerializerContext);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, ConfigProperty> p in from p in scope
|
||||
@@ -211,7 +211,7 @@ internal class ScopeJsonConverter<[DynamicallyAccessedMembers (DynamicallyAccess
|
||||
else
|
||||
{
|
||||
object? prop = p.Value.PropertyValue;
|
||||
JsonSerializer.Serialize (writer, prop, prop!.GetType (), _serializerContext);
|
||||
JsonSerializer.Serialize (writer, prop, prop!.GetType (), SerializerContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ public class SettingsScope : Scope<SettingsScope>
|
||||
// Update the existing settings with the new settings.
|
||||
try
|
||||
{
|
||||
Update ((SettingsScope)JsonSerializer.Deserialize (stream, typeof (SettingsScope), _serializerOptions)!);
|
||||
Update ((SettingsScope)JsonSerializer.Deserialize (stream, typeof (SettingsScope), SerializerOptions)!);
|
||||
OnUpdated ();
|
||||
Debug.WriteLine ($"ConfigurationManager: Read configuration from \"{source}\"");
|
||||
Logging.Trace ($"Read from \"{source}\"");
|
||||
if (!Sources.ContainsValue (source))
|
||||
{
|
||||
Sources.Add (location, source);
|
||||
@@ -79,7 +79,7 @@ public class SettingsScope : Scope<SettingsScope>
|
||||
|
||||
if (!File.Exists (realPath))
|
||||
{
|
||||
Debug.WriteLine ($"ConfigurationManager: Configuration file \"{realPath}\" does not exist.");
|
||||
Logging.Warning ($"\"{realPath}\" does not exist.");
|
||||
if (!Sources.ContainsValue (filePath))
|
||||
{
|
||||
Sources.Add (location, filePath);
|
||||
@@ -105,7 +105,7 @@ public class SettingsScope : Scope<SettingsScope>
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
Debug.WriteLine ($"Couldn't open {filePath}. Retrying...: {ioe}");
|
||||
Logging.Warning ($"Couldn't open {filePath}. Retrying...: {ioe}");
|
||||
Task.Delay (100);
|
||||
retryCount++;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class ThemeManager : IDictionary<string, ThemeScope>
|
||||
[RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
|
||||
internal static void GetHardCodedDefaults ()
|
||||
{
|
||||
//Debug.WriteLine ("Themes.GetHardCodedDefaults()");
|
||||
//Logging.Trace ("Themes.GetHardCodedDefaults()");
|
||||
var theme = new ThemeScope ();
|
||||
theme.RetrieveValues ();
|
||||
|
||||
@@ -141,7 +141,7 @@ public class ThemeManager : IDictionary<string, ThemeScope>
|
||||
/// <summary>Called when the selected theme has changed. Fires the <see cref="ThemeChanged"/> event.</summary>
|
||||
internal void OnThemeChanged (string theme)
|
||||
{
|
||||
//Debug.WriteLine ($"Themes.OnThemeChanged({theme}) -> {Theme}");
|
||||
//Logging.Trace ($"Themes.OnThemeChanged({theme}) -> {Theme}");
|
||||
ThemeChanged?.Invoke (this, new ThemeManagerEventArgs (theme));
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public class ThemeManager : IDictionary<string, ThemeScope>
|
||||
[RequiresDynamicCode ("Calls Terminal.Gui.ThemeManager.Themes")]
|
||||
internal static void Reset ()
|
||||
{
|
||||
Debug.WriteLine ("Themes.Reset()");
|
||||
//Logging.Trace ("Themes.Reset()");
|
||||
Colors.Reset ();
|
||||
Themes?.Clear ();
|
||||
SelectedTheme = string.Empty;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Terminal.Gui;
|
||||
/// <remarks>
|
||||
/// Also contains the
|
||||
/// <see cref="Meter"/> instance that should be used for internal metrics
|
||||
/// (iteration timing etc).
|
||||
/// (iteration timing etc.).
|
||||
/// </remarks>
|
||||
public static class Logging
|
||||
{
|
||||
@@ -51,7 +51,71 @@ public static class Logging
|
||||
public static readonly Histogram<int> DrainInputStream = Meter.CreateHistogram<int> ("Drain Input (ms)");
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message including the
|
||||
/// Logs an error message including the class and method name.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="caller"></param>
|
||||
/// <param name="filePath"></param>
|
||||
public static void Error (
|
||||
string message,
|
||||
[CallerMemberName] string caller = "",
|
||||
[CallerFilePath] string filePath = ""
|
||||
)
|
||||
{
|
||||
string className = Path.GetFileNameWithoutExtension (filePath);
|
||||
Logger.LogError ($"[{className}] [{caller}] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a critical message including the class and method name.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="caller"></param>
|
||||
/// <param name="filePath"></param>
|
||||
public static void Critical (
|
||||
string message,
|
||||
[CallerMemberName] string caller = "",
|
||||
[CallerFilePath] string filePath = ""
|
||||
)
|
||||
{
|
||||
string className = Path.GetFileNameWithoutExtension (filePath);
|
||||
Logger.LogCritical ($"[{className}] [{caller}] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a debug message including the class and method name.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="caller"></param>
|
||||
/// <param name="filePath"></param>
|
||||
public static void Debug (
|
||||
string message,
|
||||
[CallerMemberName] string caller = "",
|
||||
[CallerFilePath] string filePath = ""
|
||||
)
|
||||
{
|
||||
string className = Path.GetFileNameWithoutExtension (filePath);
|
||||
Logger.LogDebug ($"[{className}] [{caller}] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an informational message including the class and method name.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="caller"></param>
|
||||
/// <param name="filePath"></param>
|
||||
public static void Information (
|
||||
string message,
|
||||
[CallerMemberName] string caller = "",
|
||||
[CallerFilePath] string filePath = ""
|
||||
)
|
||||
{
|
||||
string className = Path.GetFileNameWithoutExtension (filePath);
|
||||
Logger.LogInformation ($"[{className}] [{caller}] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message including the class and method name.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="caller"></param>
|
||||
@@ -65,4 +129,20 @@ public static class Logging
|
||||
string className = Path.GetFileNameWithoutExtension (filePath);
|
||||
Logger.LogTrace ($"[{className}] [{caller}] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message including the class and method name.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="caller"></param>
|
||||
/// <param name="filePath"></param>
|
||||
public static void Warning (
|
||||
string message,
|
||||
[CallerMemberName] string caller = "",
|
||||
[CallerFilePath] string filePath = ""
|
||||
)
|
||||
{
|
||||
string className = Path.GetFileNameWithoutExtension (filePath);
|
||||
Logger.LogWarning ($"[{className}] [{caller}] {message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"><ElementKinds><Kind Name="LOCAL_CONSTANT" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c873eafb_002Dd57f_002D481d_002D8c93_002D77f6863c2f88/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=A92AB40A0394A342A0BE0D83FEEA5DBF/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=A92AB40A0394A342A0BE0D83FEEA5DBF/RelativePath/@EntryValue">..\Terminal.sln.ToDo.DotSettings</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=FileA92AB40A0394A342A0BE0D83FEEA5DBF/@KeyIndexDefined">True</s:Boolean>
|
||||
|
||||
@@ -276,7 +276,7 @@ public class Scenario : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine ($@" Failed to Quit with {Application.QuitKey} after {BenchmarkTimeout}ms and {BenchmarkResults.IterationCount} iterations. Force quit.");
|
||||
Logging.Trace ($@" Failed to Quit with {Application.QuitKey} after {BenchmarkTimeout}ms and {BenchmarkResults.IterationCount} iterations. Force quit.");
|
||||
|
||||
Application.RequestStop ();
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using Terminal.Gui;
|
||||
using static Terminal.Gui.ConfigurationManager;
|
||||
using Command = Terminal.Gui.Command;
|
||||
@@ -31,7 +31,7 @@ using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironm
|
||||
namespace UICatalog;
|
||||
|
||||
/// <summary>
|
||||
/// UI Catalog is a comprehensive sample library for Terminal.Gui. It provides a simple UI for adding to the
|
||||
/// UI Catalog is a comprehensive sample library and test app for Terminal.Gui. It provides a simple UI for adding to the
|
||||
/// catalog of scenarios.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -60,16 +60,24 @@ public class UICatalogApp
|
||||
private static int _cachedScenarioIndex;
|
||||
private static string? _cachedTheme = string.Empty;
|
||||
private static ObservableCollection<string>? _categories;
|
||||
|
||||
[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
||||
private static readonly FileSystemWatcher _currentDirWatcher = new ();
|
||||
|
||||
private static ViewDiagnosticFlags _diagnosticFlags;
|
||||
private static string _forceDriver = string.Empty;
|
||||
|
||||
[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
||||
private static readonly FileSystemWatcher _homeDirWatcher = new ();
|
||||
|
||||
private static bool _isFirstRunning = true;
|
||||
private static Options _options;
|
||||
private static ObservableCollection<Scenario>? _scenarios;
|
||||
|
||||
private const string LOGFILE_LOCATION = "./logs";
|
||||
private static string _logFilePath = string.Empty;
|
||||
private static readonly LoggingLevelSwitch _logLevelSwitch = new ();
|
||||
|
||||
// If set, holds the scenario the user selected
|
||||
private static Scenario? _selectedScenario;
|
||||
private static MenuBarItem? _themeMenuBarItem;
|
||||
@@ -81,7 +89,7 @@ public class UICatalogApp
|
||||
public static bool ShowStatusBar { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message displayed in the About Box. `public` so it can be used from Unit tests.
|
||||
/// Gets the message displayed in the About Box. `public` so it can be used from Unit tests.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetAboutBoxMessage ()
|
||||
@@ -89,10 +97,11 @@ public class UICatalogApp
|
||||
// NOTE: Do not use multiline verbatim strings here.
|
||||
// WSL gets all confused.
|
||||
StringBuilder msg = new ();
|
||||
msg.AppendLine ("UI Catalog: A comprehensive sample library for");
|
||||
msg.AppendLine ("UI Catalog: A comprehensive sample library and test app for");
|
||||
msg.AppendLine ();
|
||||
|
||||
msg.AppendLine ("""
|
||||
msg.AppendLine (
|
||||
"""
|
||||
_______ _ _ _____ _
|
||||
|__ __| (_) | | / ____| (_)
|
||||
| | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _
|
||||
@@ -123,8 +132,6 @@ public class UICatalogApp
|
||||
|
||||
private static int Main (string [] args)
|
||||
{
|
||||
Logging.Logger = CreateLogger ();
|
||||
|
||||
Console.OutputEncoding = Encoding.Default;
|
||||
|
||||
if (Debugger.IsAttached)
|
||||
@@ -136,7 +143,7 @@ public class UICatalogApp
|
||||
_categories = Scenario.GetAllCategories ();
|
||||
|
||||
// Process command line args
|
||||
// "UICatalog [--driver <driver>] [--benchmark] [scenario name]"
|
||||
|
||||
// If no driver is provided, the default driver is used.
|
||||
Option<string> driverOption = new Option<string> ("--driver", "The IConsoleDriver to use.").FromAmong (
|
||||
Application.GetDriverTypes ()
|
||||
@@ -146,21 +153,34 @@ public class UICatalogApp
|
||||
driverOption.AddAlias ("-d");
|
||||
driverOption.AddAlias ("--d");
|
||||
|
||||
Option<bool> benchmarkFlag = new Option<bool> ("--benchmark", "Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked.");
|
||||
Option<bool> benchmarkFlag = new ("--benchmark", "Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked.");
|
||||
benchmarkFlag.AddAlias ("-b");
|
||||
benchmarkFlag.AddAlias ("--b");
|
||||
|
||||
Option<uint> benchmarkTimeout = new Option<uint> ("--timeout", getDefaultValue: () => Scenario.BenchmarkTimeout, $"The maximum time in milliseconds to run a benchmark for. Default is {Scenario.BenchmarkTimeout}ms.");
|
||||
Option<uint> benchmarkTimeout = new (
|
||||
"--timeout",
|
||||
() => Scenario.BenchmarkTimeout,
|
||||
$"The maximum time in milliseconds to run a benchmark for. Default is {Scenario.BenchmarkTimeout}ms.");
|
||||
benchmarkTimeout.AddAlias ("-t");
|
||||
benchmarkTimeout.AddAlias ("--t");
|
||||
|
||||
Option<string> resultsFile = new Option<string> ("--file", "The file to save benchmark results to. If not specified, the results will be displayed in a TableView.");
|
||||
Option<string> resultsFile = new ("--file", "The file to save benchmark results to. If not specified, the results will be displayed in a TableView.");
|
||||
resultsFile.AddAlias ("-f");
|
||||
resultsFile.AddAlias ("--f");
|
||||
|
||||
// what's the app name?
|
||||
_logFilePath = $"{LOGFILE_LOCATION}/{Assembly.GetExecutingAssembly ().GetName ().Name}.log";
|
||||
Option<string> debugLogLevel = new Option<string> ("--debug-log-level", $"The level to use for logging (debug console and {_logFilePath})").FromAmong (
|
||||
Enum.GetNames<LogEventLevel> ()
|
||||
);
|
||||
debugLogLevel.SetDefaultValue("Warning");
|
||||
debugLogLevel.AddAlias ("-dl");
|
||||
debugLogLevel.AddAlias ("--dl");
|
||||
|
||||
Argument<string> scenarioArgument = new Argument<string> (
|
||||
name: "scenario",
|
||||
description: "The name of the Scenario to run. If not provided, the UI Catalog UI will be shown.",
|
||||
"scenario",
|
||||
description:
|
||||
"The name of the Scenario to run. If not provided, the UI Catalog UI will be shown.",
|
||||
getDefaultValue: () => "none"
|
||||
).FromAmong (
|
||||
_scenarios.Select (s => s.GetName ())
|
||||
@@ -168,10 +188,9 @@ public class UICatalogApp
|
||||
.ToArray ()
|
||||
);
|
||||
|
||||
|
||||
var rootCommand = new RootCommand ("A comprehensive sample library for Terminal.Gui")
|
||||
var rootCommand = new RootCommand ("A comprehensive sample library and test app for Terminal.Gui")
|
||||
{
|
||||
scenarioArgument, benchmarkFlag, benchmarkTimeout, resultsFile, driverOption,
|
||||
scenarioArgument, debugLogLevel, benchmarkFlag, benchmarkTimeout, resultsFile, driverOption
|
||||
};
|
||||
|
||||
rootCommand.SetHandler (
|
||||
@@ -184,6 +203,7 @@ public class UICatalogApp
|
||||
Benchmark = context.ParseResult.GetValueForOption (benchmarkFlag),
|
||||
BenchmarkTimeout = context.ParseResult.GetValueForOption (benchmarkTimeout),
|
||||
ResultsFile = context.ParseResult.GetValueForOption (resultsFile) ?? string.Empty,
|
||||
DebugLogLevel = context.ParseResult.GetValueForOption (debugLogLevel) ?? "Warning"
|
||||
/* etc. */
|
||||
};
|
||||
|
||||
@@ -192,10 +212,11 @@ public class UICatalogApp
|
||||
}
|
||||
);
|
||||
|
||||
bool helpShown = false;
|
||||
var parser = new CommandLineBuilder (rootCommand)
|
||||
.UseHelp (ctx => helpShown = true)
|
||||
.Build ();
|
||||
var helpShown = false;
|
||||
|
||||
Parser parser = new CommandLineBuilder (rootCommand)
|
||||
.UseHelp (ctx => helpShown = true)
|
||||
.Build ();
|
||||
|
||||
parser.Invoke (args);
|
||||
|
||||
@@ -206,6 +227,8 @@ public class UICatalogApp
|
||||
|
||||
Scenario.BenchmarkTimeout = _options.BenchmarkTimeout;
|
||||
|
||||
Logging.Logger = CreateLogger ();
|
||||
|
||||
UICatalogMain (_options);
|
||||
|
||||
return 0;
|
||||
@@ -215,19 +238,23 @@ public class UICatalogApp
|
||||
{
|
||||
// Configure Serilog to write logs to a file
|
||||
Log.Logger = new LoggerConfiguration ()
|
||||
.MinimumLevel.Verbose () // Verbose includes Trace and Debug
|
||||
.MinimumLevel.ControlledBy (_logLevelSwitch)
|
||||
.Enrich.FromLogContext () // Enables dynamic enrichment
|
||||
.WriteTo.File ("logs/logfile.txt", rollingInterval: RollingInterval.Day,
|
||||
.WriteTo.Debug ()
|
||||
.WriteTo.File (
|
||||
_logFilePath,
|
||||
rollingInterval: RollingInterval.Day,
|
||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger ();
|
||||
|
||||
// Create a logger factory compatible with Microsoft.Extensions.Logging
|
||||
using var loggerFactory = LoggerFactory.Create (builder =>
|
||||
{
|
||||
builder
|
||||
.AddSerilog (dispose: true) // Integrate Serilog with ILogger
|
||||
.SetMinimumLevel (LogLevel.Trace); // Set minimum log level
|
||||
});
|
||||
using ILoggerFactory loggerFactory = LoggerFactory.Create (
|
||||
builder =>
|
||||
{
|
||||
builder
|
||||
.AddSerilog (dispose: true) // Integrate Serilog with ILogger
|
||||
.SetMinimumLevel (LogLevel.Trace); // Set minimum log level
|
||||
});
|
||||
|
||||
// Get an ILogger instance
|
||||
return loggerFactory.CreateLogger ("Global Logger");
|
||||
@@ -354,7 +381,6 @@ public class UICatalogApp
|
||||
_homeDirWatcher.Created -= ConfigFileChanged;
|
||||
}
|
||||
|
||||
|
||||
private static void UICatalogMain (Options options)
|
||||
{
|
||||
StartConfigFileWatcher ();
|
||||
@@ -363,7 +389,6 @@ public class UICatalogApp
|
||||
// regardless of what's in a config file.
|
||||
Application.ForceDriver = _forceDriver = options.Driver;
|
||||
|
||||
|
||||
// If a Scenario name has been provided on the commandline
|
||||
// run it and exit when done.
|
||||
if (options.Scenario != "none")
|
||||
@@ -378,13 +403,17 @@ public class UICatalogApp
|
||||
)!);
|
||||
_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ())!;
|
||||
|
||||
var results = RunScenario (_selectedScenario, options.Benchmark);
|
||||
BenchmarkResults? results = RunScenario (_selectedScenario, options.Benchmark);
|
||||
|
||||
if (results is { })
|
||||
{
|
||||
Console.WriteLine (JsonSerializer.Serialize (results, new JsonSerializerOptions ()
|
||||
{
|
||||
WriteIndented = true
|
||||
}));
|
||||
Console.WriteLine (
|
||||
JsonSerializer.Serialize (
|
||||
results,
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
}));
|
||||
}
|
||||
|
||||
VerifyObjectsWereDisposed ();
|
||||
@@ -408,6 +437,7 @@ public class UICatalogApp
|
||||
scenario.TopLevelColorScheme = _topLevelColorScheme;
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
|
||||
// Measure how long it takes for the app to shut down
|
||||
var sw = new Stopwatch ();
|
||||
string scenarioName = scenario.GetName ();
|
||||
@@ -435,7 +465,7 @@ public class UICatalogApp
|
||||
else
|
||||
{
|
||||
sw.Stop ();
|
||||
Debug.WriteLine ($"Shutdown of {scenarioName} Scenario took {sw.ElapsedMilliseconds}ms");
|
||||
Logging.Trace ($"Shutdown of {scenarioName} Scenario took {sw.ElapsedMilliseconds}ms");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -443,8 +473,6 @@ public class UICatalogApp
|
||||
|
||||
StopConfigFileWatcher ();
|
||||
VerifyObjectsWereDisposed ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private static BenchmarkResults? RunScenario (Scenario scenario, bool benchmark)
|
||||
@@ -473,20 +501,19 @@ public class UICatalogApp
|
||||
|
||||
scenario.Dispose ();
|
||||
|
||||
|
||||
// TODO: Throw if shutdown was not called already
|
||||
Application.Shutdown ();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
private static void BenchmarkAllScenarios ()
|
||||
{
|
||||
List<BenchmarkResults> resultsList = new List<BenchmarkResults> ();
|
||||
List<BenchmarkResults> resultsList = new ();
|
||||
|
||||
int maxScenarios = 5;
|
||||
foreach (var s in _scenarios!)
|
||||
var maxScenarios = 5;
|
||||
|
||||
foreach (Scenario s in _scenarios!)
|
||||
{
|
||||
resultsList.Add (RunScenario (s, true)!);
|
||||
maxScenarios--;
|
||||
@@ -501,24 +528,25 @@ public class UICatalogApp
|
||||
{
|
||||
if (!string.IsNullOrEmpty (_options.ResultsFile))
|
||||
{
|
||||
var output = JsonSerializer.Serialize (
|
||||
resultsList,
|
||||
new JsonSerializerOptions ()
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
string output = JsonSerializer.Serialize (
|
||||
resultsList,
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
|
||||
using var file = File.CreateText (_options.ResultsFile);
|
||||
using StreamWriter file = File.CreateText (_options.ResultsFile);
|
||||
file.Write (output);
|
||||
file.Close ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Application.Init ();
|
||||
|
||||
var benchmarkWindow = new Window ()
|
||||
var benchmarkWindow = new Window
|
||||
{
|
||||
Title = "Benchmark Results",
|
||||
Title = "Benchmark Results"
|
||||
};
|
||||
|
||||
if (benchmarkWindow.Border is { })
|
||||
@@ -529,7 +557,7 @@ public class UICatalogApp
|
||||
TableView resultsTableView = new ()
|
||||
{
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill (),
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
|
||||
// TableView provides many options for table headers. For simplicity we turn all
|
||||
@@ -544,17 +572,17 @@ public class UICatalogApp
|
||||
resultsTableView.Style.ShowVerticalHeaderLines = true;
|
||||
|
||||
/* By default TableView lays out columns at render time and only
|
||||
* measures y rows of data at a time. Where y is the height of the
|
||||
* console. This is for the following reasons:
|
||||
*
|
||||
* - Performance, when tables have a large amount of data
|
||||
* - Defensive, prevents a single wide cell value pushing other
|
||||
* columns off screen (requiring horizontal scrolling
|
||||
*
|
||||
* In the case of UICatalog here, such an approach is overkill so
|
||||
* we just measure all the data ourselves and set the appropriate
|
||||
* max widths as ColumnStyles
|
||||
*/
|
||||
* measures y rows of data at a time. Where y is the height of the
|
||||
* console. This is for the following reasons:
|
||||
*
|
||||
* - Performance, when tables have a large amount of data
|
||||
* - Defensive, prevents a single wide cell value pushing other
|
||||
* columns off screen (requiring horizontal scrolling
|
||||
*
|
||||
* In the case of UICatalog here, such an approach is overkill so
|
||||
* we just measure all the data ourselves and set the appropriate
|
||||
* max widths as ColumnStyles
|
||||
*/
|
||||
//int longestName = _scenarios!.Max (s => s.GetName ().Length);
|
||||
|
||||
//resultsTableView.Style.ColumnStyles.Add (
|
||||
@@ -586,7 +614,7 @@ public class UICatalogApp
|
||||
dt.Columns.Add (new DataColumn ("Updated", typeof (int)));
|
||||
dt.Columns.Add (new DataColumn ("Iterations", typeof (int)));
|
||||
|
||||
foreach (var r in resultsList)
|
||||
foreach (BenchmarkResults r in resultsList)
|
||||
{
|
||||
dt.Rows.Add (
|
||||
r.Scenario,
|
||||
@@ -603,24 +631,25 @@ public class UICatalogApp
|
||||
BenchmarkResults totalRow = new ()
|
||||
{
|
||||
Scenario = "TOTAL",
|
||||
Duration = new TimeSpan (resultsList.Sum (r => r.Duration.Ticks)),
|
||||
Duration = new (resultsList.Sum (r => r.Duration.Ticks)),
|
||||
RefreshedCount = resultsList.Sum (r => r.RefreshedCount),
|
||||
LaidOutCount = resultsList.Sum (r => r.LaidOutCount),
|
||||
ClearedContentCount = resultsList.Sum (r => r.ClearedContentCount),
|
||||
DrawCompleteCount = resultsList.Sum (r => r.DrawCompleteCount),
|
||||
UpdatedCount = resultsList.Sum (r => r.UpdatedCount),
|
||||
IterationCount = resultsList.Sum (r => r.IterationCount),
|
||||
IterationCount = resultsList.Sum (r => r.IterationCount)
|
||||
};
|
||||
|
||||
dt.Rows.Add (
|
||||
totalRow.Scenario,
|
||||
totalRow.Duration,
|
||||
totalRow.RefreshedCount,
|
||||
totalRow.LaidOutCount,
|
||||
totalRow.ClearedContentCount,
|
||||
totalRow.DrawCompleteCount,
|
||||
totalRow.UpdatedCount,
|
||||
totalRow.IterationCount
|
||||
);
|
||||
totalRow.Scenario,
|
||||
totalRow.Duration,
|
||||
totalRow.RefreshedCount,
|
||||
totalRow.LaidOutCount,
|
||||
totalRow.ClearedContentCount,
|
||||
totalRow.DrawCompleteCount,
|
||||
totalRow.UpdatedCount,
|
||||
totalRow.IterationCount
|
||||
);
|
||||
|
||||
dt.DefaultView.Sort = "Duration";
|
||||
DataTable sortedCopy = dt.DefaultView.ToTable ();
|
||||
@@ -711,6 +740,7 @@ public class UICatalogApp
|
||||
),
|
||||
_themeMenuBarItem,
|
||||
new ("Diag_nostics", CreateDiagnosticMenuItems ()),
|
||||
new ("_Logging", CreateLoggingMenuItems ()),
|
||||
new (
|
||||
"_Help",
|
||||
new MenuItem []
|
||||
@@ -735,8 +765,8 @@ public class UICatalogApp
|
||||
"_About...",
|
||||
"About UI Catalog",
|
||||
() => MessageBox.Query (
|
||||
title: "",
|
||||
message: GetAboutBoxMessage (),
|
||||
"",
|
||||
GetAboutBoxMessage (),
|
||||
wrapMessage: false,
|
||||
buttons: "_Ok"
|
||||
),
|
||||
@@ -760,20 +790,21 @@ public class UICatalogApp
|
||||
ShVersion = new ()
|
||||
{
|
||||
Title = "Version Info",
|
||||
CanFocus = false,
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
var statusBarShortcut = new Shortcut
|
||||
{
|
||||
Key = Key.F10,
|
||||
Title = "Show/Hide Status Bar",
|
||||
CanFocus = false,
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
statusBarShortcut.Accepting += (sender, args) =>
|
||||
{
|
||||
_statusBar.Visible = !_statusBar.Visible;
|
||||
args.Cancel = true;
|
||||
};
|
||||
{
|
||||
_statusBar.Visible = !_statusBar.Visible;
|
||||
args.Cancel = true;
|
||||
};
|
||||
|
||||
ShForce16Colors = new ()
|
||||
{
|
||||
@@ -790,25 +821,25 @@ public class UICatalogApp
|
||||
};
|
||||
|
||||
((CheckBox)ShForce16Colors.CommandView).CheckedStateChanging += (sender, args) =>
|
||||
{
|
||||
Application.Force16Colors = args.NewValue == CheckState.Checked;
|
||||
MiForce16Colors!.Checked = Application.Force16Colors;
|
||||
Application.LayoutAndDraw ();
|
||||
};
|
||||
{
|
||||
Application.Force16Colors = args.NewValue == CheckState.Checked;
|
||||
MiForce16Colors!.Checked = Application.Force16Colors;
|
||||
Application.LayoutAndDraw ();
|
||||
};
|
||||
|
||||
_statusBar.Add (
|
||||
new Shortcut
|
||||
{
|
||||
CanFocus = false,
|
||||
Title = "Quit",
|
||||
Key = Application.QuitKey
|
||||
},
|
||||
statusBarShortcut,
|
||||
ShForce16Colors,
|
||||
new Shortcut
|
||||
{
|
||||
CanFocus = false,
|
||||
Title = "Quit",
|
||||
Key = Application.QuitKey
|
||||
},
|
||||
statusBarShortcut,
|
||||
ShForce16Colors,
|
||||
|
||||
//ShDiagnostics,
|
||||
ShVersion
|
||||
);
|
||||
//ShDiagnostics,
|
||||
ShVersion
|
||||
);
|
||||
|
||||
// Create the Category list view. This list never changes.
|
||||
CategoryList = new ()
|
||||
@@ -816,13 +847,17 @@ public class UICatalogApp
|
||||
X = 0,
|
||||
Y = Pos.Bottom (menuBar),
|
||||
Width = Dim.Auto (),
|
||||
Height = Dim.Fill (Dim.Func (() =>
|
||||
Height = Dim.Fill (
|
||||
Dim.Func (
|
||||
() =>
|
||||
{
|
||||
if (_statusBar.NeedsLayout)
|
||||
{
|
||||
throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
|
||||
throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
|
||||
|
||||
//_statusBar.Layout ();
|
||||
}
|
||||
|
||||
return _statusBar.Frame.Height;
|
||||
})),
|
||||
AllowsMarking = false,
|
||||
@@ -846,21 +881,27 @@ public class UICatalogApp
|
||||
X = Pos.Right (CategoryList) - 1,
|
||||
Y = Pos.Bottom (menuBar),
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill (Dim.Func (() =>
|
||||
Height = Dim.Fill (
|
||||
Dim.Func (
|
||||
() =>
|
||||
{
|
||||
if (_statusBar.NeedsLayout)
|
||||
{
|
||||
throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
|
||||
|
||||
//_statusBar.Layout ();
|
||||
}
|
||||
|
||||
return _statusBar.Frame.Height;
|
||||
})),
|
||||
|
||||
//AllowsMarking = false,
|
||||
CanFocus = true,
|
||||
Title = "_Scenarios",
|
||||
BorderStyle = CategoryList.BorderStyle,
|
||||
SuperViewRendersLineCanvas = true
|
||||
};
|
||||
|
||||
//ScenarioList.VerticalScrollBar.AutoHide = false;
|
||||
//ScenarioList.HorizontalScrollBar.AutoHide = false;
|
||||
|
||||
@@ -1095,12 +1136,18 @@ public class UICatalogApp
|
||||
|
||||
if (item.Title == t && item.Checked == false)
|
||||
{
|
||||
_diagnosticFlags &= ~(ViewDiagnosticFlags.Thickness | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover | ViewDiagnosticFlags.DrawIndicator);
|
||||
_diagnosticFlags &= ~(ViewDiagnosticFlags.Thickness
|
||||
| ViewDiagnosticFlags.Ruler
|
||||
| ViewDiagnosticFlags.Hover
|
||||
| ViewDiagnosticFlags.DrawIndicator);
|
||||
item.Checked = true;
|
||||
}
|
||||
else if (item.Title == t && item.Checked == true)
|
||||
{
|
||||
_diagnosticFlags |= ViewDiagnosticFlags.Thickness | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover | ViewDiagnosticFlags.DrawIndicator;
|
||||
_diagnosticFlags |= ViewDiagnosticFlags.Thickness
|
||||
| ViewDiagnosticFlags.Ruler
|
||||
| ViewDiagnosticFlags.Hover
|
||||
| ViewDiagnosticFlags.DrawIndicator;
|
||||
item.Checked = false;
|
||||
}
|
||||
else
|
||||
@@ -1235,6 +1282,65 @@ public class UICatalogApp
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
private List<MenuItem []> CreateLoggingMenuItems ()
|
||||
{
|
||||
List<MenuItem []> menuItems = new ()
|
||||
{
|
||||
CreateLoggingFlagsMenuItems ()
|
||||
};
|
||||
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
[SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
||||
private MenuItem [] CreateLoggingFlagsMenuItems ()
|
||||
{
|
||||
string [] logLevelMenuStrings = Enum.GetNames<LogEventLevel> ().Select (n => n = "_" + n).ToArray ();
|
||||
LogEventLevel [] logLevels = Enum.GetValues<LogEventLevel> ();
|
||||
|
||||
List<MenuItem?> menuItems = new ();
|
||||
|
||||
foreach (LogEventLevel logLevel in logLevels)
|
||||
{
|
||||
var item = new MenuItem
|
||||
{
|
||||
Title = logLevelMenuStrings [(int)logLevel]
|
||||
};
|
||||
item.CheckType |= MenuItemCheckStyle.Checked;
|
||||
item.Checked = Enum.Parse<LogEventLevel> (_options.DebugLogLevel) == logLevel;
|
||||
|
||||
item.Action += () =>
|
||||
{
|
||||
foreach (MenuItem? menuItem in menuItems.Where (mi => mi is { } && logLevelMenuStrings.Contains (mi.Title)))
|
||||
{
|
||||
menuItem!.Checked = false;
|
||||
}
|
||||
|
||||
if (item.Title == logLevelMenuStrings [(int)logLevel] && item.Checked == false)
|
||||
{
|
||||
_options.DebugLogLevel = Enum.GetName (logLevel)!;
|
||||
_logLevelSwitch.MinimumLevel = Enum.Parse<LogEventLevel> (_options.DebugLogLevel);
|
||||
item.Checked = true;
|
||||
}
|
||||
|
||||
Diagnostics = _diagnosticFlags;
|
||||
};
|
||||
menuItems.Add (item);
|
||||
}
|
||||
|
||||
// add a separator
|
||||
menuItems.Add (null!);
|
||||
|
||||
menuItems.Add (
|
||||
new ()
|
||||
{
|
||||
Title = $"Log file: {_logFilePath}"
|
||||
//CanExecute = () => false
|
||||
});
|
||||
|
||||
return menuItems.ToArray ();
|
||||
}
|
||||
|
||||
// TODO: This should be an ConfigurationManager setting
|
||||
private MenuItem [] CreateDisabledEnabledMenuBorder ()
|
||||
{
|
||||
@@ -1250,8 +1356,8 @@ public class UICatalogApp
|
||||
MiIsMenuBorderDisabled.Checked = (bool)!MiIsMenuBorderDisabled.Checked!;
|
||||
|
||||
MenuBar!.MenusBorderStyle = !(bool)MiIsMenuBorderDisabled.Checked
|
||||
? LineStyle.Single
|
||||
: LineStyle.None;
|
||||
? LineStyle.Single
|
||||
: LineStyle.None;
|
||||
};
|
||||
menuItems.Add (MiIsMenuBorderDisabled);
|
||||
|
||||
@@ -1284,9 +1390,9 @@ public class UICatalogApp
|
||||
MiUseSubMenusSingleFrame = new () { Title = "Enable _Sub-Menus Single Frame" };
|
||||
|
||||
MiUseSubMenusSingleFrame.ShortcutKey = KeyCode.CtrlMask
|
||||
| KeyCode.AltMask
|
||||
| (KeyCode)MiUseSubMenusSingleFrame!.Title!.Substring (8, 1) [
|
||||
0];
|
||||
| KeyCode.AltMask
|
||||
| (KeyCode)MiUseSubMenusSingleFrame!.Title!.Substring (8, 1) [
|
||||
0];
|
||||
MiUseSubMenusSingleFrame.CheckType |= MenuItemCheckStyle.Checked;
|
||||
|
||||
MiUseSubMenusSingleFrame.Action += () =>
|
||||
@@ -1367,10 +1473,7 @@ public class UICatalogApp
|
||||
|
||||
if (_statusBar is { })
|
||||
{
|
||||
_statusBar.VisibleChanged += (s, e) =>
|
||||
{
|
||||
ShowStatusBar = _statusBar.Visible;
|
||||
};
|
||||
_statusBar.VisibleChanged += (s, e) => { ShowStatusBar = _statusBar.Visible; };
|
||||
}
|
||||
|
||||
Loaded -= LoadedHandler;
|
||||
@@ -1423,6 +1526,8 @@ public class UICatalogApp
|
||||
public bool Benchmark;
|
||||
|
||||
public string ResultsFile;
|
||||
|
||||
public string DebugLogLevel;
|
||||
/* etc. */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21,2)" />
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="[3.1.5,4)" />
|
||||
<PackageReference Include="CsvHelper" Version="[33.0.1,34)" />
|
||||
|
||||
@@ -45,8 +45,8 @@ public class KeyJsonConverterTests
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize ((Key)key, ConfigurationManager._serializerOptions);
|
||||
var deserializedKey = JsonSerializer.Deserialize<Key> (json, ConfigurationManager._serializerOptions);
|
||||
string json = JsonSerializer.Serialize ((Key)key, ConfigurationManager.SerializerOptions);
|
||||
var deserializedKey = JsonSerializer.Deserialize<Key> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expectedStringTo, deserializedKey.ToString ());
|
||||
@@ -60,7 +60,7 @@ public class KeyJsonConverterTests
|
||||
|
||||
// Act
|
||||
string json = "\"Ctrl+Q\"";
|
||||
Key deserializedKey = JsonSerializer.Deserialize<Key> (json, ConfigurationManager._serializerOptions);
|
||||
Key deserializedKey = JsonSerializer.Deserialize<Key> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (key, deserializedKey);
|
||||
@@ -70,7 +70,7 @@ public class KeyJsonConverterTests
|
||||
public void Separator_Property_Serializes_As_Glyph ()
|
||||
{
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager._serializerOptions);
|
||||
string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ($"\"{Key.Separator}\"", json);
|
||||
@@ -85,7 +85,7 @@ public class KeyJsonConverterTests
|
||||
{
|
||||
// Act
|
||||
Key.Separator = (Rune)'*';
|
||||
string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager._serializerOptions);
|
||||
string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ("\"*\"", json);
|
||||
@@ -124,7 +124,7 @@ public class KeyJsonConverterTests
|
||||
// Act
|
||||
Key.Separator = (Rune)separator;
|
||||
|
||||
Key deserializedKey = JsonSerializer.Deserialize<Key> (json, ConfigurationManager._serializerOptions);
|
||||
Key deserializedKey = JsonSerializer.Deserialize<Key> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
Key expectedKey = new Key ((KeyCode)keyChar).WithCtrl.WithAlt;
|
||||
// Assert
|
||||
|
||||
@@ -31,13 +31,13 @@ public class RuneJsonConverterTests
|
||||
public void RoundTripConversion_Negative (string rune)
|
||||
{
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize (rune, ConfigurationManager._serializerOptions);
|
||||
string json = JsonSerializer.Serialize (rune, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Throws<JsonException> (
|
||||
() => JsonSerializer.Deserialize<Rune> (
|
||||
json,
|
||||
ConfigurationManager._serializerOptions
|
||||
ConfigurationManager.SerializerOptions
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -61,8 +61,8 @@ public class RuneJsonConverterTests
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize (rune, ConfigurationManager._serializerOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager._serializerOptions);
|
||||
string json = JsonSerializer.Serialize (rune, ConfigurationManager.SerializerOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expected, deserialized.ToString ());
|
||||
@@ -74,7 +74,7 @@ public class RuneJsonConverterTests
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize ((Rune)'a', ConfigurationManager._serializerOptions);
|
||||
string json = JsonSerializer.Serialize ((Rune)'a', ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ("\"a\"", json);
|
||||
@@ -86,7 +86,7 @@ public class RuneJsonConverterTests
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize ((Rune)0x01, ConfigurationManager._serializerOptions);
|
||||
string json = JsonSerializer.Serialize ((Rune)0x01, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ("\"\\u0001\"", json);
|
||||
@@ -99,7 +99,7 @@ public class RuneJsonConverterTests
|
||||
var json = "\"a\"";
|
||||
|
||||
// Act
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager._serializerOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ("a", deserialized.ToString ());
|
||||
@@ -112,7 +112,7 @@ public class RuneJsonConverterTests
|
||||
var json = "\"\\u0061\"";
|
||||
|
||||
// Act
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager._serializerOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ("a", deserialized.ToString ());
|
||||
@@ -125,7 +125,7 @@ public class RuneJsonConverterTests
|
||||
var json = "\"U+0061\"";
|
||||
|
||||
// Act
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager._serializerOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ("a", deserialized.ToString ());
|
||||
@@ -138,7 +138,7 @@ public class RuneJsonConverterTests
|
||||
var json = "97";
|
||||
|
||||
// Act
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager._serializerOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<Rune> (json, ConfigurationManager.SerializerOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal ("a", deserialized.ToString ());
|
||||
|
||||
@@ -475,19 +475,19 @@ public class MessageBoxTests
|
||||
|
||||
string expectedText = """
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ ╔══════════════════════════════════════════════════════════╗ │
|
||||
│ ║ UI Catalog: A comprehensive sample library for ║ │
|
||||
│ ║ ║ │
|
||||
│ ║ _______ _ _ _____ _ ║ │
|
||||
│ ║|__ __| (_) | | / ____| (_)║ │
|
||||
│ ║ | | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _ ║ │
|
||||
│ ║ | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |║ │
|
||||
│ ║ | | __/ | | | | | | | | | | | (_| | || |__| | |_| | |║ │
|
||||
│ ║ |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|║ │
|
||||
│ ║ ║ │
|
||||
│ ║ v2 - Pre-Alpha ║ │
|
||||
│ ║ ⟦► Ok ◄⟧║ │
|
||||
│ ╚══════════════════════════════════════════════════════════╝ │
|
||||
│ ╔═══════════════════════════════════════════════════════════╗ │
|
||||
│ ║UI Catalog: A comprehensive sample library and test app for║ │
|
||||
│ ║ ║ │
|
||||
│ ║ _______ _ _ _____ _ ║ │
|
||||
│ ║|__ __| (_) | | / ____| (_) ║ │
|
||||
│ ║ | | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _ ║ │
|
||||
│ ║ | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | | ║ │
|
||||
│ ║ | | __/ | | | | | | | | | | | (_| | || |__| | |_| | | ║ │
|
||||
│ ║ |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_| ║ │
|
||||
│ ║ ║ │
|
||||
│ ║ v2 - Pre-Alpha ║ │
|
||||
│ ║ ⟦► Ok ◄⟧║ │
|
||||
│ ╚═══════════════════════════════════════════════════════════╝ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
""";
|
||||
|
||||
|
||||
@@ -6188,7 +6188,7 @@ ek")]
|
||||
};
|
||||
|
||||
Size tfSize = tf.FormatAndGetSize ();
|
||||
Assert.Equal (new (58, 13), tfSize);
|
||||
Assert.Equal (new (59, 13), tfSize);
|
||||
|
||||
((FakeDriver)Application.Driver).SetBufferSize (tfSize.Width, tfSize.Height);
|
||||
|
||||
@@ -6196,19 +6196,19 @@ ek")]
|
||||
tf.Draw (Application.Screen, Attribute.Default, Attribute.Default);
|
||||
|
||||
var expectedText = """
|
||||
******UI Catalog: A comprehensive sample library for******
|
||||
**********************************************************
|
||||
_______ _ _ _____ _
|
||||
|__ __| (_) | | / ____| (_)
|
||||
| | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _
|
||||
| |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |
|
||||
| | __/ | | | | | | | | | | | (_| | || |__| | |_| | |
|
||||
|_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|
|
||||
**********************************************************
|
||||
**********************v2 - Pre-Alpha**********************
|
||||
**********************************************************
|
||||
**********https://github.com/gui-cs/Terminal.Gui**********
|
||||
**********************************************************
|
||||
UI Catalog: A comprehensive sample library and test app for
|
||||
***********************************************************
|
||||
_______ _ _ _____ _ *
|
||||
|__ __| (_) | | / ____| (_)*
|
||||
| | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _ *
|
||||
| |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |*
|
||||
| | __/ | | | | | | | | | | | (_| | || |__| | |_| | |*
|
||||
|_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|*
|
||||
***********************************************************
|
||||
**********************v2 - Pre-Alpha***********************
|
||||
***********************************************************
|
||||
**********https://github.com/gui-cs/Terminal.Gui***********
|
||||
***********************************************************
|
||||
""";
|
||||
|
||||
TestHelpers.AssertDriverContentsAre (expectedText.ReplaceLineEndings (), _output);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user