diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs index 8875fbbc2..ba6caae14 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs @@ -55,6 +55,11 @@ public interface IProfileEditorService : IArtemisSharedUIService /// Gets an observable of the suspended keybindings state. /// IObservable SuspendedKeybindings { get; } + + /// + /// Gets an observable of the suspended keybindings state. + /// + IObservable FocusMode { get; } /// /// Gets an observable read only collection of all available editor tools. @@ -108,6 +113,12 @@ public interface IProfileEditorService : IArtemisSharedUIService /// The new suspended state. void ChangeSuspendedKeybindings(bool suspend); + /// + /// Changes the current focus mode. + /// + /// The new focus mode. + void ChangeFocusMode(ProfileEditorFocusMode focusMode); + /// /// Selects the provided keyframe. /// diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorFocusMode.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorFocusMode.cs new file mode 100644 index 000000000..1d1f3c979 --- /dev/null +++ b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorFocusMode.cs @@ -0,0 +1,22 @@ +namespace Artemis.UI.Shared.Services.ProfileEditor; + +/// +/// Represents a mode of focus in the editor. +/// +public enum ProfileEditorFocusMode +{ + /// + /// Disable focusing. + /// + None, + + /// + /// Focus the folder of the current element. + /// + Folder, + + /// + /// Focus the current element. + /// + Selection +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs index beba258d7..f2af44d46 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs @@ -31,12 +31,12 @@ internal class ProfileEditorService : IProfileEditorService private readonly IProfileService _profileService; private readonly BehaviorSubject _suspendedEditingSubject = new(false); private readonly BehaviorSubject _suspendedKeybindingsSubject = new(false); + private readonly BehaviorSubject _focusModeSubject = new(ProfileEditorFocusMode.None); private readonly BehaviorSubject _timeSubject = new(TimeSpan.Zero); private readonly SourceList _tools; private readonly SourceList _selectedKeyframes; private readonly IWindowService _windowService; private ProfileEditorCommandScope? _profileEditorHistoryScope; - private readonly PluginSetting _focusSelectedLayer; public ProfileEditorService(ILogger logger, IProfileService profileService, @@ -44,8 +44,7 @@ internal class ProfileEditorService : IProfileEditorService IRgbService rgbService, ILayerBrushService layerBrushService, IMainWindowService mainWindowService, - IWindowService windowService, - ISettingsService settingsService) + IWindowService windowService) { _logger = logger; _profileService = profileService; @@ -53,8 +52,7 @@ internal class ProfileEditorService : IProfileEditorService _rgbService = rgbService; _layerBrushService = layerBrushService; _windowService = windowService; - _focusSelectedLayer = settingsService.GetSetting("ProfileEditor.FocusSelectedLayer", false); - + _tools = new SourceList(); _selectedKeyframes = new SourceList(); _tools.Connect().AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Subscribe(OnToolSelected); @@ -70,6 +68,8 @@ internal class ProfileEditorService : IProfileEditorService SuspendedEditing = _suspendedEditingSubject.AsObservable(); SuspendedKeybindings = _suspendedKeybindingsSubject.AsObservable(); PixelsPerSecond = _pixelsPerSecondSubject.AsObservable(); + FocusMode = _focusModeSubject.AsObservable(); + Tools = tools; SelectedKeyframes = selectedKeyframes; @@ -88,7 +88,6 @@ internal class ProfileEditorService : IProfileEditorService // When the main window closes, stop editing mainWindowService.MainWindowClosed += (_, _) => ChangeCurrentProfileConfiguration(null); - _focusSelectedLayer.SettingChanged += FocusSelectedLayerOnSettingChanged; } public IObservable ProfileConfiguration { get; } @@ -100,6 +99,7 @@ internal class ProfileEditorService : IProfileEditorService public IObservable Time { get; } public IObservable Playing { get; } public IObservable PixelsPerSecond { get; } + public IObservable FocusMode { get; } public ReadOnlyObservableCollection Tools { get; } public ReadOnlyObservableCollection SelectedKeyframes { get; } @@ -156,7 +156,8 @@ internal class ProfileEditorService : IProfileEditorService { _selectedKeyframes.Clear(); _profileElementSubject.OnNext(renderProfileElement); - _profileService.EditorFocus = _focusSelectedLayer.Value ? renderProfileElement : null; + + ApplyFocusMode(); ChangeCurrentLayerProperty(null); } @@ -190,6 +191,8 @@ internal class ProfileEditorService : IProfileEditorService _profileService.RenderForEditor = true; Tick(_timeSubject.Value); } + + ApplyFocusMode(); } public void ChangeSuspendedKeybindings(bool suspend) @@ -200,6 +203,15 @@ internal class ProfileEditorService : IProfileEditorService _suspendedKeybindingsSubject.OnNext(suspend); } + public void ChangeFocusMode(ProfileEditorFocusMode focusMode) + { + if (_focusModeSubject.Value == focusMode) + return; + + _focusModeSubject.OnNext(focusMode); + ApplyFocusMode(); + } + public void SelectKeyframe(ILayerPropertyKeyframe? keyframe, bool expand, bool toggle) { if (keyframe == null) @@ -509,9 +521,18 @@ internal class ProfileEditorService : IProfileEditorService TickProfileElement(child, time); } } - - private void FocusSelectedLayerOnSettingChanged(object? sender, EventArgs e) + + private void ApplyFocusMode() { - _profileService.EditorFocus = _focusSelectedLayer.Value ? _profileElementSubject.Value : null; + if (_suspendedEditingSubject.Value) + _profileService.EditorFocus = null; + + _profileService.EditorFocus = _focusModeSubject.Value switch + { + ProfileEditorFocusMode.None => null, + ProfileEditorFocusMode.Folder => _profileElementSubject.Value?.Parent, + ProfileEditorFocusMode.Selection => _profileElementSubject.Value, + _ => _profileService.EditorFocus + }; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarView.axaml index 565fa7914..7948b2d90 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarView.axaml +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarView.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:menuBar="clr-namespace:Artemis.UI.Screens.ProfileEditor.MenuBar" + xmlns:profileEditor="clr-namespace:Artemis.UI.Shared.Services.ProfileEditor;assembly=Artemis.UI.Shared" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.ProfileEditor.MenuBar.MenuBarView" x:DataType="menuBar:MenuBarViewModel"> @@ -97,8 +98,8 @@ - @@ -108,12 +109,28 @@ - - - - + + + + + + + + + + + + + + + + ? _profileConfiguration; private ObservableAsPropertyHelper? _profileElement; private ObservableAsPropertyHelper? _suspendedEditing; + private ObservableAsPropertyHelper? _focusNone; + private ObservableAsPropertyHelper? _focusFolder; + private ObservableAsPropertyHelper? _focusSelection; public MenuBarViewModel(ILogger logger, IProfileEditorService profileEditorService, IProfileService profileService, ISettingsService settingsService, IWindowService windowService) { @@ -50,6 +53,10 @@ public class MenuBarViewModel : ActivatableViewModelBase .Switch() .ToProperty(this, vm => vm.IsSuspended) .DisposeWith(d); + + _focusNone = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.None).ToProperty(this, vm => vm.FocusNone).DisposeWith(d); + _focusFolder = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Folder).ToProperty(this, vm => vm.FocusFolder).DisposeWith(d); + _focusSelection = profileEditorService.FocusMode.Select(f => f == ProfileEditorFocusMode.Selection).ToProperty(this, vm => vm.FocusSelection).DisposeWith(d); }); AddFolder = ReactiveCommand.Create(ExecuteAddFolder); @@ -62,8 +69,10 @@ public class MenuBarViewModel : ActivatableViewModelBase ExportProfile = ReactiveCommand.CreateFromTask(ExecuteExportProfile, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null)); DuplicateProfile = ReactiveCommand.Create(ExecuteDuplicateProfile, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null)); ToggleSuspendedEditing = ReactiveCommand.Create(ExecuteToggleSuspendedEditing); - ToggleBooleanSetting = ReactiveCommand.Create>(ExecuteToggleBooleanSetting); OpenUri = ReactiveCommand.Create(s => Process.Start(new ProcessStartInfo(s) {UseShellExecute = true, Verb = "open"})); + ToggleBooleanSetting = ReactiveCommand.Create>(ExecuteToggleBooleanSetting); + CycleFocusMode = ReactiveCommand.Create(ExecuteCycleFocusMode); + ChangeFocusMode = ReactiveCommand.Create(ExecuteChangeFocusMode); } public ReactiveCommand AddFolder { get; } @@ -78,18 +87,23 @@ public class MenuBarViewModel : ActivatableViewModelBase public ReactiveCommand, Unit> ToggleBooleanSetting { get; } public ReactiveCommand OpenUri { get; } public ReactiveCommand ToggleSuspendedEditing { get; } - + public ReactiveCommand CycleFocusMode { get; } + public ReactiveCommand ChangeFocusMode { get; } + public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value; public RenderProfileElement? ProfileElement => _profileElement?.Value; public bool IsSuspended => _isSuspended?.Value ?? false; public bool SuspendedEditing => _suspendedEditing?.Value ?? false; + public bool FocusNone => _focusNone?.Value ?? false; + public bool FocusFolder => _focusFolder?.Value ?? false; + public bool FocusSelection => _focusSelection?.Value ?? false; public PluginSetting AutoSuspend => _settingsService.GetSetting("ProfileEditor.AutoSuspend", true); - public PluginSetting FocusSelectedLayer => _settingsService.GetSetting("ProfileEditor.FocusSelectedLayer", false); public PluginSetting ShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false); public PluginSetting ShowFullPaths => _settingsService.GetSetting("ProfileEditor.ShowFullPaths", false); public PluginSetting AlwaysShowValues => _settingsService.GetSetting("ProfileEditor.AlwaysShowValues", true); public PluginSetting AlwaysApplyDataBindings => _settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", false); + public PluginSetting FocusMode => _settingsService.GetSetting("ProfileEditor.FocusMode", ProfileEditorFocusMode.Folder); public ProfileEditorHistory? History { @@ -125,7 +139,7 @@ public class MenuBarViewModel : ActivatableViewModelBase ("profileConfiguration", ProfileConfiguration) ); } - + private async Task ExecuteViewScripts() { if (ProfileConfiguration?.Profile == null) @@ -218,4 +232,24 @@ public class MenuBarViewModel : ActivatableViewModelBase setting.Value = !setting.Value; setting.Save(); } + + private void ExecuteCycleFocusMode() + { + if (FocusMode.Value == ProfileEditorFocusMode.None) + FocusMode.Value = ProfileEditorFocusMode.Folder; + else if (FocusMode.Value == ProfileEditorFocusMode.Folder) + FocusMode.Value = ProfileEditorFocusMode.Selection; + else if (FocusMode.Value == ProfileEditorFocusMode.Selection) + FocusMode.Value = ProfileEditorFocusMode.None; + + FocusMode.Save(); + _profileEditorService.ChangeFocusMode(FocusMode.Value); + } + + private void ExecuteChangeFocusMode(ProfileEditorFocusMode focusMode) + { + FocusMode.Value = focusMode; + FocusMode.Save(); + _profileEditorService.ChangeFocusMode(FocusMode.Value); + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml index 9164c768b..bda0e5b5d 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml @@ -90,8 +90,8 @@ - @@ -152,14 +152,25 @@ - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs index 111df1c7c..48d2d1248 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reactive.Disposables; using System.Threading.Tasks; using Artemis.Core; +using Artemis.Core.Services; using Artemis.UI.Ninject.Factories; using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services.ProfileEditor; @@ -17,7 +18,7 @@ public class ProfileTreeViewModel : TreeItemViewModel private readonly IProfileEditorService _profileEditorService; private TreeItemViewModel? _selectedChild; - public ProfileTreeViewModel(IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory) + public ProfileTreeViewModel(IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, ISettingsService settingsService) : base(null, null, windowService, profileEditorService, profileEditorVmFactory) { _profileEditorService = profileEditorService; @@ -44,8 +45,11 @@ public class ProfileTreeViewModel : TreeItemViewModel if (model?.ProfileElement is RenderProfileElement renderProfileElement) profileEditorService.ChangeCurrentProfileElement(renderProfileElement); }); + + FocusMode = settingsService.GetSetting("ProfileEditor.FocusMode", ProfileEditorFocusMode.Folder); } + public PluginSetting FocusMode { get; } public TreeItemViewModel? SelectedChild { get => _selectedChild; diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml index d5fbb3307..3f857f1d2 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml +++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml @@ -28,6 +28,7 @@ +