1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00
Artemis/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs
Robert 543b62a715 Profile editor - Added back hotkeys for the tools
Profile editor - Fixed hotkeys not always working
2023-10-07 20:12:35 +02:00

187 lines
6.9 KiB
C#

using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Reactive;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using System.Timers;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Avalonia;
using DynamicData;
using ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting;
public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase
{
private readonly INodeEditorService _nodeEditorService;
private readonly INodeService _nodeService;
private readonly IProfileService _profileService;
private readonly ISettingsService _settingsService;
private readonly IWindowService _windowService;
private bool _pauseUpdate;
private ObservableAsPropertyHelper<bool>? _keyBindingsEnabled;
public NodeScriptWindowViewModel(NodeScript nodeScript,
INodeService nodeService,
INodeEditorService nodeEditorService,
INodeVmFactory vmFactory,
ISettingsService settingsService,
IProfileService profileService,
IWindowService windowService) : base(nodeScript)
{
NodeScript = nodeScript;
NodeScriptViewModel = vmFactory.NodeScriptViewModel(NodeScript, false);
OpenUri = ReactiveCommand.Create<string>(s => Process.Start(new ProcessStartInfo(s) {UseShellExecute = true, Verb = "open"}));
ToggleBooleanSetting = ReactiveCommand.Create<PluginSetting<bool>>(ExecuteToggleBooleanSetting);
History = nodeEditorService.GetHistory(nodeScript);
_nodeService = nodeService;
_nodeEditorService = nodeEditorService;
_settingsService = settingsService;
_profileService = profileService;
_windowService = windowService;
CreateNode = ReactiveCommand.Create<NodeData>(ExecuteCreateNode);
AutoArrange = ReactiveCommand.CreateFromTask(ExecuteAutoArrange, this.WhenAnyValue(vm => vm.KeyBindingsEnabled));
Export = ReactiveCommand.CreateFromTask(ExecuteExport);
Import = ReactiveCommand.CreateFromTask(ExecuteImport);
SourceList<NodeData> nodeSourceList = new();
nodeSourceList.AddRange(nodeService.AvailableNodes);
nodeSourceList.Connect()
.GroupWithImmutableState(n => n.Category)
.Transform(c => new NodeMenuItemViewModel(CreateNode, c))
.Bind(out ReadOnlyObservableCollection<NodeMenuItemViewModel> categories)
.Subscribe();
Categories = categories;
this.WhenActivated(d =>
{
_keyBindingsEnabled = Shared.UI.CurrentKeyBindingsEnabled.ToProperty(this, vm => vm.KeyBindingsEnabled).DisposeWith(d);
Timer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000));
Timer saveTimer = new(TimeSpan.FromMinutes(2));
updateTimer.Elapsed += (_, _) => Update();
saveTimer.Elapsed += (_, _) => Save();
updateTimer.Start();
saveTimer.Start();
updateTimer.DisposeWith(d);
saveTimer.DisposeWith(d);
});
}
public NodeScriptViewModel NodeScriptViewModel { get; set; }
public NodeEditorHistory History { get; }
public ReactiveCommand<PluginSetting<bool>, Unit> ToggleBooleanSetting { get; set; }
public ReactiveCommand<string, Unit> OpenUri { get; set; }
public ReadOnlyObservableCollection<NodeMenuItemViewModel> Categories { get; }
public ReactiveCommand<NodeData, Unit> CreateNode { get; }
public ReactiveCommand<Unit, Unit> AutoArrange { get; }
public ReactiveCommand<Unit, Unit> Export { get; }
public ReactiveCommand<Unit, Unit> Import { get; }
public bool KeyBindingsEnabled => _keyBindingsEnabled?.Value ?? false;
public PluginSetting<bool> ShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
public PluginSetting<bool> ShowFullPaths => _settingsService.GetSetting("ProfileEditor.ShowFullPaths", false);
public PluginSetting<bool> AlwaysShowValues => _settingsService.GetSetting("ProfileEditor.AlwaysShowValues", true);
private void ExecuteToggleBooleanSetting(PluginSetting<bool> setting)
{
setting.Value = !setting.Value;
setting.Save();
}
private void ExecuteCreateNode(NodeData data)
{
// Place the node in the top-left of the canvas, keeping in mind the user may have panned
INode node = data.CreateNode(NodeScript, null);
Point point = new Point(0, 0).Transform(NodeScriptViewModel.PanMatrix.Invert());
node.X = Math.Round(point.X / 10d, 0, MidpointRounding.AwayFromZero) * 10d + 20;
node.Y = Math.Round(point.Y / 10d, 0, MidpointRounding.AwayFromZero) * 10d + 20;
_nodeEditorService.ExecuteCommand(NodeScript, new AddNode(NodeScript, node));
}
private async Task ExecuteAutoArrange()
{
try
{
if (!NodeScript.ExitNodeConnected)
{
await _windowService.ShowConfirmContentDialog("Cannot auto-arrange", "The exit node must be connected in order to perform auto-arrange.", "Close", null);
return;
}
_pauseUpdate = true;
_nodeEditorService.ExecuteCommand(NodeScript, new OrganizeScript(NodeScript));
await Task.Delay(200);
NodeScriptViewModel.RequestAutoFit();
}
finally
{
_pauseUpdate = false;
}
}
private async Task ExecuteExport()
{
// Might not cover everything but then the dialog will complain and that's good enough
string? result = await _windowService.CreateSaveFileDialog()
.HavingFilter(f => f.WithExtension("json").WithName("Artemis node script"))
.ShowAsync();
if (result == null)
return;
string json = _nodeService.ExportScript(NodeScript);
await File.WriteAllTextAsync(result, json);
}
private async Task ExecuteImport()
{
string[]? result = await _windowService.CreateOpenFileDialog()
.HavingFilter(f => f.WithExtension("json").WithName("Artemis node script"))
.ShowAsync();
if (result == null || result.Length == 0)
return;
try
{
_pauseUpdate = true;
string json = await File.ReadAllTextAsync(result[0]);
_nodeService.ImportScript(json, NodeScript);
History.Clear();
await Task.Delay(200);
NodeScriptViewModel.RequestAutoFit();
}
finally
{
_pauseUpdate = false;
}
}
private void Update()
{
if (!_pauseUpdate)
NodeScript.Run();
}
private void Save()
{
if (NodeScript.Context is Profile profile)
_profileService.SaveProfile(profile, true);
}
}