Files
FlaxEngine/Source/Editor/Options/OptionsModule.cs
Luke Schneider b7b8213179 Some additional fixes to light theme support
Fixed some issues with light theme support:

1) Icons in content tree nodes (Folder icons) now use the foreground color.  I did not find a case where the content tree is used for other icons.

2) The asset picker now uses the Background Normal color (instead of a very transparent dark gray) for the background, and Orange for the text.  Did not seem like it warranted adding a new color, and Orange works in both dark and light styles.

3) The platform selector icons are now hard-coded instead of based on the style.  This may sound odd, but the icons are colored, so they should always use White as the fully active color.  Previously they worked with a dark theme because the Foreground was set to white.

4) Fixed the CollectionBackgroundColor in the light theme being dark gray instead of light gray like it should be.  This fixes certain lists of things having a dark background in the light theme.
2023-09-29 07:43:59 -05:00

345 lines
14 KiB
C#

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using FlaxEditor.Modules;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
namespace FlaxEditor.Options
{
/// <summary>
/// Editor options management module.
/// </summary>
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
public sealed class OptionsModule : EditorModule
{
/// <summary>
/// The current editor options. Don't modify the values directly (local session state lifetime), use <see cref="Apply"/>.
/// </summary>
public EditorOptions Options = new EditorOptions();
/// <summary>
/// Occurs when editor options get changed (reloaded or applied).
/// </summary>
public event Action<EditorOptions> OptionsChanged;
/// <summary>
/// Occurs when editor options get changed (reloaded or applied).
/// </summary>
public event Action CustomSettingsChanged;
/// <summary>
/// The custom settings factory delegate. It should return the default settings object for a given options content.
/// </summary>
/// <returns>The custom settings object.</returns>
public delegate object CreateCustomSettingsDelegate();
private readonly string _optionsFilePath;
private readonly Dictionary<string, CreateCustomSettingsDelegate> _customSettings = new Dictionary<string, CreateCustomSettingsDelegate>();
/// <summary>
/// Gets the custom settings factories. Each entry defines the custom settings type identified by the given key name. The value is a factory function that returns the default options for a given type.
/// </summary>
public IReadOnlyDictionary<string, CreateCustomSettingsDelegate> CustomSettings => _customSettings;
internal OptionsModule(Editor editor)
: base(editor)
{
// Always load options before the other modules setup
InitOrder = -1000000;
_optionsFilePath = Path.Combine(Editor.LocalCachePath, "EditorOptions.json");
}
/// <summary>
/// Adds the custom settings factory.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="factory">The factory function.</param>
public void AddCustomSettings(string name, CreateCustomSettingsDelegate factory)
{
if (_customSettings.ContainsKey(name))
throw new ArgumentException(string.Format("Custom settings \'{0}\' already added.", name), nameof(name));
Editor.Log(string.Format("Add custom editor settings \'{0}\'", name));
_customSettings.Add(name, factory);
if (!Options.CustomSettings.ContainsKey(name))
{
Options.CustomSettings.Add(name, JsonSerializer.Serialize(factory(), typeof(object)));
}
CustomSettingsChanged?.Invoke();
}
/// <summary>
/// Removes the custom settings factory.
/// </summary>
/// <param name="name">The name.</param>
public void RemoveCustomSettings(string name)
{
if (!_customSettings.ContainsKey(name))
throw new ArgumentException(string.Format("Custom settings \'{0}\' has not been added.", name), nameof(name));
Editor.Log(string.Format("Remove custom editor settings \'{0}\'", name));
_customSettings.Remove(name);
CustomSettingsChanged?.Invoke();
}
/// <summary>
/// Loads the settings from the file.
/// </summary>
public void Load()
{
Editor.Log("Loading editor options");
if (!File.Exists(_optionsFilePath))
{
Editor.LogWarning("Missing editor settings");
return;
}
try
{
// Load asset
var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(_optionsFilePath);
if (asset == null)
{
Editor.LogWarning("Invalid editor settings");
return;
}
if (asset.WaitForLoaded())
{
Editor.LogError("Failed to load editor settings");
return;
}
// Deserialize data
var assetObj = asset.CreateInstance();
if (assetObj is EditorOptions options)
{
// Add missing custom options
foreach (var e in _customSettings)
{
if (!options.CustomSettings.ContainsKey(e.Key))
options.CustomSettings.Add(e.Key, JsonSerializer.Serialize(e.Value()));
}
float prevInterfaceScale = Options.Interface.InterfaceScale;
Options = options;
OnOptionsChanged();
// Scale interface relative to the current value (eg. when using system-provided Dpi Scale)
Platform.CustomDpiScale *= Options.Interface.InterfaceScale / prevInterfaceScale;
}
else
{
Editor.LogWarning("Failed to deserialize editor settings");
}
}
catch (Exception ex)
{
Editor.LogError("Failed to load editor options.");
Editor.LogWarning(ex);
}
}
/// <summary>
/// Applies the specified options and updates the dependant services.
/// </summary>
/// <param name="options">The new options.</param>
public void Apply(EditorOptions options)
{
Options = options;
OnOptionsChanged();
Save();
}
private void Save()
{
// Update file
if (Editor.SaveJsonAsset(_optionsFilePath, Options))
{
MessageBox.Show(string.Format("Failed to save editor option to '{0}'. Ensure that directory exists and program has access to it.", _optionsFilePath), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Special case for editor analytics
var editorAnalyticsTrackingFile = Path.Combine(Editor.LocalCachePath, "noTracking");
if (Options.General.EnableEditorAnalytics)
{
if (File.Exists(editorAnalyticsTrackingFile))
{
File.Delete(editorAnalyticsTrackingFile);
}
}
else
{
if (!File.Exists(editorAnalyticsTrackingFile))
{
File.WriteAllText(editorAnalyticsTrackingFile, "Don't track me, please.");
}
}
}
private void OnOptionsChanged()
{
Editor.Log("Editor options changed!");
// Sync C++ backend options
Editor.InternalOptions internalOptions;
internalOptions.AutoReloadScriptsOnMainWindowFocus = (byte)(Options.General.AutoReloadScriptsOnMainWindowFocus ? 1 : 0);
internalOptions.ForceScriptCompilationOnStartup = (byte)(Options.General.ForceScriptCompilationOnStartup ? 1 : 0);
internalOptions.UseAssetImportPathRelative = (byte)(Options.General.UseAssetImportPathRelative ? 1 : 0);
internalOptions.AutoRebuildCSG = (byte)(Options.General.AutoRebuildCSG ? 1 : 0);
internalOptions.AutoRebuildCSGTimeoutMs = Options.General.AutoRebuildCSGTimeoutMs;
internalOptions.AutoRebuildNavMesh = (byte)(Options.General.AutoRebuildNavMesh ? 1 : 0);
internalOptions.AutoRebuildNavMeshTimeoutMs = Options.General.AutoRebuildNavMeshTimeoutMs;
Editor.Internal_SetOptions(ref internalOptions);
EditorAssets.Cache.OnEditorOptionsChanged(Options);
// Send event
OptionsChanged?.Invoke(Options);
}
private void SetupStyle()
{
var themeOptions = Options.Theme;
// If a non-default style was chosen, switch to that style
string styleName = themeOptions.SelectedStyle;
if (styleName != "Default" && styleName != "LightDefault" && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null)
{
Style.Current = style;
}
else
{
if (styleName == "LightDefault")
{
Style.Current = CreateLightStyle();
}
else
{
Style.Current = CreateDefaultStyle();
}
}
}
/// <summary>
/// Creates the default style.
/// </summary>
/// <returns>The style object.</returns>
public Style CreateDefaultStyle()
{
// Metro Style colors
var options = Options;
var style = new Style
{
Background = Color.FromBgra(0xFF1C1C1C),
LightBackground = Color.FromBgra(0xFF2D2D30),
Foreground = Color.FromBgra(0xFFFFFFFF),
ForegroundGrey = Color.FromBgra(0xFFA9A9B3),
ForegroundDisabled = Color.FromBgra(0xFF787883),
ForegroundViewport = Color.FromBgra(0xFFFFFFFF),
BackgroundHighlighted = Color.FromBgra(0xFF54545C),
BorderHighlighted = Color.FromBgra(0xFF6A6A75),
BackgroundSelected = Color.FromBgra(0xFF007ACC),
BorderSelected = Color.FromBgra(0xFF1C97EA),
BackgroundNormal = Color.FromBgra(0xFF3F3F46),
BorderNormal = Color.FromBgra(0xFF54545C),
TextBoxBackground = Color.FromBgra(0xFF333337),
TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46),
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
ProgressNormal = Color.FromBgra(0xFF0ad328),
// Fonts
FontTitle = options.Interface.TitleFont.GetFont(),
FontLarge = options.Interface.LargeFont.GetFont(),
FontMedium = options.Interface.MediumFont.GetFont(),
FontSmall = options.Interface.SmallFont.GetFont(),
// Icons
ArrowDown = Editor.Icons.ArrowDown12,
ArrowRight = Editor.Icons.ArrowRight12,
Search = Editor.Icons.Search12,
Settings = Editor.Icons.Settings12,
Cross = Editor.Icons.Cross12,
CheckBoxIntermediate = Editor.Icons.CheckBoxIntermediate12,
CheckBoxTick = Editor.Icons.CheckBoxTick12,
StatusBarSizeGrip = Editor.Icons.WindowDrag12,
Translate = Editor.Icons.Translate32,
Rotate = Editor.Icons.Rotate32,
Scale = Editor.Icons.Scale32,
Scalar = Editor.Icons.Scalar32,
SharedTooltip = new Tooltip()
};
style.DragWindow = style.BackgroundSelected * 0.7f;
return style;
}
/// <summary>
/// Creates the light style (2nd default).
/// </summary>
/// <returns>The style object.</returns>
public Style CreateLightStyle()
{
// Metro Style colors
var options = Options;
var style = new Style
{
Background = new Color(0.92f, 0.92f, 0.92f, 1f),
LightBackground = new Color(0.84f, 0.84f, 0.88f, 1f),
DragWindow = new Color(0.0f, 0.26f, 0.43f, 0.70f),
Foreground = new Color(0.0f, 0.0f, 0.0f, 1f),
ForegroundGrey = new Color(0.30f, 0.30f, 0.31f, 1f),
ForegroundDisabled = new Color(0.45f, 0.45f, 0.49f, 1f),
ForegroundViewport = new Color(1.0f, 1.0f, 1.0f, 1f),
BackgroundHighlighted = new Color(0.59f, 0.59f, 0.64f, 1f),
BorderHighlighted = new Color(0.50f, 0.50f, 0.55f, 1f),
BackgroundSelected = new Color(0.00f, 0.46f, 0.78f, 0.78f),
BorderSelected = new Color(0.11f, 0.57f, 0.88f, 0.65f),
BackgroundNormal = new Color(0.67f, 0.67f, 0.75f, 1f),
BorderNormal = new Color(0.59f, 0.59f, 0.64f, 1f),
TextBoxBackground = new Color(0.75f, 0.75f, 0.81f, 1f),
TextBoxBackgroundSelected = new Color(0.73f, 0.73f, 0.80f, 1f),
CollectionBackgroundColor = new Color(0.85f, 0.85f, 0.88f, 1f),
ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f),
// Fonts
FontTitle = options.Interface.TitleFont.GetFont(),
FontLarge = options.Interface.LargeFont.GetFont(),
FontMedium = options.Interface.MediumFont.GetFont(),
FontSmall = options.Interface.SmallFont.GetFont(),
// Icons
ArrowDown = Editor.Icons.ArrowDown12,
ArrowRight = Editor.Icons.ArrowRight12,
Search = Editor.Icons.Search12,
Settings = Editor.Icons.Settings12,
Cross = Editor.Icons.Cross12,
CheckBoxIntermediate = Editor.Icons.CheckBoxIntermediate12,
CheckBoxTick = Editor.Icons.CheckBoxTick12,
StatusBarSizeGrip = Editor.Icons.WindowDrag12,
Translate = Editor.Icons.Translate32,
Rotate = Editor.Icons.Rotate32,
Scale = Editor.Icons.Scale32,
Scalar = Editor.Icons.Scalar32,
SharedTooltip = new Tooltip()
};
return style;
}
/// <inheritdoc />
public override void OnInit()
{
Editor.Log("Options file path: " + _optionsFilePath);
Load();
SetupStyle();
}
}
}