diff --git a/Example/Example.cs b/Example/Example.cs index 1d13f47b2..ede120378 100644 --- a/Example/Example.cs +++ b/Example/Example.cs @@ -6,7 +6,7 @@ using System; using Terminal.Gui; -// Override the default configuraiton for the application to use the Light theme +// Override the default configuration for the application to use the Light theme ConfigurationManager.RuntimeConfig = """{ "Theme": "Light" }"""; Application.Run ().Dispose (); diff --git a/Terminal.Gui/Configuration/ConfigLocations.cs b/Terminal.Gui/Configuration/ConfigLocations.cs index c2499c6c3..b5469c1c9 100644 --- a/Terminal.Gui/Configuration/ConfigLocations.cs +++ b/Terminal.Gui/Configuration/ConfigLocations.cs @@ -22,35 +22,35 @@ public enum ConfigLocations /// Default = 0b_0000_0001, - /// - /// Global settings in the current directory (e.g. ./.tui/config.json). - /// - GlobalCurrent = 0b_0000_0010, - - /// - /// Global settings in the home directory (e.g. ~/.tui/config.json). - /// - GlobalHome = 0b_0000_0100, - /// /// App resources (e.g. MyApp.Resources.config.json). /// - AppResources = 0b_0000_1000, - - /// - /// App settings in the current directory (e.g. ./.tui/MyApp.config.json). - /// - AppCurrent = 0b_0001_0000, - - /// - /// App settings in the home directory (e.g. ~/.tui/MyApp.config.json). - /// - AppHome = 0b_0010_0000, + AppResources = 0b_0000_0010, /// /// Settings in the static property. /// - Runtime = 0b_0100_0000, + Runtime = 0b_0000_0100, + + /// + /// Global settings in the current directory (e.g. ./.tui/config.json). + /// + GlobalCurrent = 0b_0000_1000, + + /// + /// Global settings in the home directory (e.g. ~/.tui/config.json). + /// + GlobalHome = 0b_0001_0000, + + /// + /// App settings in the current directory (e.g. ./.tui/MyApp.config.json). + /// + AppCurrent = 0b_0010_0000, + + /// + /// App settings in the home directory (e.g. ~/.tui/MyApp.config.json). + /// + AppHome = 0b_0100_0000, /// This constant is a combination of all locations All = 0b_1111_1111 diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index dff4abed4..473f1bf65 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -223,7 +223,7 @@ public static class ConfigurationManager /// /// Gets or sets the in-memory config.json. See . /// - public static string? RuntimeConfig { get; set; } + public static string? RuntimeConfig { get; set; } = """{ }"""; /// /// Loads all settings found in the configuration storage locations (). Optionally, resets @@ -250,16 +250,6 @@ public static class ConfigurationManager Reset (); } - if (Locations.HasFlag (ConfigLocations.GlobalCurrent)) - { - Settings?.Update ($"./.tui/{_configFilename}"); - } - - if (Locations.HasFlag (ConfigLocations.GlobalHome)) - { - Settings?.Update ($"~/.tui/{_configFilename}"); - } - if (Locations.HasFlag (ConfigLocations.AppResources)) { string? embeddedStylesResourceName = Assembly.GetEntryAssembly () @@ -272,22 +262,33 @@ public static class ConfigurationManager embeddedStylesResourceName = _configFilename; } - Settings?.UpdateFromResource (Assembly.GetEntryAssembly ()!, embeddedStylesResourceName!); - } - - if (Locations.HasFlag (ConfigLocations.AppCurrent)) - { - Settings?.Update ($"./.tui/{AppName}.{_configFilename}"); - } - - if (Locations.HasFlag (ConfigLocations.AppHome)) - { - Settings?.Update ($"~/.tui/{AppName}.{_configFilename}"); + Settings?.UpdateFromResource (Assembly.GetEntryAssembly ()!, embeddedStylesResourceName!, ConfigLocations.AppResources); } if (Locations.HasFlag (ConfigLocations.Runtime) && !string.IsNullOrEmpty (RuntimeConfig)) { - Settings?.Update (RuntimeConfig, "ConfigurationManager.Memory"); + Settings?.Update (RuntimeConfig, "ConfigurationManager.RuntimeConfig", ConfigLocations.Runtime); + } + + if (Locations.HasFlag (ConfigLocations.GlobalCurrent)) + { + Settings?.Update ($"./.tui/{_configFilename}", ConfigLocations.GlobalCurrent); + } + + if (Locations.HasFlag (ConfigLocations.GlobalHome)) + { + Settings?.Update ($"~/.tui/{_configFilename}", ConfigLocations.GlobalHome); + } + + + if (Locations.HasFlag (ConfigLocations.AppCurrent)) + { + Settings?.Update ($"./.tui/{AppName}.{_configFilename}", ConfigLocations.AppCurrent); + } + + if (Locations.HasFlag (ConfigLocations.AppHome)) + { + Settings?.Update ($"~/.tui/{AppName}.{_configFilename}", ConfigLocations.AppHome); } ThemeManager.SelectedTheme = Settings!["Theme"].PropertyValue as string ?? "Default"; @@ -358,7 +359,8 @@ public static class ConfigurationManager { Settings.UpdateFromResource ( typeof (ConfigurationManager).Assembly, - $"Terminal.Gui.Resources.{_configFilename}" + $"Terminal.Gui.Resources.{_configFilename}", + ConfigLocations.Default ); } diff --git a/Terminal.Gui/Configuration/SettingsScope.cs b/Terminal.Gui/Configuration/SettingsScope.cs index 983d790a1..25936ca86 100644 --- a/Terminal.Gui/Configuration/SettingsScope.cs +++ b/Terminal.Gui/Configuration/SettingsScope.cs @@ -27,7 +27,7 @@ namespace Terminal.Gui; public class SettingsScope : Scope { /// The list of paths to the configuration files. - public List Sources = new (); + public Dictionary Sources { get; } = new (); /// Points to our JSON schema. [JsonInclude] @@ -37,9 +37,10 @@ public class SettingsScope : Scope /// Updates the with the settings in a JSON string. /// Json document to update the settings with. /// The source (filename/resource name) the Json document was read from. + /// Location [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public SettingsScope? Update (Stream stream, string source) + public SettingsScope? Update (Stream stream, string source, ConfigLocations location) { // Update the existing settings with the new settings. try @@ -47,9 +48,9 @@ public class SettingsScope : Scope Update ((SettingsScope)JsonSerializer.Deserialize (stream, typeof (SettingsScope), _serializerOptions)!); OnUpdated (); Debug.WriteLine ($"ConfigurationManager: Read configuration from \"{source}\""); - if (!Sources.Contains (source)) + if (!Sources.ContainsValue (source)) { - Sources.Add (source); + Sources.Add (location, source); } return this; @@ -68,19 +69,20 @@ public class SettingsScope : Scope } /// Updates the with the settings in a JSON file. - /// + /// Path to the file. + /// The location [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public SettingsScope? Update (string filePath) + public SettingsScope? Update (string filePath, ConfigLocations location) { string realPath = filePath.Replace ("~", Environment.GetFolderPath (Environment.SpecialFolder.UserProfile)); if (!File.Exists (realPath)) { Debug.WriteLine ($"ConfigurationManager: Configuration file \"{realPath}\" does not exist."); - if (!Sources.Contains (filePath)) + if (!Sources.ContainsValue (filePath)) { - Sources.Add (filePath); + Sources.Add (location, filePath); } return this; @@ -95,7 +97,7 @@ public class SettingsScope : Scope try { FileStream? stream = File.OpenRead (realPath); - SettingsScope? s = Update (stream, filePath); + SettingsScope? s = Update (stream, filePath, location); stream.Close (); stream.Dispose (); @@ -103,7 +105,7 @@ public class SettingsScope : Scope } catch (IOException ioe) { - Debug.WriteLine($"Couldn't open {filePath}. Retrying...: {ioe}"); + Debug.WriteLine ($"Couldn't open {filePath}. Retrying...: {ioe}"); Task.Delay (100); retryCount++; } @@ -115,32 +117,33 @@ public class SettingsScope : Scope /// Updates the with the settings in a JSON string. /// Json document to update the settings with. /// The source (filename/resource name) the Json document was read from. + /// The location. [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public SettingsScope? Update (string? json, string source) + public SettingsScope? Update (string? json, string source, ConfigLocations location) { - //if (string.IsNullOrEmpty (json)) - //{ - // Debug.WriteLine ($"ConfigurationManager: Configuration file \"{source}\" is empty."); - // return this; - //} + if (string.IsNullOrEmpty (json)) + { + return null; + } var stream = new MemoryStream (); var writer = new StreamWriter (stream); writer.Write (json); writer.Flush (); stream.Position = 0; - return Update (stream, source); + return Update (stream, source, location); } /// Updates the with the settings from a Json resource. /// /// + /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] - public SettingsScope? UpdateFromResource (Assembly assembly, string resourceName) + public SettingsScope? UpdateFromResource (Assembly assembly, string resourceName, ConfigLocations location) { - if (resourceName is null || string.IsNullOrEmpty (resourceName)) + if (string.IsNullOrEmpty (resourceName)) { Debug.WriteLine ( $"ConfigurationManager: Resource \"{resourceName}\" does not exist in \"{assembly.GetName ().Name}\"." @@ -149,20 +152,13 @@ public class SettingsScope : Scope return this; } - // BUG: Not trim-compatible - // Not a bug, per se, but it's easily fixable by just loading the file. - // Defaults can just be field initializers for involved types. - using Stream? stream = assembly.GetManifestResourceStream (resourceName)!; + using Stream? stream = assembly.GetManifestResourceStream (resourceName); if (stream is null) { - Debug.WriteLine ( - $"ConfigurationManager: Failed to read resource \"{resourceName}\" from \"{assembly.GetName ().Name}\"." - ); - - return this; + return null; } - return Update (stream, $"resource://[{assembly.GetName ().Name}]/{resourceName}"); + return Update (stream, $"resource://[{assembly.GetName ().Name}]/{resourceName}", location); } } diff --git a/UICatalog/Scenarios/Editors/Resources/config.json b/UICatalog/Resources/config.json similarity index 100% rename from UICatalog/Scenarios/Editors/Resources/config.json rename to UICatalog/Resources/config.json diff --git a/UICatalog/Scenarios/ConfigurationEditor.cs b/UICatalog/Scenarios/ConfigurationEditor.cs index 037acceec..5f30416bf 100644 --- a/UICatalog/Scenarios/ConfigurationEditor.cs +++ b/UICatalog/Scenarios/ConfigurationEditor.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using System.IO; using System.Linq; using System.Reflection; @@ -21,9 +22,9 @@ public class ConfigurationEditor : Scenario HotNormal = new Attribute (Color.Magenta, Color.White) }; - private static Action _editorColorSchemeChanged; - private Shortcut _lenShortcut; - private TileView _tileView; + private static Action? _editorColorSchemeChanged; + private TabView? _tabView; + private Shortcut? _lenShortcut; [SerializableConfigurationProperty (Scope = typeof (AppScope))] public static ColorScheme EditorColorScheme @@ -42,26 +43,15 @@ public class ConfigurationEditor : Scenario Toplevel top = new (); - _tileView = new TileView (0) - { - Width = Dim.Fill (), - Height = Dim.Fill (1), - Orientation = Orientation.Vertical, - LineStyle = LineStyle.Single, - TabStop = TabBehavior.TabGroup - }; - - top.Add (_tileView); - _lenShortcut = new Shortcut () { - Title = "Len: ", + Title = "", }; var quitShortcut = new Shortcut () { Key = Application.QuitKey, - Title = $"{Application.QuitKey} Quit", + Title = $"Quit", Action = Quit }; @@ -81,35 +71,44 @@ public class ConfigurationEditor : Scenario var statusBar = new StatusBar ([quitShortcut, reloadShortcut, saveShortcut, _lenShortcut]); - top.Add (statusBar); + _tabView = new () + { + Width = Dim.Fill (), + Height = Dim.Fill (Dim.Func (() => statusBar.Frame.Height)) + }; + + top.Add (_tabView, statusBar); top.Loaded += (s, a) => { Open (); - //_tileView.AdvanceFocus (NavigationDirection.Forward, null); + _editorColorSchemeChanged?.Invoke (); }; - _editorColorSchemeChanged += () => - { - foreach (Tile t in _tileView.Tiles) - { - t.ContentView.ColorScheme = EditorColorScheme; - t.ContentView.SetNeedsDraw (); - } + void OnEditorColorSchemeChanged () + { + if (Application.Top is { }) + { + return; + } - ; - }; + foreach (ConfigTextView t in _tabView.Subviews.Where (v => v is ConfigTextView).Cast ()) + { + t.ColorScheme = EditorColorScheme; + } + } - _editorColorSchemeChanged.Invoke (); + _editorColorSchemeChanged += OnEditorColorSchemeChanged; Application.Run (top); + _editorColorSchemeChanged -= OnEditorColorSchemeChanged; top.Dispose (); Application.Shutdown (); } public void Save () { - if (_tileView.MostFocused is ConfigTextView editor) + if (Application.Navigation?.GetFocused () is ConfigTextView editor) { editor.Save (); } @@ -117,56 +116,65 @@ public class ConfigurationEditor : Scenario private void Open () { - var subMenu = new MenuBarItem { Title = "_View" }; - - foreach (string configFile in ConfigurationManager.Settings.Sources) + foreach (var config in ConfigurationManager.Settings!.Sources) { var homeDir = $"{Environment.GetFolderPath (Environment.SpecialFolder.UserProfile)}"; - var fileInfo = new FileInfo (configFile.Replace ("~", homeDir)); + var fileInfo = new FileInfo (config.Value.Replace ("~", homeDir)); - Tile tile = _tileView.InsertTile (_tileView.Tiles.Count); - tile.Title = configFile.StartsWith ("resource://") ? fileInfo.Name : configFile; - - var textView = new ConfigTextView + var editor = new ConfigTextView { - X = 0, - Y = 0, + Title = config.Value.StartsWith ("resource://") ? fileInfo.Name : config.Value, Width = Dim.Fill (), - Height = Dim.Fill (), + Height = Dim.Fill(), FileInfo = fileInfo, - Tile = tile }; - tile.ContentView.Add (textView); + Tab tab = new Tab () + { + View = editor, + DisplayText = config.Key.ToString () + }; - textView.Read (); + _tabView!.AddTab (tab, false); - textView.HasFocusChanged += (s, e) => - { - if (e.NewValue) - { - _lenShortcut.Title = $"Len:{textView.Text.Length}"; - } - }; + editor.Read (); + + editor.ContentsChanged += (sender, args) => + { + _lenShortcut!.Title = _lenShortcut!.Title.Replace ("*", ""); + if (editor.IsDirty) + { + _lenShortcut!.Title += "*"; + } + }; + + _lenShortcut!.Title = $"{editor.Title}"; } - if (_tileView.Tiles.Count > 2) - { - _tileView.Tiles.ToArray () [1].ContentView.SetFocus (); - } + _tabView!.SelectedTabChanged += (sender, args) => + { + _lenShortcut!.Title = $"{args.NewTab.View!.Title}"; + }; + } private void Quit () { - foreach (Tile tile in _tileView.Tiles) - { - var editor = tile.ContentView.Subviews [0] as ConfigTextView; + foreach (ConfigTextView editor in _tabView!.Tabs.Select(v => + { + if (v.View is ConfigTextView ctv) + { + return ctv; + } + return null; + }).Cast ()) + { if (editor.IsDirty) { int result = MessageBox.Query ( "Save Changes", - $"Save changes to {editor.FileInfo.FullName}", + $"Save changes to {editor.FileInfo!.Name}", "_Yes", "_No", "_Cancel" @@ -189,7 +197,7 @@ public class ConfigurationEditor : Scenario private void Reload () { - if (_tileView.MostFocused is ConfigTextView editor) + if (Application.Navigation?.GetFocused () is ConfigTextView editor) { editor.Read (); } @@ -199,32 +207,16 @@ public class ConfigurationEditor : Scenario { internal ConfigTextView () { - ContentsChanged += (s, obj) => - { - if (IsDirty) - { - if (!Tile.Title.EndsWith ('*')) - { - Tile.Title += '*'; - } - else - { - Tile.Title = Tile.Title.TrimEnd ('*'); - } - } - }; TabStop = TabBehavior.TabGroup; - } - internal FileInfo FileInfo { get; set; } - internal Tile Tile { get; set; } + internal FileInfo? FileInfo { get; set; } internal void Read () { - Assembly assembly = null; + Assembly? assembly = null; - if (FileInfo.FullName.Contains ("[Terminal.Gui]")) + if (FileInfo!.FullName.Contains ("[Terminal.Gui]")) { // Library resources assembly = typeof (ConfigurationManager).Assembly; @@ -236,19 +228,27 @@ public class ConfigurationEditor : Scenario if (assembly != null) { - string name = assembly - .GetManifestResourceNames () - .FirstOrDefault (x => x.EndsWith ("config.json")); - using Stream stream = assembly.GetManifestResourceStream (name); - using var reader = new StreamReader (stream); - Text = reader.ReadToEnd (); - ReadOnly = true; - Enabled = true; + string? name = assembly + .GetManifestResourceNames () + .FirstOrDefault (x => x.EndsWith ("config.json")); + if (!string.IsNullOrEmpty (name)) + { + + using Stream? stream = assembly.GetManifestResourceStream (name); + using var reader = new StreamReader (stream!); + Text = reader.ReadToEnd (); + ReadOnly = true; + Enabled = true; + } return; } - if (!FileInfo.Exists) + if (FileInfo!.FullName.Contains ("RuntimeConfig")) + { + Text = ConfigurationManager.RuntimeConfig!; + + } else if (!FileInfo.Exists) { // Create empty config file Text = ConfigurationManager.GetEmptyJson (); @@ -257,12 +257,17 @@ public class ConfigurationEditor : Scenario { Text = File.ReadAllText (FileInfo.FullName); } - - Tile.Title = Tile.Title.TrimEnd ('*'); } internal void Save () { + if (FileInfo!.FullName.Contains ("RuntimeConfig")) + { + ConfigurationManager.RuntimeConfig = Text; + IsDirty = false; + return; + } + if (!Directory.Exists (FileInfo.DirectoryName)) { // Create dir @@ -272,7 +277,6 @@ public class ConfigurationEditor : Scenario using StreamWriter writer = File.CreateText (FileInfo.FullName); writer.Write (Text); writer.Close (); - Tile.Title = Tile.Title.TrimEnd ('*'); IsDirty = false; } } diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index a8ae5aa19..fb1247886 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -20,10 +20,10 @@ TRACE;DEBUG_IDISPOSABLE - + - + diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index 3c6c50174..00b6445f8 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -189,7 +189,7 @@ public class ConfigurationManagerTests Updated += ConfigurationManager_Updated; var fired = false; - void ConfigurationManager_Updated (object? sender, ConfigurationManagerEventArgs obj) + void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs obj) { fired = true; } @@ -285,7 +285,7 @@ public class ConfigurationManagerTests Updated += ConfigurationManager_Updated; var fired = false; - void ConfigurationManager_Updated (object? sender, ConfigurationManagerEventArgs obj) + void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs obj) { fired = true; } @@ -522,7 +522,7 @@ public class ConfigurationManagerTests // Change Base Stream json = ToStream (); - Settings!.Update (json, "TestConfigurationManagerInitDriver"); + Settings!.Update (json, "TestConfigurationManagerInitDriver", ConfigLocations.Runtime); Dictionary colorSchemes = (Dictionary)Themes [Themes.Theme] ["ColorSchemes"].PropertyValue; @@ -580,7 +580,7 @@ public class ConfigurationManagerTests } }"; - Settings!.Update (json, "test"); + Settings!.Update (json, "test", ConfigLocations.Runtime); // AbNormal is not a ColorScheme attribute json = @" @@ -603,7 +603,7 @@ public class ConfigurationManagerTests } }"; - Settings.Update (json, "test"); + Settings.Update (json, "test", ConfigLocations.Runtime); // Modify hotNormal background only json = @" @@ -625,9 +625,9 @@ public class ConfigurationManagerTests } }"; - Settings.Update (json, "test"); + Settings.Update (json, "test", ConfigLocations.Runtime); - Settings.Update ("{}}", "test"); + Settings.Update ("{}}", "test", ConfigLocations.Runtime); Assert.NotEqual (0, _jsonErrors.Length); @@ -663,7 +663,7 @@ public class ConfigurationManagerTests ] }"; - var jsonException = Assert.Throws (() => Settings!.Update (json, "test")); + var jsonException = Assert.Throws (() => Settings!.Update (json, "test", ConfigLocations.Runtime)); Assert.Equal ("Unexpected color name: brownish.", jsonException.Message); // AbNormal is not a ColorScheme attribute @@ -687,7 +687,7 @@ public class ConfigurationManagerTests ] }"; - jsonException = Assert.Throws (() => Settings!.Update (json, "test")); + jsonException = Assert.Throws (() => Settings!.Update (json, "test", ConfigLocations.Runtime)); Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message); // Modify hotNormal background only @@ -710,7 +710,7 @@ public class ConfigurationManagerTests ] }"; - jsonException = Assert.Throws (() => Settings!.Update (json, "test")); + jsonException = Assert.Throws (() => Settings!.Update (json, "test", ConfigLocations.Runtime)); Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message); // Unknown property @@ -719,7 +719,7 @@ public class ConfigurationManagerTests ""Unknown"" : ""Not known"" }"; - jsonException = Assert.Throws (() => Settings!.Update (json, "test")); + jsonException = Assert.Throws (() => Settings!.Update (json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("Unknown property", jsonException.Message); Assert.Equal (0, _jsonErrors.Length); @@ -735,7 +735,7 @@ public class ConfigurationManagerTests GetHardCodedDefaults (); Stream stream = ToStream (); - Settings!.Update (stream, "TestConfigurationManagerToJson"); + Settings!.Update (stream, "TestConfigurationManagerToJson", ConfigLocations.Runtime); } [Fact] @@ -884,7 +884,7 @@ public class ConfigurationManagerTests Reset (); ThrowOnJsonErrors = true; - Settings!.Update (json, "TestConfigurationManagerUpdateFromJson"); + Settings!.Update (json, "TestConfigurationManagerUpdateFromJson", ConfigLocations.Runtime); Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode); Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue)!.KeyCode); diff --git a/UnitTests/Configuration/SettingsScopeTests.cs b/UnitTests/Configuration/SettingsScopeTests.cs index 746bfba6c..13e333a0c 100644 --- a/UnitTests/Configuration/SettingsScopeTests.cs +++ b/UnitTests/Configuration/SettingsScopeTests.cs @@ -23,7 +23,7 @@ public class SettingsScopeTests } """; - Settings!.Update (json, "test"); + Settings!.Update (json, "test", ConfigLocations.Runtime); // assert Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue); diff --git a/docfx/docs/config.md b/docfx/docs/config.md index e593f921f..d806e7f3e 100644 --- a/docfx/docs/config.md +++ b/docfx/docs/config.md @@ -12,19 +12,19 @@ Settings that will apply to all applications (global settings) reside in files n Settings are applied using the following precedence (higher precedence settings overwrite lower precedence settings): -1. @Terminal.Gui.ConfigLocations.Runtime - Settings stored in the @Terminal.Gui.ConfigurationManager.RuntimeConfig static property --- Hightest precedence. +1. @Terminal.Gui.ConfigLocations.Default - Default settings in the Terminal.Gui assembly -- Lowest precedence. -2. @Terminal.Gui.ConfigLocations.AppHome - App-specific settings in the users's home directory (`~/.tui/appname.config.json`). +2. @Terminal.Gui.ConfigLocations.Runtime - Settings stored in the @Terminal.Gui.ConfigurationManager.RuntimeConfig static property. -3. @Terminal.Gui.ConfigLocations.AppCurrent - App-specific settings in the directory the app was launched from (`./.tui/appname.config.json`). +3. @Terminal.Gui.ConfigLocations.AppResources - App settings in app resources (`Resources/config.json`). -4. @Terminal.Gui.ConfigLocations.AppResources - App settings in app resources (`Resources/config.json`). +4. @Terminal.Gui.ConfigLocations.AppHome - App-specific settings in the users's home directory (`~/.tui/appname.config.json`). -5. @Terminal.Gui.ConfigLocations.GlobalHome - Global settings in the the user's home directory (`~/.tui/config.json`). +5. @Terminal.Gui.ConfigLocations.AppCurrent - App-specific settings in the directory the app was launched from (`./.tui/appname.config.json`). -6. @Terminal.Gui.ConfigLocations.GlobalCurrent - Global settings in the directory the app was launched from (`./.tui/config.json`). +6. @Terminal.Gui.ConfigLocations.GlobalHome - Global settings in the the user's home directory (`~/.tui/config.json`). -7. @Terminal.Gui.ConfigLocations.Default - Default settings in the Terminal.Gui assembly -- Lowest precedence. +7. @Terminal.Gui.ConfigLocations.GlobalCurrent - Global settings in the directory the app was launched from (`./.tui/config.json`) --- Hightest precedence. The `UI Catalog` application provides an example of how to use the [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) class to load and save configuration files. The `Configuration Editor` scenario provides an editor that allows users to edit the configuration files. UI Catalog also uses a file system watcher to detect changes to the configuration files to tell [`ConfigurationManager`](~/api/Terminal.Gui.ConfigurationManager.yml) to reload them; allowing users to change settings without having to restart the application.