mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Profile editor - Added layer renaming
Profile editor - Added undo/redo notification (for now)
This commit is contained in:
parent
0b905cca2e
commit
5802855eb1
@ -8,233 +8,236 @@ using FluentAvalonia.UI.Controls;
|
|||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using Button = Avalonia.Controls.Button;
|
using Button = Avalonia.Controls.Button;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Services.Builders
|
namespace Artemis.UI.Shared.Services.Builders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a builder that can be used to create notifications.
|
||||||
|
/// </summary>
|
||||||
|
public class NotificationBuilder
|
||||||
|
{
|
||||||
|
private readonly InfoBar _infoBar;
|
||||||
|
private readonly Window _parent;
|
||||||
|
private TimeSpan _timeout = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="NotificationBuilder" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">The parent window that will host the notification.</param>
|
||||||
|
public NotificationBuilder(Window parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
_infoBar = new InfoBar
|
||||||
|
{
|
||||||
|
Classes = Classes.Parse("notification-info-bar"),
|
||||||
|
VerticalAlignment = VerticalAlignment.Bottom,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Right
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the title of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The new title.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
||||||
|
public NotificationBuilder WithTitle(string? title)
|
||||||
|
{
|
||||||
|
_infoBar.Title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the message of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">The new message.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
||||||
|
public NotificationBuilder WithMessage(string? content)
|
||||||
|
{
|
||||||
|
_infoBar.Message = content;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the timeout of the notification after which it disappears automatically.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeout">The timeout of the notification after which it disappears automatically.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
||||||
|
public NotificationBuilder WithTimeout(TimeSpan timeout)
|
||||||
|
{
|
||||||
|
_timeout = timeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the vertical position of the notification inside the parent window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">The vertical position of the notification inside the parent window.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
||||||
|
public NotificationBuilder WithVerticalPosition(VerticalAlignment position)
|
||||||
|
{
|
||||||
|
_infoBar.VerticalAlignment = position;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the horizontal position of the notification inside the parent window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">The horizontal position of the notification inside the parent window.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
||||||
|
public NotificationBuilder WithHorizontalPosition(HorizontalAlignment position)
|
||||||
|
{
|
||||||
|
_infoBar.HorizontalAlignment = position;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the severity (color) of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="severity">The severity (color) of the notification.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
||||||
|
public NotificationBuilder WithSeverity(NotificationSeverity severity)
|
||||||
|
{
|
||||||
|
_infoBar.Severity = (InfoBarSeverity) severity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the action button of the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configure">An action to configure the button.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
||||||
|
public NotificationBuilder HavingButton(Action<NotificationButtonBuilder> configure)
|
||||||
|
{
|
||||||
|
NotificationButtonBuilder builder = new();
|
||||||
|
configure(builder);
|
||||||
|
_infoBar.ActionButton = builder.Build();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An action that can be called to hide the notification.</returns>
|
||||||
|
/// <exception cref="ArtemisSharedUIException" />
|
||||||
|
public Action Show()
|
||||||
|
{
|
||||||
|
if (_parent.Content is not Panel panel)
|
||||||
|
throw new ArtemisSharedUIException("Can't display a notification on a window without a panel at its root.");
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
panel.Children.Add(_infoBar);
|
||||||
|
_infoBar.Closed += InfoBarOnClosed;
|
||||||
|
_infoBar.IsOpen = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(_timeout);
|
||||||
|
Dispatcher.UIThread.Post(() => _infoBar.IsOpen = false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => Dispatcher.UIThread.Post(() => _infoBar.IsOpen = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InfoBarOnClosed(InfoBar sender, InfoBarClosedEventArgs args)
|
||||||
|
{
|
||||||
|
_infoBar.Closed -= InfoBarOnClosed;
|
||||||
|
if (_parent.Content is not Panel panel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
panel.Children.Remove(_infoBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a builder that can be used to create buttons inside notifications.
|
||||||
|
/// </summary>
|
||||||
|
public class NotificationButtonBuilder
|
||||||
|
{
|
||||||
|
private Action? _action;
|
||||||
|
private ICommand? _command;
|
||||||
|
private object? _commandParameter;
|
||||||
|
private string _text = "Text";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes text message of the button.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The new text.</param>
|
||||||
|
/// <returns>The notification builder that can be used to further build the button.</returns>
|
||||||
|
public NotificationButtonBuilder WithText(string text)
|
||||||
|
{
|
||||||
|
_text = text;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes action that is called when the button is clicked.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">The action to call when the button is clicked.</param>
|
||||||
|
/// <returns>The builder that can be used to further build the button.</returns>
|
||||||
|
public NotificationButtonBuilder WithAction(Action action)
|
||||||
|
{
|
||||||
|
_command = null;
|
||||||
|
_action = action;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes command that is called when the button is clicked.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The command to call when the button is clicked.</param>
|
||||||
|
/// <returns>The builder that can be used to further build the button.</returns>
|
||||||
|
public NotificationButtonBuilder WithCommand(ICommand command)
|
||||||
|
{
|
||||||
|
_action = null;
|
||||||
|
_command = command;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes parameter of the command that is called when the button is clicked.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commandParameter">The parameter of the command to call when the button is clicked.</param>
|
||||||
|
/// <returns>The builder that can be used to further build the button.</returns>
|
||||||
|
public NotificationButtonBuilder WithCommandParameter(object? commandParameter)
|
||||||
|
{
|
||||||
|
_commandParameter = commandParameter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IControl Build()
|
||||||
|
{
|
||||||
|
if (_action != null)
|
||||||
|
return new Button {Content = _text, Command = ReactiveCommand.Create(() => _action())};
|
||||||
|
if (_command != null)
|
||||||
|
return new Button {Content = _text, Command = _command, CommandParameter = _commandParameter};
|
||||||
|
return new Button {Content = _text};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a severity of a notification.
|
||||||
|
/// </summary>
|
||||||
|
public enum NotificationSeverity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a builder that can be used to create notifications.
|
/// A severity for informational messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NotificationBuilder
|
Informational,
|
||||||
{
|
|
||||||
private readonly InfoBar _infoBar;
|
|
||||||
private readonly Window _parent;
|
|
||||||
private TimeSpan _timeout = TimeSpan.FromSeconds(5);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="NotificationBuilder" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">The parent window that will host the notification.</param>
|
|
||||||
public NotificationBuilder(Window parent)
|
|
||||||
{
|
|
||||||
_parent = parent;
|
|
||||||
_infoBar = new InfoBar
|
|
||||||
{
|
|
||||||
Classes = Classes.Parse("notification-info-bar"),
|
|
||||||
VerticalAlignment = VerticalAlignment.Bottom,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Right
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the title of the notification.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="title">The new title.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
|
||||||
public NotificationBuilder WithTitle(string? title)
|
|
||||||
{
|
|
||||||
_infoBar.Title = title;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the message of the notification.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="content">The new message.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
|
||||||
public NotificationBuilder WithMessage(string? content)
|
|
||||||
{
|
|
||||||
_infoBar.Message = content;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the timeout of the notification after which it disappears automatically.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="timeout">The timeout of the notification after which it disappears automatically.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
|
||||||
public NotificationBuilder WithTimeout(TimeSpan timeout)
|
|
||||||
{
|
|
||||||
_timeout = timeout;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the vertical position of the notification inside the parent window.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="position">The vertical position of the notification inside the parent window.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
|
||||||
public NotificationBuilder WithVerticalPosition(VerticalAlignment position)
|
|
||||||
{
|
|
||||||
_infoBar.VerticalAlignment = position;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the horizontal position of the notification inside the parent window.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="position">The horizontal position of the notification inside the parent window.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
|
||||||
public NotificationBuilder WithHorizontalPosition(HorizontalAlignment position)
|
|
||||||
{
|
|
||||||
_infoBar.HorizontalAlignment = position;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the severity (color) of the notification.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="severity">The severity (color) of the notification.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
|
||||||
public NotificationBuilder WithSeverity(NotificationSeverity severity)
|
|
||||||
{
|
|
||||||
_infoBar.Severity = (InfoBarSeverity) severity;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the action button of the notification.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configure">An action to configure the button.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the notification.</returns>
|
|
||||||
public NotificationBuilder HavingButton(Action<NotificationButtonBuilder> configure)
|
|
||||||
{
|
|
||||||
NotificationButtonBuilder builder = new();
|
|
||||||
configure(builder);
|
|
||||||
_infoBar.ActionButton = builder.Build();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows the notification.
|
|
||||||
/// </summary>
|
|
||||||
public void Show()
|
|
||||||
{
|
|
||||||
if (_parent.Content is not Panel panel)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
|
||||||
panel.Children.Add(_infoBar);
|
|
||||||
_infoBar.Closed += InfoBarOnClosed;
|
|
||||||
_infoBar.IsOpen = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(_timeout);
|
|
||||||
Dispatcher.UIThread.Post(() => _infoBar.IsOpen = false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InfoBarOnClosed(InfoBar sender, InfoBarClosedEventArgs args)
|
|
||||||
{
|
|
||||||
_infoBar.Closed -= InfoBarOnClosed;
|
|
||||||
if (_parent.Content is not Panel panel)
|
|
||||||
return;
|
|
||||||
|
|
||||||
panel.Children.Remove(_infoBar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a builder that can be used to create buttons inside notifications.
|
/// A severity for success messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NotificationButtonBuilder
|
Success,
|
||||||
{
|
|
||||||
private Action? _action;
|
|
||||||
private ICommand? _command;
|
|
||||||
private string _text = "Text";
|
|
||||||
private object? _commandParameter;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes text message of the button.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="text">The new text.</param>
|
|
||||||
/// <returns>The notification builder that can be used to further build the button.</returns>
|
|
||||||
public NotificationButtonBuilder WithText(string text)
|
|
||||||
{
|
|
||||||
_text = text;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes action that is called when the button is clicked.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="action">The action to call when the button is clicked.</param>
|
|
||||||
/// <returns>The builder that can be used to further build the button.</returns>
|
|
||||||
public NotificationButtonBuilder WithAction(Action action)
|
|
||||||
{
|
|
||||||
_command = null;
|
|
||||||
_action = action;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes command that is called when the button is clicked.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command">The command to call when the button is clicked.</param>
|
|
||||||
/// <returns>The builder that can be used to further build the button.</returns>
|
|
||||||
public NotificationButtonBuilder WithCommand(ICommand command)
|
|
||||||
{
|
|
||||||
_action = null;
|
|
||||||
_command = command;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes parameter of the command that is called when the button is clicked.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="commandParameter">The parameter of the command to call when the button is clicked.</param>
|
|
||||||
/// <returns>The builder that can be used to further build the button.</returns>
|
|
||||||
public NotificationButtonBuilder WithCommandParameter(object? commandParameter)
|
|
||||||
{
|
|
||||||
_commandParameter = commandParameter;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IControl Build()
|
|
||||||
{
|
|
||||||
if (_action != null)
|
|
||||||
return new Button {Content = _text, Command = ReactiveCommand.Create(() => _action())};
|
|
||||||
if (_command != null)
|
|
||||||
return new Button {Content = _text, Command = _command, CommandParameter = _commandParameter};
|
|
||||||
return new Button {Content = _text};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a severity of a notification.
|
/// A severity for warning messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum NotificationSeverity
|
Warning,
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A severity for informational messages.
|
|
||||||
/// </summary>
|
|
||||||
Informational,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A severity for success messages.
|
/// A severity for error messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Success,
|
Error
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A severity for warning messages.
|
|
||||||
/// </summary>
|
|
||||||
Warning,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A severity for error messages.
|
|
||||||
/// </summary>
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a profile editor command that can be used to rename a profile element.
|
||||||
|
/// </summary>
|
||||||
|
public class RenameProfileElement : IProfileEditorCommand
|
||||||
|
{
|
||||||
|
private readonly string? _name;
|
||||||
|
private readonly string? _originalName;
|
||||||
|
private readonly ProfileElement _subject;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="RenameProfileElement" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public RenameProfileElement(ProfileElement subject, string? name)
|
||||||
|
{
|
||||||
|
_subject = subject;
|
||||||
|
_name = name;
|
||||||
|
_originalName = subject.Name;
|
||||||
|
|
||||||
|
DisplayName = subject switch
|
||||||
|
{
|
||||||
|
Layer => "Rename layer",
|
||||||
|
Folder => "Rename folder",
|
||||||
|
_ => throw new ArgumentException("Type of subject is not supported")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Implementation of IProfileEditorCommand
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_subject.Name = _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
_subject.Name = _originalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -30,8 +30,8 @@ namespace Artemis.UI.Shared.Services.ProfileEditor
|
|||||||
public IObservable<bool> CanRedo => _canRedo.AsObservable().DistinctUntilChanged();
|
public IObservable<bool> CanRedo => _canRedo.AsObservable().DistinctUntilChanged();
|
||||||
|
|
||||||
public ReactiveCommand<IProfileEditorCommand, Unit> Execute { get; }
|
public ReactiveCommand<IProfileEditorCommand, Unit> Execute { get; }
|
||||||
public ReactiveCommand<Unit, Unit> Undo { get; }
|
public ReactiveCommand<Unit, IProfileEditorCommand?> Undo { get; }
|
||||||
public ReactiveCommand<Unit, Unit> Redo { get; }
|
public ReactiveCommand<Unit, IProfileEditorCommand?> Redo { get; }
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
@ -67,24 +67,28 @@ namespace Artemis.UI.Shared.Services.ProfileEditor
|
|||||||
_undoCommands.Clear();
|
_undoCommands.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteUndo()
|
private IProfileEditorCommand? ExecuteUndo()
|
||||||
{
|
{
|
||||||
if (!_undoCommands.TryPop(out IProfileEditorCommand? command))
|
if (!_undoCommands.TryPop(out IProfileEditorCommand? command))
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
command.Undo();
|
command.Undo();
|
||||||
_redoCommands.Push(command);
|
_redoCommands.Push(command);
|
||||||
UpdateSubjects();
|
UpdateSubjects();
|
||||||
|
|
||||||
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteRedo()
|
private IProfileEditorCommand? ExecuteRedo()
|
||||||
{
|
{
|
||||||
if (!_redoCommands.TryPop(out IProfileEditorCommand? command))
|
if (!_redoCommands.TryPop(out IProfileEditorCommand? command))
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
command.Execute();
|
command.Execute();
|
||||||
_undoCommands.Push(command);
|
_undoCommands.Push(command);
|
||||||
UpdateSubjects();
|
UpdateSubjects();
|
||||||
|
|
||||||
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateSubjects()
|
private void UpdateSubjects()
|
||||||
|
|||||||
@ -22,8 +22,8 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
{
|
{
|
||||||
_profileService = profileService;
|
_profileService = profileService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
ProfileConfiguration = _profileConfigurationSubject.AsObservable().DistinctUntilChanged();
|
ProfileConfiguration = _profileConfigurationSubject.AsObservable();
|
||||||
ProfileElement = _profileElementSubject.AsObservable().DistinctUntilChanged();
|
ProfileElement = _profileElementSubject.AsObservable();
|
||||||
History = Observable.Defer(() => Observable.Return(GetHistory(_profileConfigurationSubject.Value))).Concat(ProfileConfiguration.Select(GetHistory));
|
History = Observable.Defer(() => Observable.Return(GetHistory(_profileConfigurationSubject.Value))).Concat(ProfileConfiguration.Select(GetHistory));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
<StyleInclude Source="/Styles/Border.axaml" />
|
<StyleInclude Source="/Styles/Border.axaml" />
|
||||||
<StyleInclude Source="/Styles/Button.axaml" />
|
<StyleInclude Source="/Styles/Button.axaml" />
|
||||||
<StyleInclude Source="/Styles/TextBlock.axaml" />
|
<StyleInclude Source="/Styles/TextBlock.axaml" />
|
||||||
|
<StyleInclude Source="/Styles/TextBox.axaml" />
|
||||||
<StyleInclude Source="/Styles/Sidebar.axaml" />
|
<StyleInclude Source="/Styles/Sidebar.axaml" />
|
||||||
<StyleInclude Source="/Styles/InfoBar.axaml" />
|
<StyleInclude Source="/Styles/InfoBar.axaml" />
|
||||||
<StyleInclude Source="/Styles/TreeView.axaml" />
|
<StyleInclude Source="/Styles/TreeView.axaml" />
|
||||||
|
|||||||
28
src/Avalonia/Artemis.UI.Shared/Styles/TextBox.axaml
Normal file
28
src/Avalonia/Artemis.UI.Shared/Styles/TextBox.axaml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<Border Padding="50">
|
||||||
|
<StackPanel Spacing="5">
|
||||||
|
<TextBox />
|
||||||
|
<TextBox Classes="condensed" />
|
||||||
|
<TextBox Text="Hello Down there" />
|
||||||
|
<TextBox Classes="condensed" Text="Hello Down there" />
|
||||||
|
<TextBox Watermark="Watermark" />
|
||||||
|
<TextBox Classes="condensed" Watermark="Watermark" />
|
||||||
|
<TextBox Text="Test" IsEnabled="False" />
|
||||||
|
<TextBox Classes="condensed" Text="Test" IsEnabled="False" />
|
||||||
|
<TextBox Text="Test Clear" Classes="clearButton" />
|
||||||
|
<TextBox Classes="condensed clearButton" Text="Test Clear" />
|
||||||
|
<TextBox Text="Test Password" Classes="revealPasswordButton" PasswordChar="*" />
|
||||||
|
<TextBox Classes="condensed revealPasswordButton" Text="Test Password" PasswordChar="*" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Design.PreviewWith>
|
||||||
|
|
||||||
|
<!-- Add Styles Here -->
|
||||||
|
<Style Selector="TextBox.condensed">
|
||||||
|
<Setter Property="Padding" Value="4 2" />
|
||||||
|
<Setter Property="FontSize" Value="14"></Setter>
|
||||||
|
<Setter Property="MinHeight" Value="25" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Reactive;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
@ -8,13 +11,42 @@ namespace Artemis.UI.Screens.ProfileEditor.MenuBar
|
|||||||
{
|
{
|
||||||
public class MenuBarViewModel : ActivatableViewModelBase
|
public class MenuBarViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly INotificationService _notificationService;
|
||||||
private ProfileEditorHistory? _history;
|
private ProfileEditorHistory? _history;
|
||||||
|
private Action? _lastMessage;
|
||||||
|
|
||||||
public MenuBarViewModel(IProfileEditorService profileEditorService)
|
public MenuBarViewModel(IProfileEditorService profileEditorService, INotificationService notificationService)
|
||||||
{
|
{
|
||||||
|
_notificationService = notificationService;
|
||||||
this.WhenActivated(d => profileEditorService.History.Subscribe(history => History = history).DisposeWith(d));
|
this.WhenActivated(d => profileEditorService.History.Subscribe(history => History = history).DisposeWith(d));
|
||||||
|
this.WhenAnyValue(x => x.History)
|
||||||
|
.Select(h => h?.Undo ?? Observable.Never<IProfileEditorCommand?>())
|
||||||
|
.Switch()
|
||||||
|
.Subscribe(DisplayUndo);
|
||||||
|
this.WhenAnyValue(x => x.History)
|
||||||
|
.Select(h => h?.Redo ?? Observable.Never<IProfileEditorCommand?>())
|
||||||
|
.Switch()
|
||||||
|
.Subscribe(DisplayRedo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DisplayUndo(IProfileEditorCommand? command)
|
||||||
|
{
|
||||||
|
if (command == null || History == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_lastMessage?.Invoke();
|
||||||
|
_lastMessage = _notificationService.CreateNotification().WithMessage($"Undid '{command.DisplayName}'.").HavingButton(b => b.WithText("Redo").WithCommand(History.Redo)).Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisplayRedo(IProfileEditorCommand? command)
|
||||||
|
{
|
||||||
|
if (command == null || History == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_lastMessage?.Invoke();
|
||||||
|
_notificationService.CreateNotification().WithMessage($"Redid '{command.DisplayName}'.").HavingButton(b => b.WithText("Undo").WithCommand(History.Undo)).Show(); ;
|
||||||
|
}
|
||||||
|
|
||||||
public ProfileEditorHistory? History
|
public ProfileEditorHistory? History
|
||||||
{
|
{
|
||||||
get => _history;
|
get => _history;
|
||||||
|
|||||||
@ -14,19 +14,33 @@
|
|||||||
<converters:PropertyTreeMarginConverter x:Key="PropertyTreeMarginConverter" />
|
<converters:PropertyTreeMarginConverter x:Key="PropertyTreeMarginConverter" />
|
||||||
<sharedConverters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
|
<sharedConverters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
<UserControl.Styles>
|
||||||
|
<Style Selector="avalonia|MaterialIcon.chevron-collapsed">
|
||||||
|
<Setter Property="RenderTransform" Value="rotate(180deg)" />
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<Border Name="Bd"
|
<Border Name="Bd"
|
||||||
BorderBrush="{DynamicResource MaterialDesignDivider}"
|
BorderBrush="{DynamicResource MaterialDesignDivider}"
|
||||||
BorderThickness="0,0,0,1"
|
BorderThickness="0,0,0,1"
|
||||||
Height="25">
|
Height="25">
|
||||||
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*">
|
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*">
|
||||||
<ToggleButton x:Name="Expander"
|
|
||||||
Foreground="{DynamicResource MaterialDesignBody}"
|
<avalonia:MaterialIcon Classes.chevron-collapsed="{Binding !ProfileElementPropertyGroupViewModel.IsExpanded}"
|
||||||
IsChecked="{Binding Path=LayerPropertyGroupViewModel.IsExpanded}"
|
IsVisible="{Binding ProfileElementPropertyGroupViewModel.HasChildren}"
|
||||||
IsVisible="{Binding LayerPropertyGroupViewModel.HasChildren}"
|
Kind="ChevronUp"
|
||||||
ClickMode="Press" />
|
Grid.Column="0"
|
||||||
|
Margin="5 0"
|
||||||
|
PointerPressed="InputElement_OnPointerPressed"
|
||||||
|
Background="Transparent">
|
||||||
|
<avalonia:MaterialIcon.Transitions>
|
||||||
|
<Transitions>
|
||||||
|
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" />
|
||||||
|
</Transitions>
|
||||||
|
</avalonia:MaterialIcon.Transitions>
|
||||||
|
</avalonia:MaterialIcon>
|
||||||
|
|
||||||
<StackPanel Grid.Column="1">
|
<StackPanel Grid.Column="1">
|
||||||
<!-- Type: None -->
|
<!-- Type: None -->
|
||||||
<TextBlock Text="{Binding LayerPropertyGroup.GroupDescription.Name}"
|
<TextBlock Text="{Binding LayerPropertyGroup.GroupDescription.Name}"
|
||||||
ToolTip.Tip="{Binding LayerPropertyGroup.GroupDescription.Description}"
|
ToolTip.Tip="{Binding LayerPropertyGroup.GroupDescription.Description}"
|
||||||
@ -164,11 +178,11 @@
|
|||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Do not bind directly to the LayerPropertyGroupViewModel.Children collection
|
Do not bind directly to the ProfileElementPropertyGroupViewModel.Children collection
|
||||||
Instead use a reference provided by the VM that is null when collapsed, virtualization for noobs
|
Instead use a reference provided by the VM that is null when collapsed, virtualization for noobs
|
||||||
-->
|
-->
|
||||||
<ItemsControl Items="{Binding Children}"
|
<ItemsControl Items="{Binding Children}"
|
||||||
IsVisible="{Binding LayerPropertyGroupViewModel.IsExpanded}"
|
IsVisible="{Binding ProfileElementPropertyGroupViewModel.IsExpanded}"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<ItemsControl.DataTemplates>
|
<ItemsControl.DataTemplates>
|
||||||
<DataTemplate DataType="profileElementProperties:ProfileElementPropertyGroupViewModel">
|
<DataTemplate DataType="profileElementProperties:ProfileElementPropertyGroupViewModel">
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
@ -14,5 +15,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree
|
|||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InputElement_OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (ViewModel != null)
|
||||||
|
ViewModel.ProfileElementPropertyGroupViewModel.IsExpanded = !ViewModel.ProfileElementPropertyGroupViewModel.IsExpanded;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,15 @@
|
|||||||
Kind="FolderOpen"
|
Kind="FolderOpen"
|
||||||
Margin="0 0 5 0"
|
Margin="0 0 5 0"
|
||||||
IsVisible="{Binding IsExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" />
|
IsVisible="{Binding IsExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" />
|
||||||
<TextBlock Grid.Column="2" Text="{Binding Folder.Name}" VerticalAlignment="Center" />
|
<TextBox Grid.Column="2"
|
||||||
|
Margin="-5 0 0 0"
|
||||||
|
Classes="condensed"
|
||||||
|
IsVisible="{Binding Renaming}"
|
||||||
|
Text="{Binding RenameValue}"
|
||||||
|
x:Name="Input"
|
||||||
|
KeyUp="InputElement_OnKeyUp"
|
||||||
|
LostFocus="InputElement_OnLostFocus"/>
|
||||||
|
<TextBlock Grid.Column="2" IsVisible="{Binding !Renaming}" Text="{Binding Folder.Name}" VerticalAlignment="Center" />
|
||||||
<ToggleButton Grid.Column="3"
|
<ToggleButton Grid.Column="3"
|
||||||
Classes="icon-button icon-button-small"
|
Classes="icon-button icon-button-small"
|
||||||
ToolTip.Tip="Toggle suspended state"
|
ToolTip.Tip="Toggle suspended state"
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
using System;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||||
{
|
{
|
||||||
@ -14,6 +18,24 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
this.WhenActivated(_ => ViewModel?.Rename.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
this.Get<TextBox>("Input").Focus();
|
||||||
|
this.Get<TextBox>("Input").SelectAll();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InputElement_OnKeyUp(object? sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter)
|
||||||
|
ViewModel?.SubmitRename();
|
||||||
|
else if (e.Key == Key.Escape)
|
||||||
|
ViewModel?.CancelRename();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InputElement_OnLostFocus(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel?.CancelRename();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -14,5 +14,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Folder Folder { get; }
|
public Folder Folder { get; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,7 +16,15 @@
|
|||||||
<avalonia:MaterialIcon Kind="AlertCircle" />
|
<avalonia:MaterialIcon Kind="AlertCircle" />
|
||||||
</Button>
|
</Button>
|
||||||
<avalonia:MaterialIcon Grid.Column="1" Kind="{Binding Layer.LayerBrush.Descriptor.Icon}" Margin="0 0 5 0" />
|
<avalonia:MaterialIcon Grid.Column="1" Kind="{Binding Layer.LayerBrush.Descriptor.Icon}" Margin="0 0 5 0" />
|
||||||
<TextBlock Grid.Column="2" Text="{Binding Layer.Name}" VerticalAlignment="Center" />
|
<TextBox Grid.Column="2"
|
||||||
|
Margin="-5 0 0 0"
|
||||||
|
Classes="condensed"
|
||||||
|
x:Name="Input"
|
||||||
|
IsVisible="{Binding Renaming}"
|
||||||
|
Text="{Binding RenameValue}"
|
||||||
|
KeyUp="InputElement_OnKeyUp"
|
||||||
|
LostFocus="InputElement_OnLostFocus"></TextBox>
|
||||||
|
<TextBlock Grid.Column="2" IsVisible="{Binding !Renaming}" Text="{Binding Layer.Name}" VerticalAlignment="Center" />
|
||||||
<ToggleButton Grid.Column="3"
|
<ToggleButton Grid.Column="3"
|
||||||
Classes="icon-button icon-button-small"
|
Classes="icon-button icon-button-small"
|
||||||
ToolTip.Tip="Toggle suspended state"
|
ToolTip.Tip="Toggle suspended state"
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
using System;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||||
{
|
{
|
||||||
@ -14,6 +18,24 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
this.WhenActivated(_ => ViewModel?.Rename.Subscribe(_ =>
|
||||||
|
{
|
||||||
|
this.Get<TextBox>("Input").Focus();
|
||||||
|
this.Get<TextBox>("Input").SelectAll();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InputElement_OnKeyUp(object? sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter)
|
||||||
|
ViewModel?.SubmitRename();
|
||||||
|
else if (e.Key == Key.Escape)
|
||||||
|
ViewModel?.CancelRename();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InputElement_OnLostFocus(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel?.CancelRename();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13,11 +13,11 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</TreeView.Styles>
|
</TreeView.Styles>
|
||||||
<TreeView.KeyBindings>
|
<TreeView.KeyBindings>
|
||||||
<KeyBinding Gesture="F2" Command="{Binding RenameElement}" CommandParameter="{Binding SelectedTreeItem}" />
|
<KeyBinding Gesture="F2" Command="{Binding SelectedChild.Rename}" />
|
||||||
<KeyBinding Gesture="Delete" Command="{Binding DeleteElement}" CommandParameter="{Binding SelectedTreeItem}" />
|
<KeyBinding Gesture="Delete" Command="{Binding Delete}" CommandParameter="{Binding SelectedTreeItem}" />
|
||||||
<KeyBinding Gesture="Ctrl+D" Command="{Binding DuplicateElement}" CommandParameter="{Binding SelectedTreeItem}" />
|
<KeyBinding Gesture="Ctrl+D" Command="{Binding Duplicate}" CommandParameter="{Binding SelectedTreeItem}" />
|
||||||
<KeyBinding Gesture="Ctrl+C" Command="{Binding CopyElement}" CommandParameter="{Binding SelectedTreeItem}" />
|
<KeyBinding Gesture="Ctrl+C" Command="{Binding Copy}" CommandParameter="{Binding SelectedTreeItem}" />
|
||||||
<KeyBinding Gesture="Ctrl+V" Command="{Binding PasteElement}" CommandParameter="{Binding SelectedTreeItem}" />
|
<KeyBinding Gesture="Ctrl+V" Command="{Binding Paste}" CommandParameter="{Binding SelectedTreeItem}" />
|
||||||
</TreeView.KeyBindings>
|
</TreeView.KeyBindings>
|
||||||
<TreeView.ItemTemplate>
|
<TreeView.ItemTemplate>
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}">
|
<TreeDataTemplate ItemsSource="{Binding Children}">
|
||||||
@ -35,28 +35,28 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="Duplicate" Command="{Binding DuplicateElement}" InputGesture="Ctrl+D" IsEnabled="False">
|
<MenuItem Header="Duplicate" Command="{Binding Duplicate}" InputGesture="Ctrl+D" IsEnabled="False">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<avalonia:MaterialIcon Kind="ContentDuplicate" />
|
<avalonia:MaterialIcon Kind="ContentDuplicate" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Copy" Command="{Binding CopyElement}" InputGesture="Ctrl+C" IsEnabled="False">
|
<MenuItem Header="Copy" Command="{Binding Copy}" InputGesture="Ctrl+C" IsEnabled="False">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<avalonia:MaterialIcon Kind="ContentCopy" />
|
<avalonia:MaterialIcon Kind="ContentCopy" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Paste" Command="{Binding PasteElement}" InputGesture="Ctrl+V">
|
<MenuItem Header="Paste" Command="{Binding Paste}" InputGesture="Ctrl+V">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<avalonia:MaterialIcon Kind="ContentPaste" />
|
<avalonia:MaterialIcon Kind="ContentPaste" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="Rename" Command="{Binding RenameElement}" InputGesture="F2" IsEnabled="False">
|
<MenuItem Header="Rename" Command="{Binding Rename}" InputGesture="F2">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<avalonia:MaterialIcon Kind="RenameBox" />
|
<avalonia:MaterialIcon Kind="RenameBox" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Delete" Command="{Binding DeleteElement}" InputGesture="Delete" IsEnabled="False">
|
<MenuItem Header="Delete" Command="{Binding Delete}" InputGesture="Delete" IsEnabled="False">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<avalonia:MaterialIcon Kind="TrashCan" />
|
<avalonia:MaterialIcon Kind="TrashCan" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reactive;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
@ -41,8 +42,10 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
if (model?.ProfileElement is RenderProfileElement renderProfileElement)
|
if (model?.ProfileElement is RenderProfileElement renderProfileElement)
|
||||||
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public TreeItemViewModel? SelectedChild
|
public TreeItemViewModel? SelectedChild
|
||||||
{
|
{
|
||||||
get => _selectedChild;
|
get => _selectedChild;
|
||||||
|
|||||||
@ -24,6 +24,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
private bool _isExpanded;
|
private bool _isExpanded;
|
||||||
private ProfileElement? _profileElement;
|
private ProfileElement? _profileElement;
|
||||||
private RenderProfileElement? _currentProfileElement;
|
private RenderProfileElement? _currentProfileElement;
|
||||||
|
private bool _renaming;
|
||||||
|
private string? _renameValue;
|
||||||
|
|
||||||
protected TreeItemViewModel(TreeItemViewModel? parent, ProfileElement? profileElement, IWindowService windowService, IProfileEditorService profileEditorService,
|
protected TreeItemViewModel(TreeItemViewModel? parent, ProfileElement? profileElement, IWindowService windowService, IProfileEditorService profileEditorService,
|
||||||
IProfileEditorVmFactory profileEditorVmFactory)
|
IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
@ -51,6 +53,12 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(ProfileElement, "New folder"), ProfileElement, 0));
|
profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(ProfileElement, "New folder"), ProfileElement, 0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Rename = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
Renaming = true;
|
||||||
|
RenameValue = ProfileElement?.Name;
|
||||||
|
});
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
_profileEditorService.ProfileElement.Subscribe(element => _currentProfileElement = element).DisposeWith(d);
|
_profileEditorService.ProfileElement.Subscribe(element => _currentProfileElement = element).DisposeWith(d);
|
||||||
@ -71,11 +79,24 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
set => this.RaiseAndSetIfChanged(ref _isExpanded, value);
|
set => this.RaiseAndSetIfChanged(ref _isExpanded, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Renaming
|
||||||
|
{
|
||||||
|
get => _renaming;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _renaming, value);
|
||||||
|
}
|
||||||
|
|
||||||
public TreeItemViewModel? Parent { get; set; }
|
public TreeItemViewModel? Parent { get; set; }
|
||||||
public ObservableCollection<TreeItemViewModel> Children { get; } = new();
|
public ObservableCollection<TreeItemViewModel> Children { get; } = new();
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> AddLayer { get; }
|
public ReactiveCommand<Unit, Unit> AddLayer { get; }
|
||||||
public ReactiveCommand<Unit, Unit> AddFolder { get; }
|
public ReactiveCommand<Unit, Unit> AddFolder { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> Rename { get; }
|
||||||
|
|
||||||
|
public string? RenameValue
|
||||||
|
{
|
||||||
|
get => _renameValue;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _renameValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ShowBrokenStateExceptions()
|
public async Task ShowBrokenStateExceptions()
|
||||||
{
|
{
|
||||||
@ -93,6 +114,23 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SubmitRename()
|
||||||
|
{
|
||||||
|
if (ProfileElement == null)
|
||||||
|
{
|
||||||
|
Renaming = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_profileEditorService.ExecuteCommand(new RenameProfileElement(ProfileElement, RenameValue));
|
||||||
|
Renaming = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelRename()
|
||||||
|
{
|
||||||
|
Renaming = false;
|
||||||
|
}
|
||||||
|
|
||||||
protected void SubscribeToProfileElement(CompositeDisposable d)
|
protected void SubscribeToProfileElement(CompositeDisposable d)
|
||||||
{
|
{
|
||||||
if (ProfileElement == null)
|
if (ProfileElement == null)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user