Skip to content

Commit a93dd8b

Browse files
committed
[Editor] Settings window
1 parent dba9c65 commit a93dd8b

File tree

7 files changed

+232
-0
lines changed

7 files changed

+232
-0
lines changed

sources/editor/Stride.GameStudio.Avalonia/Services/EditorDialogService.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using Avalonia.Controls;
55
using Stride.Core.Assets.Editor.Services;
6+
using Stride.Core.Assets.Editor.Settings;
67
using Stride.Core.Assets.Editor.ViewModels;
78
using Stride.Core.Presentation.Avalonia.Services;
89
using Stride.Core.Presentation.Services;
@@ -89,4 +90,17 @@ public void ShowProgressWindow(WorkProgressViewModel workProgress)
8990
workProgress.NotifyWindowClosed();
9091
}
9192
}
93+
94+
public async Task ShowSettingsWindowAsync()
95+
{
96+
if (MainWindow is null) return;
97+
98+
await Dispatcher.InvokeTask(async () =>
99+
{
100+
await new SettingsWindow
101+
{
102+
DataContext = new SettingsViewModel(serviceProvider, EditorSettings.SettingsContainer.CurrentProfile)
103+
}.ShowDialog(MainWindow);
104+
});
105+
}
92106
}

sources/editor/Stride.GameStudio.Avalonia/ViewModels/MainViewModel.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public MainViewModel(IViewModelServiceProvider serviceProvider)
4343
ExitCommand = new AnonymousCommand(serviceProvider, OnExit, () => DialogService.HasMainWindow);
4444
OpenCommand = new AnonymousTaskCommand<UFile?>(serviceProvider, OnOpen);
4545
OpenDebugWindowCommand = new AnonymousTaskCommand(serviceProvider, OnOpenDebugWindow, () => DialogService.HasMainWindow);
46+
OpenSettingsWindowCommand = new AnonymousTaskCommand(serviceProvider, OnOpenSettingsWindow, () => DialogService.HasMainWindow);
4647
OpenWebPageCommand = new AnonymousTaskCommand<string>(serviceProvider, OnOpenWebPage);
4748

4849
Status = new StatusViewModel(ServiceProvider);
@@ -75,6 +76,8 @@ public string Title
7576

7677
public ICommandBase OpenDebugWindowCommand { get; }
7778

79+
public ICommandBase OpenSettingsWindowCommand { get; }
80+
7881
public ICommandBase OpenWebPageCommand { get; }
7982

8083
private EditorDialogService DialogService => ServiceProvider.Get<EditorDialogService>();
@@ -148,6 +151,11 @@ private async Task OnOpenDebugWindow()
148151
await DialogService.ShowDebugWindowAsync();
149152
}
150153

154+
private async Task OnOpenSettingsWindow()
155+
{
156+
await DialogService.ShowSettingsWindowAsync();
157+
}
158+
151159
private async Task OnOpenWebPage(string url)
152160
{
153161
try

sources/editor/Stride.GameStudio.Avalonia/Views/MainView.axaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
<MenuItem Header="{sd:LocalizeString Redo, Context=Menu}"
5353
Command="{Binding Session?.ActionHistory?.RedoCommand, TargetNullValue={x:Static cpc:DisabledCommand.Instance}}"
5454
HotKey="{x:Static gsvw:MainView.RedoGesture}" InputGesture="{Binding $self.HotKey, Mode=OneTime}"/>
55+
<Separator/>
56+
<MenuItem Header="{sd:LocalizeString _Settings..., Context=Menu}"
57+
Command="{Binding OpenSettingsWindowCommand}"
58+
IsEnabled="{Binding Session, Mode=OneWay, Converter={sd:ObjectToBool}}"/>
5559
</MenuItem>
5660
<!-- Help menu -->
5761
<MenuItem Header="{sd:LocalizeString _Help, Context=Menu}">
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<Window xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:sd="http://schemas.stride3d.net/xaml/presentation"
6+
xmlns:caeb="using:Stride.Core.Assets.Editor.Avalonia.Behaviors"
7+
xmlns:caevm="using:Stride.Core.Assets.Editor.ViewModels"
8+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
9+
x:Class="Stride.GameStudio.Avalonia.Views.SettingsWindow"
10+
x:DataType="caevm:SettingsViewModel"
11+
Title="SettingsWindow">
12+
<Window.Resources>
13+
<ResourceDictionary>
14+
<ResourceDictionary.MergedDictionaries>
15+
<ResourceInclude Source="avares://Stride.Core.Assets.Editor.Avalonia/Themes/PropertyViewTheme.axaml"/>
16+
</ResourceDictionary.MergedDictionaries>
17+
</ResourceDictionary>
18+
</Window.Resources>
19+
<DockPanel>
20+
<UniformGrid DockPanel.Dock="Bottom"
21+
Rows="1"
22+
HorizontalAlignment="Right"
23+
Margin="16">
24+
<Button Content="{sd:LocalizeString Save and close, Context=Button}"
25+
Margin="8,0,0,0" Padding="16,4">
26+
<Interaction.Behaviors>
27+
<sd:ButtonCloseWindowBehavior Command="{Binding ValidateChangesCommand}"/>
28+
</Interaction.Behaviors>
29+
</Button>
30+
<Button Content="{sd:LocalizeString Cancel, Context=Button}"
31+
Margin="8,0,0,0" Padding="16,4"
32+
IsCancel="True">
33+
<Interaction.Behaviors>
34+
<sd:ButtonCloseWindowBehavior Command="{Binding DiscardChangesCommand}"/>
35+
</Interaction.Behaviors>
36+
</Button>
37+
</UniformGrid>
38+
39+
<Grid ColumnDefinitions="*,Auto,2*">
40+
<TreeView Grid.Column="0"
41+
ItemsSource="{Binding Categories}"
42+
SelectedItem="{Binding SelectedCategory}">
43+
<Control.DataTemplates>
44+
<TreeDataTemplate DataType="{x:Type caevm:SettingsCategoryViewModel}"
45+
ItemsSource="{Binding SubCategories}">
46+
<StackPanel Orientation="Horizontal">
47+
<TextBlock Text="{Binding Name}" />
48+
</StackPanel>
49+
</TreeDataTemplate>
50+
</Control.DataTemplates>
51+
</TreeView>
52+
53+
<GridSplitter Grid.Column="1" ResizeBehavior="PreviousAndNext" VerticalAlignment="Stretch" />
54+
55+
<sd:PropertyView Grid.Column="2"
56+
ItemsSource="{Binding ViewModel?.RootNode.Children}">
57+
<Interaction.Behaviors>
58+
<caeb:PropertyViewAutoExpandNodesBehavior ViewModel="{Binding ViewModel, Mode=OneWay}" />
59+
</Interaction.Behaviors>
60+
</sd:PropertyView>
61+
</Grid>
62+
</DockPanel>
63+
</Window>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 Avalonia.Controls;
5+
6+
namespace Stride.GameStudio.Avalonia.Views;
7+
8+
public partial class SettingsWindow : Window
9+
{
10+
public SettingsWindow()
11+
{
12+
InitializeComponent();
13+
}
14+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 Avalonia.Controls;
5+
using Avalonia.Interactivity;
6+
7+
namespace Stride.Core.Presentation.Avalonia.Behaviors;
8+
9+
public sealed class ButtonCloseWindowBehavior : CloseWindowBehavior<Button>
10+
{
11+
/// <inheritdoc/>
12+
protected override void OnAttached()
13+
{
14+
base.OnAttached();
15+
AssociatedObject!.Click += ButtonClicked;
16+
}
17+
18+
/// <inheritdoc/>
19+
protected override void OnDetaching()
20+
{
21+
AssociatedObject!.Click -= ButtonClicked;
22+
base.OnDetaching();
23+
}
24+
25+
/// <summary>
26+
/// Raised when the associated button is clicked. Close the containing window
27+
/// </summary>
28+
private void ButtonClicked(object? sender, RoutedEventArgs e)
29+
{
30+
CloseWindow();
31+
}
32+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 System.Diagnostics;
5+
using System.Windows.Input;
6+
using Avalonia;
7+
using Avalonia.Controls;
8+
using Avalonia.Input;
9+
using Avalonia.Xaml.Interactivity;
10+
11+
namespace Stride.Core.Presentation.Avalonia.Behaviors;
12+
13+
public abstract class CloseWindowBehavior<T> : StyledElementBehavior<T>
14+
where T : Visual
15+
{
16+
public static readonly StyledProperty<ICommand?> CommandProperty =
17+
AvaloniaProperty.Register<CloseWindowBehavior<T>, ICommand?>(nameof(Command));
18+
19+
public static readonly StyledProperty<object?> CommandParameterProperty =
20+
AvaloniaProperty.Register<CloseWindowBehavior<T>, object?>(nameof(CommandParameter));
21+
22+
static CloseWindowBehavior()
23+
{
24+
CommandProperty.Changed.AddClassHandler<CloseWindowBehavior<T>, ICommand?>(OnCommandChanged);
25+
CommandParameterProperty.Changed.AddClassHandler<CloseWindowBehavior<T>, object?>(OnCommandParameterChanged);
26+
}
27+
28+
public ICommand? Command
29+
{
30+
get => GetValue(CommandProperty);
31+
set => SetValue(CommandProperty, value);
32+
}
33+
34+
public object? CommandParameter
35+
{
36+
get => GetValue(CommandParameterProperty);
37+
set => SetValue(CommandParameterProperty, value);
38+
}
39+
40+
/// <inheritdoc/>
41+
protected override void OnAttached()
42+
{
43+
base.OnAttached();
44+
if (Command is not null)
45+
{
46+
UpdateIsEnabled();
47+
}
48+
}
49+
50+
protected void CloseWindow()
51+
{
52+
if (Command is not null && Command.CanExecute(CommandParameter))
53+
{
54+
Command.Execute(CommandParameter);
55+
}
56+
57+
if (TopLevel.GetTopLevel(AssociatedObject) is not Window window)
58+
{
59+
throw new InvalidOperationException("The button attached to this behavior is not in a window");
60+
}
61+
62+
window.Close();
63+
}
64+
65+
private static void OnCommandChanged(CloseWindowBehavior<T> sender, AvaloniaPropertyChangedEventArgs<ICommand?> e)
66+
{
67+
if (e.OldValue.Value is { } oldCommand)
68+
{
69+
oldCommand.CanExecuteChanged -= CommandCanExecuteChanged;
70+
}
71+
if (e.NewValue.Value is { } newCommand)
72+
{
73+
newCommand.CanExecuteChanged += CommandCanExecuteChanged;
74+
}
75+
return;
76+
77+
void CommandCanExecuteChanged(object? _, EventArgs __)
78+
{
79+
sender.UpdateIsEnabled();
80+
}
81+
}
82+
83+
private static void OnCommandParameterChanged(CloseWindowBehavior<T> sender, AvaloniaPropertyChangedEventArgs<object?> e)
84+
{
85+
if (sender.Command is not null)
86+
{
87+
sender.UpdateIsEnabled();
88+
}
89+
}
90+
91+
private void UpdateIsEnabled()
92+
{
93+
Debug.Assert(Command is not null);
94+
95+
AssociatedObject?.SetCurrentValue(InputElement.IsEnabledProperty, Command.CanExecute(CommandParameter));
96+
}
97+
}

0 commit comments

Comments
 (0)