Skip to content

Commit e04b49a

Browse files
committed
[Editor] Settings view models
1 parent 7246fb7 commit e04b49a

7 files changed

Lines changed: 401 additions & 0 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
2+
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
3+
4+
namespace Stride.Core.Assets.Editor.Quantum.NodePresenters.Keys;
5+
6+
public static class SettingsData
7+
{
8+
public const string HasAcceptableValues = nameof(HasAcceptableValues);
9+
public const string AcceptableValues = nameof(AcceptableValues);
10+
public static readonly PropertyKey<bool> HasAcceptableValuesKey = new(HasAcceptableValues, typeof(SettingsData));
11+
public static readonly PropertyKey<IEnumerable<object>> AcceptableValuesKey = new(AcceptableValues, typeof(SettingsData));
12+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
2+
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
3+
4+
using Stride.Core.Assets.Editor.Quantum.NodePresenters.Keys;
5+
using Stride.Core.Assets.Editor.Settings;
6+
using Stride.Core.Presentation.Quantum.Presenters;
7+
8+
namespace Stride.Core.Assets.Editor.Quantum.NodePresenters.Updaters;
9+
10+
public sealed class SettingsPropertyNodeUpdater : NodePresenterUpdaterBase
11+
{
12+
public override void UpdateNode(INodePresenter node)
13+
{
14+
if (node.Value is PackageSettingsWrapper.SettingsKeyWrapper settingsKey)
15+
{
16+
var acceptableValues = settingsKey.Key.AcceptableValues.ToList();
17+
node.AttachedProperties.Add(SettingsData.HasAcceptableValuesKey, acceptableValues.Count > 0);
18+
node.AttachedProperties.Add(SettingsData.AcceptableValuesKey, acceptableValues);
19+
}
20+
}
21+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
2+
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
3+
4+
using Stride.Core.Settings;
5+
using Stride.Core.Translation;
6+
7+
namespace Stride.Core.Assets.Editor.Settings;
8+
9+
public static class EditorSettings
10+
{
11+
private static SettingsProfile? profile;
12+
public static SettingsContainer SettingsContainer { get; } = new();
13+
14+
// Categories
15+
public static readonly string Interface = Tr._p("Settings", "Interface");
16+
17+
static EditorSettings()
18+
{
19+
Language = new SettingsKey<string>("Interface/Language", SettingsContainer, "MachineDefault")
20+
{
21+
DisplayName = $"{Interface}/{Tr._p("Settings", "Language")}",
22+
};
23+
}
24+
25+
public static SettingsKey<string> Language { get; }
26+
27+
public static bool NeedRestart { get; set; }
28+
29+
public static void Initialize()
30+
{
31+
profile = SettingsContainer.LoadSettingsProfile(EditorPath.EditorConfigPath, true) ?? SettingsContainer.CreateSettingsProfile(true);
32+
33+
// Settings that requires a restart must register here
34+
Language.ChangesValidated += (_, _) => NeedRestart = true;
35+
}
36+
37+
public static void Save()
38+
{
39+
SettingsContainer.SaveSettingsProfile(profile!, EditorPath.EditorConfigPath);
40+
}
41+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
2+
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
3+
4+
using Stride.Core.Annotations;
5+
using Stride.Core.Settings;
6+
7+
namespace Stride.Core.Assets.Editor.Settings;
8+
9+
public sealed class PackageSettingsWrapper
10+
{
11+
/// <summary>
12+
/// An helper class to wrap a <see cref="SettingsKey"/> in the context of a given <see cref="SettingsProfile"/> into a simple object.
13+
/// </summary>
14+
public abstract class SettingsKeyWrapper
15+
{
16+
protected readonly SettingsProfile Profile;
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="SettingsKeyWrapper"/> class.
20+
/// </summary>
21+
/// <param name="key">The <see cref="SettingsKey"/> represented by this instance.</param>
22+
/// <param name="profile">The <see cref="SettingsProfile"/> in which this settings key is contained.</param>
23+
protected SettingsKeyWrapper(SettingsKey key, SettingsProfile profile)
24+
{
25+
Key = key;
26+
Profile = profile;
27+
}
28+
29+
/// <summary>
30+
/// The <see cref="SettingsKey"/> associated to this <see cref="SettingsKeyWrapper"/>.
31+
/// </summary>
32+
[DataMemberIgnore]
33+
public SettingsKey Key { get; private set; }
34+
35+
/// <summary>
36+
/// Creates a new instance of the correct implementation of <see cref="SettingsKeyWrapper"/> that matches the given settings key.
37+
/// </summary>
38+
/// <param name="key">The settings key for which to create a instance.</param>
39+
/// <param name="profile">The <see cref="SettingsProfile"/> in which this settings key is contained.</param>
40+
/// <param name="package">The package to mark dirty.</param>
41+
/// <returns>A new instance of the <see cref="SettingsKeyWrapper"/> class.</returns>
42+
public static SettingsKeyWrapper Create(SettingsKey key, SettingsProfile profile, Package? package = null)
43+
{
44+
return (SettingsKeyWrapper)Activator.CreateInstance(typeof(SettingsKeyWrapper<>).MakeGenericType(key.Type), key, profile, package)!;
45+
}
46+
}
47+
48+
/// <summary>
49+
/// An helper class to wrap a <see cref="SettingsKey{T}"/> in the context of a given <see cref="SettingsProfile"/> into a simple object.
50+
/// </summary>
51+
/// <typeparam name="T">The type of value in the <see cref="SettingsKey{T}"/>.</typeparam>
52+
public sealed class SettingsKeyWrapper<T> : SettingsKeyWrapper
53+
{
54+
private readonly SettingsKey<T> key;
55+
private readonly Package? package;
56+
57+
/// <summary>
58+
/// Initializes a new instance of the <see cref="SettingsKeyWrapper{T}"/> class.
59+
/// </summary>
60+
/// <param name="key">The <see cref="SettingsKey{T}"/> represented by this instance.</param>
61+
/// <param name="profile">The <see cref="SettingsProfile"/> in which this settings key is contained.</param>
62+
/// <param name="package"></param>
63+
public SettingsKeyWrapper(SettingsKey<T> key, SettingsProfile profile, Package? package)
64+
: base(key, profile)
65+
{
66+
this.key = key;
67+
this.package = package;
68+
}
69+
70+
/// <summary>
71+
/// The current value that can be pushed later to the <see cref="SettingsKey{T}"/> represented by this instance.
72+
/// </summary>
73+
[InlineProperty]
74+
public T TypedValue
75+
{
76+
get => GetValue();
77+
set
78+
{
79+
SetValue(value);
80+
if (package is not null)
81+
package.IsDirty = true;
82+
83+
}
84+
}
85+
86+
private void SetValue(T value)
87+
{
88+
key.SetValue(value, Profile);
89+
}
90+
91+
private T GetValue()
92+
{
93+
return key.GetValue(Profile, true);
94+
}
95+
}
96+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
2+
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
3+
4+
using Stride.Core.Annotations;
5+
using Stride.Core.Assets.Editor.Settings;
6+
using Stride.Core.Assets.Quantum;
7+
using Stride.Core.Presentation.Collections;
8+
using Stride.Core.Presentation.Core;
9+
using Stride.Core.Presentation.Quantum;
10+
using Stride.Core.Presentation.ViewModels;
11+
using Stride.Core.Quantum;
12+
using Stride.Core.Settings;
13+
14+
namespace Stride.Core.Assets.Editor.ViewModels;
15+
16+
public sealed class SettingsCategoryViewModel : DispatcherViewModel, IPropertyProviderViewModel, IComparable<SettingsCategoryViewModel>
17+
{
18+
private readonly AssetNodeContainer nodeContainer;
19+
private readonly SettingsContainerNode settingsList = [];
20+
21+
[MemberCollection(ReadOnly = true)]
22+
internal class SettingsContainerNode : Dictionary<string, object>;
23+
24+
public SettingsCategoryViewModel(IViewModelServiceProvider serviceProvider, SettingsProfile profile, string name, SettingsCategoryViewModel? parent, AssetNodeContainer nodeContainer)
25+
: base(serviceProvider)
26+
{
27+
this.nodeContainer = nodeContainer;
28+
Name = name;
29+
Parent = parent;
30+
31+
// Get all settings key and sort them by display name
32+
var settingsKeys = profile.Container.GetAllSettingsKeys();
33+
var settingsPath = Path;
34+
if (!settingsPath.EndsWith('/'))
35+
settingsPath += "/";
36+
int pathLength = settingsPath.Length;
37+
settingsKeys.Sort(new AnonymousComparer<SettingsKey>((x, y) => x?.DisplayName.CompareTo(y?.DisplayName) ?? 0));
38+
39+
// We keep settings that starts by settingsPath and does not contains subsequent slashes
40+
foreach (SettingsKey key in settingsKeys)
41+
{
42+
var displayName = key.DisplayName.ToString();
43+
if (displayName.StartsWith(settingsPath, StringComparison.Ordinal) && !displayName[pathLength..].Contains('/'))
44+
{
45+
var settingsObject = PackageSettingsWrapper.SettingsKeyWrapper.Create(key, profile);
46+
settingsList.Add(key.DisplayName.GetFileName()!, settingsObject);
47+
}
48+
}
49+
50+
}
51+
52+
public string Name { get; }
53+
54+
/// <summary>
55+
/// The parent of this category.
56+
/// </summary>
57+
public SettingsCategoryViewModel? Parent { get; }
58+
59+
/// <summary>
60+
/// The path of this category.
61+
/// </summary>
62+
public string Path => Parent is not null ? Parent.Path + Name + "/" : Name + "/";
63+
64+
/// <summary>
65+
/// Sub-categories contained in this category.
66+
/// </summary>
67+
public SortedObservableCollection<SettingsCategoryViewModel> SubCategories { get; } = [];
68+
69+
70+
/// <summary>
71+
/// A dictionary representing the different settings objects (<see cref="SettingsKey"/>) indexed by their display name.
72+
/// </summary>
73+
internal IReadOnlyDictionary<string, object> SettingsList => settingsList;
74+
75+
bool IPropertyProviderViewModel.CanProvidePropertiesViewModel => true;
76+
77+
IObjectNode IPropertyProviderViewModel.GetRootNode()
78+
{
79+
return nodeContainer.GetOrCreateNode(SettingsList);
80+
}
81+
82+
bool IPropertyProviderViewModel.ShouldConstructMember(IMemberNode member) => true;
83+
84+
bool IPropertyProviderViewModel.ShouldConstructItem(IObjectNode collection, NodeIndex index) => true;
85+
86+
int IComparable<SettingsCategoryViewModel>.CompareTo(SettingsCategoryViewModel? other)
87+
{
88+
if (ReferenceEquals(this, other))
89+
return 0;
90+
91+
if (other is null)
92+
return 1;
93+
94+
return string.Compare(Name, other.Name, StringComparison.Ordinal);
95+
}
96+
}

0 commit comments

Comments
 (0)