1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Profile editor - Copy child elements when copying folders

UI - Save window position/size
Numberic node - Correctly show 0 instead of an empty input when reloading after a save
This commit is contained in:
Robert 2022-09-09 20:29:12 +02:00
parent f733ce02de
commit 65a2aa70e2
8 changed files with 206 additions and 13 deletions

View File

@ -3,6 +3,7 @@ using System.Text;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Storage.Entities.Profile;
using Artemis.UI.Models;
using Avalonia;
using Avalonia.Input;
@ -21,7 +22,7 @@ public static class ProfileElementExtensions
return;
DataObject dataObject = new();
string copy = CoreJson.SerializeObject(folder.FolderEntity, true);
string copy = CoreJson.SerializeObject(new FolderClipboardModel(folder), true);
dataObject.Set(ClipboardDataFormat, copy);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
@ -50,9 +51,8 @@ public static class ProfileElementExtensions
object? entity = CoreJson.DeserializeObject(Encoding.Unicode.GetString(bytes), true);
switch (entity)
{
case FolderEntity folderEntity:
folderEntity.Id = Guid.NewGuid();
return new Folder(parent.Profile, parent, folderEntity);
case FolderClipboardModel folderClipboardModel:
return folderClipboardModel.Paste(parent.Profile, parent);
case LayerEntity layerEntity:
layerEntity.Id = Guid.NewGuid();
return new Layer(parent.Profile, parent, layerEntity);

View File

@ -7,7 +7,8 @@
x:Class="Artemis.UI.MainWindow"
Icon="/Assets/Images/Logo/application.ico"
Title="Artemis 2.0"
WindowStartupLocation="CenterScreen">
MinWidth="600"
MinHeight="400">
<Panel Name="RootPanel">
<Border Name="DragHandle" Background="Transparent" Height="40" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<DockPanel>

View File

@ -1,10 +1,15 @@
using System;
using System.Reactive;
using System.Reactive.Linq;
using Artemis.UI.Models;
using Artemis.UI.Screens.Root;
using Artemis.UI.Shared;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using FluentAvalonia.Core.ApplicationModel;
using ReactiveUI;
namespace Artemis.UI;
@ -12,13 +17,17 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
{
private readonly Panel _rootPanel;
private readonly ContentControl _sidebarContentControl;
private bool _activated;
public MainWindow()
{
Opened += OnOpened;
Activated += OnActivated;
Deactivated += OnDeactivated;
ApplyWindowSize();
InitializeComponent();
_rootPanel = this.Get<Panel>("RootPanel");
_sidebarContentControl = this.Get<ContentControl>("SidebarContentControl");
_rootPanel.LayoutUpdated += OnLayoutUpdated;
@ -26,6 +35,27 @@ public class MainWindow : ReactiveCoreWindow<RootViewModel>
#if DEBUG
this.AttachDevTools();
#endif
Observable.FromEventPattern<PixelPointEventArgs>(x => PositionChanged += x, x => PositionChanged -= x)
.Select(_ => Unit.Default)
.Merge(this.WhenAnyValue(vm => vm.WindowState, vm => vm.Width, vm => vm.Width, vm => vm.Height).Select(_ => Unit.Default))
.Throttle(TimeSpan.FromMilliseconds(200), AvaloniaScheduler.Instance)
.Subscribe(_ => SaveWindowSize());
}
private void ApplyWindowSize()
{
_activated = true;
RootViewModel.WindowSizeSetting?.Value?.ApplyToWindow(this);
}
private void SaveWindowSize()
{
if (RootViewModel.WindowSizeSetting == null || !_activated)
return;
RootViewModel.WindowSizeSetting.Value ??= new WindowSize();
RootViewModel.WindowSizeSetting.Value.ApplyFromWindow(this);
}
// TODO: Replace with a media query once https://github.com/AvaloniaUI/Avalonia/pull/7938 is implemented

View File

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using Artemis.Core;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.UI.Exceptions;
namespace Artemis.UI.Models;
public class FolderClipboardModel
{
public FolderClipboardModel(Folder folder)
{
FolderEntity = folder.FolderEntity;
Folders = new List<FolderEntity>();
Layers = new List<LayerEntity>();
foreach (Folder allFolder in folder.GetAllFolders())
Folders.Add(allFolder.FolderEntity);
foreach (Layer allLayer in folder.GetAllLayers())
Layers.Add(allLayer.LayerEntity);
}
// ReSharper disable once UnusedMember.Global - For JSON.NET
public FolderClipboardModel()
{
FolderEntity = null;
Folders = new List<FolderEntity>();
Layers = new List<LayerEntity>();
}
public FolderEntity? FolderEntity { get; set; }
public List<FolderEntity> Folders { get; set; }
public List<LayerEntity> Layers { get; set; }
public bool HasBeenPasted { get; set; }
public Folder Paste(Profile profile, ProfileElement parent)
{
if (FolderEntity == null)
throw new ArtemisUIException("Couldn't paste folder because FolderEntity deserialized as null");
if (HasBeenPasted)
throw new ArtemisUIException("Clipboard model can only be pasted once");
HasBeenPasted = true;
// Generate new GUIDs
ReplaceGuid(FolderEntity);
foreach (FolderEntity folderEntity in Folders)
ReplaceGuid(folderEntity);
foreach (LayerEntity layerEntity in Layers)
ReplaceGuid(layerEntity);
// Inject the pasted elements into the profile
profile.ProfileEntity.Folders.AddRange(Folders);
profile.ProfileEntity.Layers.AddRange(Layers);
// Let the folder initialize and load as usual
FolderEntity.Name += " - copy";
Folder folder = new(profile, parent, FolderEntity);
return folder;
}
private void ReplaceGuid(RenderElementEntity parent)
{
Guid old = parent.Id;
parent.Id = Guid.NewGuid();
foreach (FolderEntity child in Folders)
{
if (child.ParentId == old)
child.ParentId = parent.Id;
}
foreach (LayerEntity child in Layers)
{
if (child.ParentId == old)
child.ParentId = parent.Id;
}
}
}

View File

@ -0,0 +1,71 @@
using System;
using Avalonia;
using Avalonia.Controls;
namespace Artemis.UI.Models;
public class WindowSize
{
private bool _applying;
public int Top { get; set; }
public int Left { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public int MaximizedTop { get; set; }
public int MaximizedLeft { get; set; }
public double MaximizedWidth { get; set; }
public double MaximizedHeight { get; set; }
public bool IsMaximized { get; set; }
public void ApplyFromWindow(Window window)
{
if (_applying)
return;
if (double.IsNaN(window.Width) || double.IsNaN(window.Height))
return;
IsMaximized = window.WindowState == WindowState.Maximized;
if (IsMaximized)
{
MaximizedTop = window.Position.Y;
MaximizedLeft = window.Position.X;
MaximizedHeight = window.Height;
MaximizedWidth = window.Width;
}
else
{
Top = window.Position.Y;
Left = window.Position.X;
Height = window.Height;
Width = window.Width;
}
}
public void ApplyToWindow(Window window)
{
if (_applying)
return;
try
{
_applying = true;
if (IsMaximized)
{
window.Position = new PixelPoint(MaximizedLeft, MaximizedTop);
window.WindowState = WindowState.Maximized;
}
else
{
window.Position = new PixelPoint(Left, Top);
window.Height = Height;
window.Width = Width;
window.WindowState = WindowState.Normal;
}
}
finally
{
_applying = false;
}
}
}

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Models;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Services.Interfaces;
@ -44,7 +45,8 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
ISidebarVmFactory sidebarVmFactory)
{
Router = new RoutingState();
WindowSizeSetting = settingsService.GetSetting<WindowSize?>("WindowSize");
_coreService = coreService;
_settingsService = settingsService;
_windowService = windowService;
@ -54,7 +56,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
_defaultTitleBarViewModel = defaultTitleBarViewModel;
_sidebarVmFactory = sidebarVmFactory;
_lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!;
mainWindowService.ConfigureMainWindowProvider(this);
DisplayAccordingToSettings();
@ -90,6 +92,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
private void CurrentMainWindowOnClosing(object? sender, EventArgs e)
{
WindowSizeSetting.Save();
_lifeTime.MainWindow = null;
SidebarViewModel = null;
Router.NavigateAndReset.Execute(new EmptyViewModel(this, "blank")).Subscribe();
@ -121,6 +124,8 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
/// <inheritdoc />
public RoutingState Router { get; }
public static PluginSetting<WindowSize?>? WindowSizeSetting { get; private set; }
#region Tray commands
public void OpenScreen(string displayName)
@ -170,7 +175,6 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
_lifeTime.MainWindow.Closing += CurrentMainWindowOnClosing;
}
_lifeTime.MainWindow.WindowState = WindowState.Normal;
_lifeTime.MainWindow.Activate();
OnMainWindowOpened();
}
@ -226,6 +230,11 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
}
#endregion
public void SaveWindowBounds(int x, int y, int width, int height)
{
throw new NotImplementedException();
}
}
internal class EmptyViewModel : MainScreenViewModel

View File

@ -15,7 +15,8 @@ public class NumericConverter : IValueConverter
if (value is not Numeric numeric)
return value;
return Numeric.IsTypeCompatible(targetType) ? numeric.ToType(targetType, NumberFormatInfo.InvariantInfo) : value;
object result = Numeric.IsTypeCompatible(targetType) ? numeric.ToType(targetType, NumberFormatInfo.InvariantInfo) : value;
return result;
}
/// <inheritdoc />

View File

@ -12,9 +12,11 @@
<UserControl.Resources>
<converters:NumericConverter x:Key="NumericConverter" />
</UserControl.Resources>
<controls:NumberBox VerticalAlignment="Center" MinWidth="75" SimpleNumberFormat="F3" Classes="condensed">
<Interaction.Behaviors>
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding CurrentValue, Converter={StaticResource NumericConverter}}"/>
</Interaction.Behaviors>
<controls:NumberBox VerticalAlignment="Center"
MinWidth="75"
SimpleNumberFormat="F3"
Classes="condensed"
AcceptsExpression="True"
Value="{CompiledBinding CurrentValue, Converter={StaticResource NumericConverter}}">
</controls:NumberBox>
</UserControl>