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:
parent
f733ce02de
commit
65a2aa70e2
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
79
src/Artemis.UI/Models/FolderClipboardModel.cs
Normal file
79
src/Artemis.UI/Models/FolderClipboardModel.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/Artemis.UI/Models/WindowSize.cs
Normal file
71
src/Artemis.UI/Models/WindowSize.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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 />
|
||||
|
||||
@ -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>
|
||||
Loading…
x
Reference in New Issue
Block a user