diff --git a/.gitignore b/.gitignore index cbf9d62d8..14e25a86c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,5 @@ demo.* *.tui/ *.dotCover + +logs/ diff --git a/Terminal.Gui/Application/ApplicationImpl.cs b/Terminal.Gui/Application/ApplicationImpl.cs index b6b8f91c4..bf40f381b 100644 --- a/Terminal.Gui/Application/ApplicationImpl.cs +++ b/Terminal.Gui/Application/ApplicationImpl.cs @@ -215,6 +215,7 @@ public class ApplicationImpl : IApplication bool wasInitialized = Application.Initialized; Application.ResetState (); + LogJsonErrors (); PrintJsonErrors (); if (wasInitialized) diff --git a/Terminal.Gui/Configuration/AttributeJsonConverter.cs b/Terminal.Gui/Configuration/AttributeJsonConverter.cs index e14748fd9..ff1797221 100644 --- a/Terminal.Gui/Configuration/AttributeJsonConverter.cs +++ b/Terminal.Gui/Configuration/AttributeJsonConverter.cs @@ -57,11 +57,11 @@ internal class AttributeJsonConverter : JsonConverter 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; diff --git a/Terminal.Gui/Configuration/ColorSchemeJsonConverter.cs b/Terminal.Gui/Configuration/ColorSchemeJsonConverter.cs index ae0742b5f..4b4f17c6a 100644 --- a/Terminal.Gui/Configuration/ColorSchemeJsonConverter.cs +++ b/Terminal.Gui/Configuration/ColorSchemeJsonConverter.cs @@ -59,7 +59,7 @@ internal class ColorSchemeJsonConverter : JsonConverter 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 ()) { diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 48bf2fd58..676f68251 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -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? _allConfigProperties; [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] - 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 = "")] - internal static readonly SourceGenerationContext _serializerContext = new (_serializerOptions); + internal static readonly SourceGenerationContext SerializerContext = new (SerializerOptions); [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] 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); } /// @@ -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 /// public static void OnApplied () { - Debug.WriteLine ("ConfigurationManager.OnApplied()"); + //Logging.Trace (""); Applied?.Invoke (null, new ()); @@ -308,7 +309,7 @@ public static class ConfigurationManager /// 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 ()); + } + } + /// /// Resets the state of . Should be called whenever a new app session (e.g. in /// starts. Called by if the reset 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 (); diff --git a/Terminal.Gui/Configuration/DictionaryJsonConverter.cs b/Terminal.Gui/Configuration/DictionaryJsonConverter.cs index bed7f874b..f9385a89e 100644 --- a/Terminal.Gui/Configuration/DictionaryJsonConverter.cs +++ b/Terminal.Gui/Configuration/DictionaryJsonConverter.cs @@ -28,7 +28,7 @@ internal class DictionaryJsonConverter : JsonConverter> { 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 : JsonConverter> //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 (); } diff --git a/Terminal.Gui/Configuration/ScopeJsonConverter.cs b/Terminal.Gui/Configuration/ScopeJsonConverter.cs index a23216ea0..11e83c6f7 100644 --- a/Terminal.Gui/Configuration/ScopeJsonConverter.cs +++ b/Terminal.Gui/Configuration/ScopeJsonConverter.cs @@ -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 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); } } diff --git a/Terminal.Gui/Configuration/SettingsScope.cs b/Terminal.Gui/Configuration/SettingsScope.cs index 25936ca86..1afe60399 100644 --- a/Terminal.Gui/Configuration/SettingsScope.cs +++ b/Terminal.Gui/Configuration/SettingsScope.cs @@ -45,9 +45,9 @@ public class SettingsScope : Scope // 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 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 } catch (IOException ioe) { - Debug.WriteLine ($"Couldn't open {filePath}. Retrying...: {ioe}"); + Logging.Warning ($"Couldn't open {filePath}. Retrying...: {ioe}"); Task.Delay (100); retryCount++; } diff --git a/Terminal.Gui/Configuration/ThemeManager.cs b/Terminal.Gui/Configuration/ThemeManager.cs index 8eb54fd29..e786ed798 100644 --- a/Terminal.Gui/Configuration/ThemeManager.cs +++ b/Terminal.Gui/Configuration/ThemeManager.cs @@ -127,7 +127,7 @@ public class ThemeManager : IDictionary [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 /// Called when the selected theme has changed. Fires the event. 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 [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; diff --git a/Terminal.Gui/ConsoleDrivers/V2/Logging.cs b/Terminal.Gui/ConsoleDrivers/V2/Logging.cs index 0d367f4a4..3257e8bd5 100644 --- a/Terminal.Gui/ConsoleDrivers/V2/Logging.cs +++ b/Terminal.Gui/ConsoleDrivers/V2/Logging.cs @@ -13,7 +13,7 @@ namespace Terminal.Gui; /// /// Also contains the /// instance that should be used for internal metrics -/// (iteration timing etc). +/// (iteration timing etc.). /// public static class Logging { @@ -51,7 +51,71 @@ public static class Logging public static readonly Histogram DrainInputStream = Meter.CreateHistogram ("Drain Input (ms)"); /// - /// Logs a trace message including the + /// Logs an error message including the class and method name. + /// + /// + /// + /// + public static void Error ( + string message, + [CallerMemberName] string caller = "", + [CallerFilePath] string filePath = "" + ) + { + string className = Path.GetFileNameWithoutExtension (filePath); + Logger.LogError ($"[{className}] [{caller}] {message}"); + } + + /// + /// Logs a critical message including the class and method name. + /// + /// + /// + /// + public static void Critical ( + string message, + [CallerMemberName] string caller = "", + [CallerFilePath] string filePath = "" + ) + { + string className = Path.GetFileNameWithoutExtension (filePath); + Logger.LogCritical ($"[{className}] [{caller}] {message}"); + } + + /// + /// Logs a debug message including the class and method name. + /// + /// + /// + /// + public static void Debug ( + string message, + [CallerMemberName] string caller = "", + [CallerFilePath] string filePath = "" + ) + { + string className = Path.GetFileNameWithoutExtension (filePath); + Logger.LogDebug ($"[{className}] [{caller}] {message}"); + } + + /// + /// Logs an informational message including the class and method name. + /// + /// + /// + /// + public static void Information ( + string message, + [CallerMemberName] string caller = "", + [CallerFilePath] string filePath = "" + ) + { + string className = Path.GetFileNameWithoutExtension (filePath); + Logger.LogInformation ($"[{className}] [{caller}] {message}"); + } + + /// + /// Logs a trace message including the class and method name. /// /// /// @@ -65,4 +129,20 @@ public static class Logging string className = Path.GetFileNameWithoutExtension (filePath); Logger.LogTrace ($"[{className}] [{caller}] {message}"); } + + /// + /// Logs a warning message including the class and method name. + /// + /// + /// + /// + public static void Warning ( + string message, + [CallerMemberName] string caller = "", + [CallerFilePath] string filePath = "" + ) + { + string className = Path.GetFileNameWithoutExtension (filePath); + Logger.LogWarning ($"[{className}] [{caller}] {message}"); + } } diff --git a/Terminal.sln.DotSettings b/Terminal.sln.DotSettings index 47b7f0412..8b9178c39 100644 --- a/Terminal.sln.DotSettings +++ b/Terminal.sln.DotSettings @@ -396,7 +396,7 @@ <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> <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> <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> - <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> + True ..\Terminal.sln.ToDo.DotSettings True diff --git a/UICatalog/Scenario.cs b/UICatalog/Scenario.cs index c460675c2..e45e269f7 100644 --- a/UICatalog/Scenario.cs +++ b/UICatalog/Scenario.cs @@ -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 (); diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 8b2ca2b68..bbcafcd5d 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -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; /// -/// 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. /// /// @@ -60,16 +60,24 @@ public class UICatalogApp private static int _cachedScenarioIndex; private static string? _cachedTheme = string.Empty; private static ObservableCollection? _categories; + [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] private static readonly FileSystemWatcher _currentDirWatcher = new (); + private static ViewDiagnosticFlags _diagnosticFlags; private static string _forceDriver = string.Empty; + [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] private static readonly FileSystemWatcher _homeDirWatcher = new (); + private static bool _isFirstRunning = true; private static Options _options; private static ObservableCollection? _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; /// - /// 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. /// /// 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 ] [--benchmark] [scenario name]" + // If no driver is provided, the default driver is used. Option driverOption = new Option ("--driver", "The IConsoleDriver to use.").FromAmong ( Application.GetDriverTypes () @@ -146,21 +153,34 @@ public class UICatalogApp driverOption.AddAlias ("-d"); driverOption.AddAlias ("--d"); - Option benchmarkFlag = new Option ("--benchmark", "Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked."); + Option benchmarkFlag = new ("--benchmark", "Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked."); benchmarkFlag.AddAlias ("-b"); benchmarkFlag.AddAlias ("--b"); - Option benchmarkTimeout = new Option ("--timeout", getDefaultValue: () => Scenario.BenchmarkTimeout, $"The maximum time in milliseconds to run a benchmark for. Default is {Scenario.BenchmarkTimeout}ms."); + Option 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 resultsFile = new Option ("--file", "The file to save benchmark results to. If not specified, the results will be displayed in a TableView."); + Option 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 debugLogLevel = new Option ("--debug-log-level", $"The level to use for logging (debug console and {_logFilePath})").FromAmong ( + Enum.GetNames () + ); + debugLogLevel.SetDefaultValue("Warning"); + debugLogLevel.AddAlias ("-dl"); + debugLogLevel.AddAlias ("--dl"); + Argument scenarioArgument = new Argument ( - 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 resultsList = new List (); + List 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 CreateLoggingMenuItems () + { + List menuItems = new () + { + CreateLoggingFlagsMenuItems () + }; + + return menuItems; + } + + [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] + private MenuItem [] CreateLoggingFlagsMenuItems () + { + string [] logLevelMenuStrings = Enum.GetNames ().Select (n => n = "_" + n).ToArray (); + LogEventLevel [] logLevels = Enum.GetValues (); + + List menuItems = new (); + + foreach (LogEventLevel logLevel in logLevels) + { + var item = new MenuItem + { + Title = logLevelMenuStrings [(int)logLevel] + }; + item.CheckType |= MenuItemCheckStyle.Checked; + item.Checked = Enum.Parse (_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 (_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. */ } } diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index c854ef8e6..aba324a4c 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -33,6 +33,7 @@ + diff --git a/UnitTests/Configuration/KeyJsonConverterTests.cs b/UnitTests/Configuration/KeyJsonConverterTests.cs index b30b5a30a..bb12d1590 100644 --- a/UnitTests/Configuration/KeyJsonConverterTests.cs +++ b/UnitTests/Configuration/KeyJsonConverterTests.cs @@ -45,8 +45,8 @@ public class KeyJsonConverterTests // Arrange // Act - string json = JsonSerializer.Serialize ((Key)key, ConfigurationManager._serializerOptions); - var deserializedKey = JsonSerializer.Deserialize (json, ConfigurationManager._serializerOptions); + string json = JsonSerializer.Serialize ((Key)key, ConfigurationManager.SerializerOptions); + var deserializedKey = JsonSerializer.Deserialize (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 (json, ConfigurationManager._serializerOptions); + Key deserializedKey = JsonSerializer.Deserialize (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 (json, ConfigurationManager._serializerOptions); + Key deserializedKey = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerOptions); Key expectedKey = new Key ((KeyCode)keyChar).WithCtrl.WithAlt; // Assert diff --git a/UnitTests/Configuration/RuneJsonConverterTests.cs b/UnitTests/Configuration/RuneJsonConverterTests.cs index b8c98498c..17f96458c 100644 --- a/UnitTests/Configuration/RuneJsonConverterTests.cs +++ b/UnitTests/Configuration/RuneJsonConverterTests.cs @@ -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 ( () => JsonSerializer.Deserialize ( 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 (json, ConfigurationManager._serializerOptions); + string json = JsonSerializer.Serialize (rune, ConfigurationManager.SerializerOptions); + var deserialized = JsonSerializer.Deserialize (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 (json, ConfigurationManager._serializerOptions); + var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerOptions); // Assert Assert.Equal ("a", deserialized.ToString ()); @@ -112,7 +112,7 @@ public class RuneJsonConverterTests var json = "\"\\u0061\""; // Act - var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager._serializerOptions); + var deserialized = JsonSerializer.Deserialize (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 (json, ConfigurationManager._serializerOptions); + var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerOptions); // Assert Assert.Equal ("a", deserialized.ToString ()); @@ -138,7 +138,7 @@ public class RuneJsonConverterTests var json = "97"; // Act - var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager._serializerOptions); + var deserialized = JsonSerializer.Deserialize (json, ConfigurationManager.SerializerOptions); // Assert Assert.Equal ("a", deserialized.ToString ()); diff --git a/UnitTests/Dialogs/MessageBoxTests.cs b/UnitTests/Dialogs/MessageBoxTests.cs index ff7fd728a..b163d154f 100644 --- a/UnitTests/Dialogs/MessageBoxTests.cs +++ b/UnitTests/Dialogs/MessageBoxTests.cs @@ -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 ◄⟧║ │ + │ ╚═══════════════════════════════════════════════════════════╝ │ └────────────────────────────────────────────────────────────────────┘ """; diff --git a/UnitTests/Text/TextFormatterTests.cs b/UnitTests/Text/TextFormatterTests.cs index 986775079..786f566fd 100644 --- a/UnitTests/Text/TextFormatterTests.cs +++ b/UnitTests/Text/TextFormatterTests.cs @@ -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); diff --git a/local_packages/Terminal.Gui.2.0.0.nupkg b/local_packages/Terminal.Gui.2.0.0.nupkg index 77a851558..d8fe09339 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.nupkg and b/local_packages/Terminal.Gui.2.0.0.nupkg differ diff --git a/local_packages/Terminal.Gui.2.0.0.snupkg b/local_packages/Terminal.Gui.2.0.0.snupkg index 3cb03eef7..91515e2d8 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.snupkg and b/local_packages/Terminal.Gui.2.0.0.snupkg differ