Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions sources/core/Stride.Core.Design/Settings/SettingsKey.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using Stride.Core.Annotations;
using Stride.Core.Extensions;
using Stride.Core.IO;
using Stride.Core.Yaml;
Expand Down Expand Up @@ -78,7 +77,7 @@ protected SettingsKey(UFile name, SettingsContainer container, Func<object> defa
/// <summary>
/// Gets or sets the description of this <see cref="SettingsKey"/>.
/// </summary>
public string Description { get; set; }
public string? Description { get; set; }

/// <summary>
/// Gets an enumeration of acceptable values for this <see cref="SettingsKey"/>.
Expand All @@ -94,7 +93,7 @@ protected SettingsKey(UFile name, SettingsContainer container, Func<object> defa
/// <summary>
/// Raised when the value of the settings key has been modified and the method <see cref="SettingsProfile.ValidateSettingsChanges"/> has been invoked.
/// </summary>
public event EventHandler<ChangesValidatedEventArgs> ChangesValidated;
public event EventHandler<ChangesValidatedEventArgs>? ChangesValidated;

/// <summary>
/// Converts a value of a different type to the type associated with this <see cref="SettingsKey"/>. If the conversion is not possible,
Expand Down Expand Up @@ -166,21 +165,20 @@ public SettingsKey(UFile name, SettingsContainer container, Func<object> default
}

/// <inheritdoc/>
[NotNull]
public override Type Type { get { return typeof(T); } }

/// <summary>
/// Gets the default value of this settings key.
/// </summary>
public T DefaultValue { get { return DefaultObjectValueCallback != null ? (T)DefaultObjectValueCallback() : (T)DefaultObjectValue; } }
public T DefaultValue { get { return DefaultObjectValueCallback?.Invoke() is T value ? value : (T)DefaultObjectValue; } }

/// <summary>
/// Gets or sets a function that returns an enumation of acceptable values for this <see cref="SettingsKey{T}"/>.
/// </summary>
public Func<IEnumerable<T>> GetAcceptableValues { get; set; }
public Func<IEnumerable<T>>? GetAcceptableValues { get; set; }

/// <inheritdoc/>
public override IEnumerable<object> AcceptableValues { get { return GetAcceptableValues != null ? (IEnumerable<object>)GetAcceptableValues() : Enumerable.Empty<object>(); } }
public override IEnumerable<object> AcceptableValues { get { return (IEnumerable<object>?)GetAcceptableValues?.Invoke() ?? []; } }

/// <summary>
/// Gets the value of this settings key in the given profile.
Expand All @@ -191,9 +189,8 @@ public SettingsKey(UFile name, SettingsContainer container, Func<object> default
/// <exception cref="KeyNotFoundException">No value can be found in the given profile matching this settings key.</exception>
public T GetValue(SettingsProfile profile, bool searchInParentProfile)
{
object value;
profile = ResolveProfile(profile);
if (profile.GetValue(Name, out value, searchInParentProfile, false))
if (profile.GetValue(Name, out var value, searchInParentProfile, false))
{
try
{
Expand Down
8 changes: 4 additions & 4 deletions sources/core/Stride.Core.Design/Settings/SettingsProfile.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using Stride.Core.Annotations;
using System.Diagnostics.CodeAnalysis;
using Stride.Core.IO;
using Stride.Core.Serialization;
using Stride.Core.Transactions;
Expand All @@ -20,7 +20,7 @@ public class SettingsProfile : IDisposable
private readonly SortedList<UFile, SettingsEntry> settings = [];
private readonly HashSet<UFile> modifiedSettings = [];
private readonly SettingsProfile? parentProfile;
private FileSystemWatcher fileWatcher;
private FileSystemWatcher? fileWatcher;
private UFile filePath;
private bool monitorFileModification;

Expand Down Expand Up @@ -53,7 +53,7 @@ internal SettingsProfile(SettingsContainer container, SettingsProfile? parentPro
/// <summary>
/// Raised when the file corresponding to this profile is modified on the disk, and <see cref="MonitorFileModification"/> is <c>true</c>.
/// </summary>
public event EventHandler<FileModifiedEventArgs> FileModified;
public event EventHandler<FileModifiedEventArgs>? FileModified;

/// <summary>
/// Gets the collection of <see cref="SettingsEntry"/> currently existing in this <see cref="SettingsProfile"/>.
Expand Down Expand Up @@ -208,7 +208,7 @@ internal void RegisterEntry(SettingsEntry entry)
/// <param name="searchInParent">Indicates whether to search in the parent profile, if the name is not found in this profile.</param>
/// <param name="createInCurrentProfile">If true, the list will be created in the current profile, from the value of its parent profile.</param>
/// <returns><c>true</c> if an entry matching the name is found, <c>false</c> otherwise.</returns>
internal bool GetValue(UFile name, out object? value, bool searchInParent, bool createInCurrentProfile)
internal bool GetValue(UFile name, [MaybeNullWhen(false)] out object value, bool searchInParent, bool createInCurrentProfile)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public sealed class NodePathToObject : OneWayValueConverter<NodePathToObject>
nameof(NodeViewModel.IsVisible) => node.IsVisible,
nameof(NodeViewModel.Name) => node.Name,
nameof(NodeViewModel.NodeValue) => node.NodeValue,
nameof(NodeViewModel.Parent) => node.Parent,
nameof(NodeViewModel.Root) => node.Root,
nameof(NodeViewModel.VisibleChildrenCount) => node.VisibleChildrenCount,
_ => throw new ArgumentException($"Unsupported {name} property.")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sd="http://schemas.stride3d.net/xaml/presentation"
xmlns:caecp="using:Stride.Core.Assets.Editor.Components.Properties"
xmlns:caev="using:Stride.Core.Assets.Editor.Avalonia.Views"
xmlns:cpqvm="using:Stride.Core.Presentation.Quantum.ViewModels">
<ControlTheme x:Key="PropertyExpanderTheme" TargetType="Expander">
<Setter Property="Padding" Value="10,0,0,0"/>
<Setter Property="Template">
<ControlTemplate TargetType="Expander">
<Border BorderThickness="0" Margin="0" Padding="0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
IsVisible="{TemplateBinding IsExpanded}"
Focusable="False"/>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type sd:PropertyView}" TargetType="sd:PropertyView" BasedOn="{StaticResource {x:Type ItemsControl}}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="Template">
<ControlTemplate TargetType="sd:PropertyView">
<Border Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<ScrollViewer AllowAutoHide="False"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" >
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type sd:PropertyViewItem}" TargetType="sd:PropertyViewItem"
x:DataType="cpqvm:NodeViewModel">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Increment" Value="12"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HeaderTemplate">
<TreeDataTemplate ItemsSource="{Binding Children, Mode=OneWay}" DataType="cpqvm:NodeViewModel">
<ContentPresenter Content="{Binding}"
ContentTemplate="{x:Static caev:PropertyViewHelper.HeaderProviders}"/>
</TreeDataTemplate>
</Setter>
<Setter Property="Template">
<ControlTemplate TargetType="sd:PropertyViewItem">
<Border BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}"
IsVisible="{Binding IsVisible}">
<DockPanel>
<Border DockPanel.Dock="Top" Background="{TemplateBinding Background}">
<ContentPresenter Name="PART_Header"
Content="{TemplateBinding HeaderedContentControl.Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalAlignment="{TemplateBinding Control.HorizontalAlignment}"
IsVisible="{sd:MultiBinding {Binding !$parent[sd:PropertyView].((caecp:SessionObjectPropertiesViewModel)DataContext).ShowOverridesOnly, FallbackValue={sd:True}},
{Binding [IsOverridden]},
{Binding ![HasBase]},
Converter={sd:OrMulti}}"/>
</Border>
<Expander IsEnabled="True" IsExpanded="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Stretch"
Theme="{StaticResource PropertyExpanderTheme}">
<StackPanel>
<ItemsPresenter Name="ItemsHost"/>
<ContentPresenter Name="PART_Footer"
Content="{TemplateBinding HeaderedContentControl.Header}"
ContentTemplate="{x:Static caev:PropertyViewHelper.FooterProviders}"
HorizontalAlignment="{TemplateBinding Control.HorizontalAlignment}"
IsVisible="{sd:MultiBinding {Binding !$parent[sd:PropertyView].((caecp:SessionObjectPropertiesViewModel)DataContext).ShowOverridesOnly, FallbackValue={sd:True}},
{Binding [IsOverridden]},
{Binding ![HasBase]},
Converter={sd:OrMulti}}"/>
</StackPanel>
</Expander>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,7 @@
</DataTemplate>
</caev:ContentReferenceTemplateProvider>

<!-- Provider for nulable struct -->
<!-- Provider for nullable struct -->
<caev:NullableTemplateProvider x:Key="NullableStructPropertyTemplateProvider" caev:PropertyViewHelper.TemplateCategory="PropertyEditor">
<DataTemplate DataType="cpqvm:NodeViewModel">
<DockPanel>
Expand Down Expand Up @@ -1523,6 +1523,16 @@
</DataTemplate>
</caev:NullableTemplateProvider>

<caev:SettingsStringFromAcceptableValuesTemplateProvider x:Key="StringFromAcceptableValuesEditorTemplate" OverrideRule="All" caev:PropertyViewHelper.TemplateCategory="PropertyEditor">
<DataTemplate DataType="cpqvm:NodeViewModel">
<Border>
<ComboBox Margin="2"
ItemsSource="{Binding ConverterParameter=Parent.[AcceptableValues], Converter={caec:NodePathToObject}}"
SelectedItem="{Binding NodeValue}"/>
</Border>
</DataTemplate>
</caev:SettingsStringFromAcceptableValuesTemplateProvider>

<!-- Provider for unloadable object -->
<caev:UnloadableObjectTemplateProvider x:Key="YamlProxyTemplateProvider" OverrideRule="All" caev:PropertyViewHelper.TemplateCategory="PropertyHeader">
<DataTemplate DataType="cpqvm:NodeViewModel">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using System.Linq;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Stride.Core.Presentation.Avalonia.Views;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using Stride.Core.Assets.Editor.Quantum.NodePresenters.Keys;
using Stride.Core.Presentation.Quantum.ViewModels;

namespace Stride.Core.Assets.Editor.Avalonia.Views;

public class SettingsStringFromAcceptableValuesTemplateProvider : NodeViewModelTemplateProvider
{
public override string Name => "StringFromAcceptableValues";

public override bool MatchNode(NodeViewModel node)
{
return node.Parent != null && (node.Parent.AssociatedData.TryGetValue(SettingsData.HasAcceptableValues, out var hasAcceptableValues) && (bool)hasAcceptableValues);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

namespace Stride.Core.Assets.Editor.Quantum.NodePresenters.Keys;

public static class SettingsData
{
public const string HasAcceptableValues = nameof(HasAcceptableValues);
public const string AcceptableValues = nameof(AcceptableValues);
public static readonly PropertyKey<bool> HasAcceptableValuesKey = new(HasAcceptableValues, typeof(SettingsData));
public static readonly PropertyKey<IEnumerable<object>> AcceptableValuesKey = new(AcceptableValues, typeof(SettingsData));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using Stride.Core.Assets.Editor.Quantum.NodePresenters.Keys;
using Stride.Core.Assets.Editor.Settings;
using Stride.Core.Presentation.Quantum.Presenters;

namespace Stride.Core.Assets.Editor.Quantum.NodePresenters.Updaters;

public sealed class SettingsPropertyNodeUpdater : NodePresenterUpdaterBase
{
public override void UpdateNode(INodePresenter node)
{
if (node.Value is PackageSettingsWrapper.SettingsKeyWrapper settingsKey)
{
var acceptableValues = settingsKey.Key.AcceptableValues.ToList();
node.AttachedProperties.Add(SettingsData.HasAcceptableValuesKey, acceptableValues.Count > 0);
node.AttachedProperties.Add(SettingsData.AcceptableValuesKey, acceptableValues);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using Stride.Core.Settings;
using Stride.Core.Translation;

namespace Stride.Core.Assets.Editor.Settings;

public static class EditorSettings
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why make it a singleton?
it forces to have only one setting, what about swapping between settings etc, no need for it to be singleton and static?

Copy link
Member Author

@Kryptos-FR Kryptos-FR Jun 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by swapping between settings? Can you explain the use cases?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have a folder with X different settings bundles and you should be able to swap between them
like there is no reason for it to be singleton, graphicscontext is also not a singleton for example

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you have different bundles of settings files for the editor? Those are basic settings like theme, language, list of recent projects, etc.

If you think about having several user profiles, that could be a new feature but it is out of scope for the rewrite effort. Here I'm just porting the existing settings system from the old editor, and such feature is not implemented there either. Feel free to open a feature request.

{
private static SettingsProfile? profile;
public static SettingsContainer SettingsContainer { get; } = new();

// Categories
public static readonly string Interface = Tr._p("Settings", "Interface");

static EditorSettings()
{
Language = new SettingsKey<string>("Interface/Language", SettingsContainer, "MachineDefault")
{
DisplayName = $"{Interface}/{Tr._p("Settings", "Language")}",
};
}

public static SettingsKey<string> Language { get; }

public static bool NeedRestart { get; set; }

public static void Initialize()
{
profile = SettingsContainer.LoadSettingsProfile(EditorPath.EditorConfigPath, true) ?? SettingsContainer.CreateSettingsProfile(true);

// Settings that requires a restart must register here
Language.ChangesValidated += (_, _) => NeedRestart = true;
}

public static void Save()
{
SettingsContainer.SaveSettingsProfile(profile!, EditorPath.EditorConfigPath);
}
}
Loading
Loading