mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
UI - Implemented most of the surface editor
This commit is contained in:
parent
ca1e3ce365
commit
f98e398bc5
@ -52,9 +52,10 @@ namespace Artemis.UI.Avalonia.Shared.Controls
|
|||||||
// Determine the scale required to fit the desired size of the control
|
// Determine the scale required to fit the desired size of the control
|
||||||
double scale = Math.Min(Bounds.Width / _deviceBounds.Width, Bounds.Height / _deviceBounds.Height);
|
double scale = Math.Min(Bounds.Width / _deviceBounds.Width, Bounds.Height / _deviceBounds.Height);
|
||||||
|
|
||||||
// Scale the visualization in the desired bounding box
|
|
||||||
|
|
||||||
DrawingContext.PushedState? boundsPush = null;
|
DrawingContext.PushedState? boundsPush = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Scale the visualization in the desired bounding box
|
||||||
if (Bounds.Width > 0 && Bounds.Height > 0)
|
if (Bounds.Width > 0 && Bounds.Height > 0)
|
||||||
boundsPush = drawingContext.PushPostTransform(Matrix.CreateScale(scale, scale));
|
boundsPush = drawingContext.PushPostTransform(Matrix.CreateScale(scale, scale));
|
||||||
|
|
||||||
@ -71,9 +72,12 @@ namespace Artemis.UI.Avalonia.Shared.Controls
|
|||||||
|
|
||||||
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
||||||
deviceVisualizerLed.RenderGeometry(drawingContext, false);
|
deviceVisualizerLed.RenderGeometry(drawingContext, false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
boundsPush?.Dispose();
|
boundsPush?.Dispose();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a LED of the device has been clicked
|
/// Occurs when a LED of the device has been clicked
|
||||||
@ -275,6 +279,16 @@ namespace Artemis.UI.Avalonia.Shared.Controls
|
|||||||
InvalidateMeasure();
|
InvalidateMeasure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Overrides of Layoutable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
|
{
|
||||||
|
return new Size(Math.Min(availableSize.Width, _deviceBounds.Width), Math.Min(availableSize.Height, _deviceBounds.Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,6 +54,7 @@ namespace Artemis.UI.Avalonia.Shared.Controls
|
|||||||
public SelectionRectangle()
|
public SelectionRectangle()
|
||||||
{
|
{
|
||||||
AffectsRender<TextBlock>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty);
|
AffectsRender<TextBlock>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty);
|
||||||
|
IsHitTestVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -108,6 +109,9 @@ namespace Artemis.UI.Avalonia.Shared.Controls
|
|||||||
|
|
||||||
private void ParentOnPointerPressed(object? sender, PointerPressedEventArgs e)
|
private void ParentOnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
|
return;
|
||||||
|
|
||||||
e.Pointer.Capture(this);
|
e.Pointer.Capture(this);
|
||||||
|
|
||||||
_startPosition = e.GetPosition(Parent);
|
_startPosition = e.GetPosition(Parent);
|
||||||
@ -131,6 +135,9 @@ namespace Artemis.UI.Avalonia.Shared.Controls
|
|||||||
|
|
||||||
private void ParentOnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
private void ParentOnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!ReferenceEquals(e.Pointer.Captured, this))
|
||||||
|
return;
|
||||||
|
|
||||||
e.Pointer.Capture(null);
|
e.Pointer.Capture(null);
|
||||||
|
|
||||||
if (_displayRect != null)
|
if (_displayRect != null)
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
namespace Artemis.UI.Avalonia.Shared.Services.Interfaces
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Shared.Services.Interfaces
|
||||||
{
|
{
|
||||||
public interface IWindowService : IArtemisSharedUIService
|
public interface IWindowService : IArtemisSharedUIService
|
||||||
{
|
{
|
||||||
@ -13,8 +15,9 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Given a ViewModel, show its corresponding View as a Dialog
|
/// Given a ViewModel, show its corresponding View as a Dialog
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The return type</typeparam>
|
||||||
/// <param name="viewModel">ViewModel to show the View for</param>
|
/// <param name="viewModel">ViewModel to show the View for</param>
|
||||||
/// <returns>DialogResult of the View</returns>
|
/// <returns>A task containing the return value of type <typeparamref name="T"/></returns>
|
||||||
bool? ShowDialog(object viewModel);
|
Task<T> ShowDialog<T>(object viewModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Artemis.UI.Avalonia.Shared.Exceptions;
|
using Artemis.UI.Avalonia.Shared.Exceptions;
|
||||||
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Shared.Services
|
namespace Artemis.UI.Avalonia.Shared.Services
|
||||||
@ -42,9 +46,23 @@ namespace Artemis.UI.Avalonia.Shared.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool? ShowDialog(object viewModel)
|
public async Task<T> ShowDialog<T>(object viewModel)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime classic)
|
||||||
|
throw new ArtemisSharedUIException($"Can't show a dialog when application lifetime is not IClassicDesktopStyleApplicationLifetime.");
|
||||||
|
|
||||||
|
string name = viewModel.GetType().FullName!.Split('`')[0].Replace("ViewModel", "View");
|
||||||
|
Type? type = viewModel.GetType().Assembly.GetType(name);
|
||||||
|
|
||||||
|
if (type == null)
|
||||||
|
throw new ArtemisSharedUIException($"Failed to find a window named {name}.");
|
||||||
|
if (!type.IsAssignableTo(typeof(Window)))
|
||||||
|
throw new ArtemisSharedUIException($"Type {name} is not a window.");
|
||||||
|
|
||||||
|
Window window = (Window) Activator.CreateInstance(type)!;
|
||||||
|
window.DataContext = viewModel;
|
||||||
|
Window parent = classic.Windows.FirstOrDefault(w => w.IsActive) ?? classic.MainWindow;
|
||||||
|
return await window.ShowDialog<T>(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -1,76 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>WinExe</OutputType>
|
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Controls\" />
|
|
||||||
<Folder Include="Models\" />
|
|
||||||
<AvaloniaResource Include="Assets\**" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="Assets\Images\home-banner.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Avalonia" Version="0.10.7" />
|
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.7" />
|
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.7" />
|
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.7" />
|
|
||||||
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.7.2" />
|
|
||||||
<PackageReference Include="FluentAvaloniaUI" Version="1.1.3" />
|
|
||||||
<PackageReference Include="Flurl.Http" Version="3.2.0" />
|
|
||||||
<PackageReference Include="Live.Avalonia" Version="1.3.1" />
|
|
||||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
|
||||||
<PackageReference Include="Splat.Ninject" Version="13.1.22" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
|
||||||
<ProjectReference Include="..\Artemis.UI.Avalonia.Shared\Artemis.UI.Avalonia.Shared.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Update="MainWindow.axaml.cs">
|
|
||||||
<DependentUpon>%(Filename)</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Root\Views\SidebarCategoryView.axaml.cs">
|
|
||||||
<DependentUpon>%(Filename)</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Root\Views\SidebarProfileConfigurationView.axaml.cs">
|
|
||||||
<DependentUpon>%(Filename)</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Root\Views\SidebarScreenView.axaml.cs">
|
|
||||||
<DependentUpon>%(Filename)</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Root\Views\SidebarView.axaml.cs">
|
|
||||||
<DependentUpon>%(Filename)</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Sidebar\Views\SidebarView.axaml.cs">
|
|
||||||
<DependentUpon>SidebarView.axaml</DependentUpon>
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Root\Views\RootView.axaml.cs">
|
|
||||||
<DependentUpon>RootView.axaml</DependentUpon>
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<UpToDateCheckInput Remove="Views\MainWindow.axaml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Assets\Images\Logo\bow-black.ico" />
|
|
||||||
<Content Include="Assets\Images\Logo\bow-white.ico" />
|
|
||||||
<Content Include="Assets\Images\Logo\bow.ico" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="RGB.NET.Core">
|
|
||||||
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Resource Include="Assets\Images\Logo\bow-black.ico" />
|
|
||||||
<Resource Include="Assets\Images\Logo\bow-white.ico" />
|
|
||||||
<Resource Include="Assets\Images\Logo\bow-white.svg" />
|
|
||||||
<Resource Include="Assets\Images\Logo\bow.ico" />
|
|
||||||
<Resource Include="Assets\Images\Logo\bow.svg" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -11,6 +11,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Assets\Images\home-banner.png" />
|
<None Remove="Assets\Images\home-banner.png" />
|
||||||
|
<None Remove="Screens\SurfaceEditor\Views\ListDeviceView.xaml" />
|
||||||
|
<None Remove="Screens\SurfaceEditor\Views\SurfaceDeviceView.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="0.10.10" />
|
<PackageReference Include="Avalonia" Version="0.10.10" />
|
||||||
@ -62,6 +64,16 @@
|
|||||||
<Content Include="Assets\Images\Logo\bow-white.ico" />
|
<Content Include="Assets\Images\Logo\bow-white.ico" />
|
||||||
<Content Include="Assets\Images\Logo\bow.ico" />
|
<Content Include="Assets\Images\Logo\bow.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Include="Screens\SurfaceEditor\Views\ListDeviceView.xaml">
|
||||||
|
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Screens\SurfaceEditor\Views\SurfaceDeviceView.xaml">
|
||||||
|
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="RGB.NET.Core">
|
<Reference Include="RGB.NET.Core">
|
||||||
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll</HintPath>
|
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll</HintPath>
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Extensions
|
||||||
|
{
|
||||||
|
public static class ObservableCollectionExtensions
|
||||||
|
{
|
||||||
|
public static void Sort<T>(this ObservableCollection<T> collection, Func<T, object> order)
|
||||||
|
{
|
||||||
|
List<T> ordered = collection.OrderBy(order).ToList();
|
||||||
|
for (int index = 0; index < ordered.Count; index++)
|
||||||
|
{
|
||||||
|
T dataBindingConditionViewModel = ordered[index];
|
||||||
|
if (collection.IndexOf(dataBindingConditionViewModel) != index)
|
||||||
|
collection.Move(collection.IndexOf(dataBindingConditionViewModel), index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Avalonia.Screens.Device.ViewModels;
|
||||||
using Artemis.UI.Avalonia.Screens.Root.ViewModels;
|
using Artemis.UI.Avalonia.Screens.Root.ViewModels;
|
||||||
|
using Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Ninject.Factories
|
namespace Artemis.UI.Avalonia.Ninject.Factories
|
||||||
@ -8,10 +10,21 @@ namespace Artemis.UI.Avalonia.Ninject.Factories
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IDeviceVmFactory : IVmFactory
|
||||||
|
{
|
||||||
|
DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device);
|
||||||
|
}
|
||||||
|
|
||||||
public interface ISidebarVmFactory : IVmFactory
|
public interface ISidebarVmFactory : IVmFactory
|
||||||
{
|
{
|
||||||
SidebarViewModel SidebarViewModel(IScreen hostScreen);
|
SidebarViewModel SidebarViewModel(IScreen hostScreen);
|
||||||
SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory);
|
SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory);
|
||||||
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration);
|
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface SurfaceVmFactory : IVmFactory
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device);
|
||||||
|
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Screens.Device.ViewModels
|
||||||
|
{
|
||||||
|
public class DevicePropertiesViewModel : ActivatableViewModelBase
|
||||||
|
{
|
||||||
|
public DevicePropertiesViewModel(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
Device = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArtemisDevice Device { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:Artemis.UI.Avalonia.Shared.Controls;assembly=Artemis.UI.Avalonia.Shared"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||||
|
x:Class="Artemis.UI.Avalonia.Screens.Device.Views.DevicePropertiesView"
|
||||||
|
Title="Artemis | Device Properties"
|
||||||
|
Width="1200"
|
||||||
|
Height="800"
|
||||||
|
ExtendClientAreaToDecorationsHint="True">
|
||||||
|
<Grid ColumnDefinitions="*,0,*">
|
||||||
|
|
||||||
|
|
||||||
|
<Grid Grid.Column="0" Name="DeviceDisplayGrid">
|
||||||
|
<Grid.Background>
|
||||||
|
<VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,25,25">
|
||||||
|
<VisualBrush.Visual>
|
||||||
|
<Grid Width="25" Height="25" RowDefinitions="*,*" ColumnDefinitions="*,*">
|
||||||
|
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" />
|
||||||
|
<Rectangle Grid.Row="0" Grid.Column="1" />
|
||||||
|
<Rectangle Grid.Row="1" Grid.Column="0" />
|
||||||
|
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" />
|
||||||
|
</Grid>
|
||||||
|
</VisualBrush.Visual>
|
||||||
|
</VisualBrush>
|
||||||
|
</Grid.Background>
|
||||||
|
|
||||||
|
<!-- No need to provide LEDs to highlight as LEDs are already physically highlighted -->
|
||||||
|
<controls:DeviceVisualizer Device="{Binding Device}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
ShowColors="True"
|
||||||
|
Margin="20" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="15" IsVisible="{Binding Device.Layout.RgbLayout.Author}">
|
||||||
|
<TextBlock Classes="h5" Text="Device layout by " />
|
||||||
|
<TextBlock Classes="h5" FontWeight="Bold" Text="{Binding Device.Layout.RgbLayout.Author}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
<GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch"/>
|
||||||
|
|
||||||
|
<Border Grid.Column="2" Classes="card" CornerRadius="10 0 0 0">
|
||||||
|
<TabControl Margin="12" Items="{Binding Tabs}">
|
||||||
|
<TabControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding DisplayName}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ItemTemplate>
|
||||||
|
<TabControl.ContentTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Auto">
|
||||||
|
<ContentControl Content="{Binding}" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ContentTemplate>
|
||||||
|
</TabControl>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
using Artemis.UI.Avalonia.Screens.Device.ViewModels;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Screens.Device.Views
|
||||||
|
{
|
||||||
|
public partial class DevicePropertiesView : ReactiveWindow<DevicePropertiesViewModel>
|
||||||
|
{
|
||||||
|
public DevicePropertiesView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
#if DEBUG
|
||||||
|
this.AttachDevTools();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,54 +28,44 @@
|
|||||||
Margin="30"
|
Margin="30"
|
||||||
Text=" Welcome to Artemis, the unified RGB platform." />
|
Text=" Welcome to Artemis, the unified RGB platform." />
|
||||||
|
|
||||||
<Grid Grid.Row="1" MaxWidth="840" Margin="30" VerticalAlignment="Bottom">
|
<Grid Grid.Row="1" MaxWidth="840" Margin="30" VerticalAlignment="Bottom" ColumnDefinitions="*,*" RowDefinitions="*,*">
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition />
|
|
||||||
<ColumnDefinition />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition />
|
|
||||||
<RowDefinition />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Border Classes="card" Margin="8" Grid.ColumnSpan="2">
|
<Border Classes="card" Margin="8" Grid.ColumnSpan="2">
|
||||||
<Grid VerticalAlignment="Stretch" RowDefinitions="Auto,Auto" ColumnDefinitions="150,*">
|
<Grid VerticalAlignment="Stretch" RowDefinitions="Auto,Auto" ColumnDefinitions="150,*">
|
||||||
<avalonia:MaterialIcon Kind="Plug" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<avalonia:MaterialIcon Kind="Plug" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1">
|
<StackPanel Grid.Row="0" Grid.Column="1">
|
||||||
<TextBlock FontSize="24" Margin="16 16 16 8">Plugins</TextBlock>
|
<TextBlock Classes="h3">Plugins</TextBlock>
|
||||||
<TextBlock TextWrapping="Wrap" Margin="16 0 16 8" VerticalAlignment="Top">
|
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top">
|
||||||
Artemis is built up using plugins. This means devices, brushes, effects and modules (for supporting games!) can all be added via plugins.
|
Artemis is built up using plugins. This means devices, brushes, effects and modules (for supporting games) can all be added via plugins.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock TextWrapping="Wrap" Margin="16 0 16 8" VerticalAlignment="Top">
|
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top" Margin="0 15">
|
||||||
Under Settings > Plugins you can find your currently installed plugins, these default plugins are created by Artemis developers.
|
Under Settings > Plugins you can find your currently installed plugins, these default plugins are created by Artemis developers. We're also keeping track of a list of third-party plugins on our wiki.
|
||||||
</TextBlock>
|
|
||||||
<TextBlock TextWrapping="Wrap" Margin="16 0 16 8" VerticalAlignment="Top">
|
|
||||||
We're also keeping track of a list of third-party plugins on our wiki.
|
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Border Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" BorderThickness="0 1 0 0" BorderBrush="{DynamicResource MaterialDesignDivider}" Padding="8">
|
<controls:HyperlinkButton Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins" HorizontalAlignment="Right">
|
||||||
<controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins" HorizontalAlignment="Right">
|
<controls:HyperlinkButton.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="Test"></MenuItem>
|
||||||
|
</ContextMenu>
|
||||||
|
</controls:HyperlinkButton.ContextMenu>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<avalonia:MaterialIcon Kind="OpenInBrowser" />
|
<avalonia:MaterialIcon Kind="OpenInBrowser" />
|
||||||
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Get more plugins</TextBlock>
|
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Get more plugins</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</controls:HyperlinkButton>
|
</controls:HyperlinkButton>
|
||||||
</Border>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border Classes="card" Margin="8" Grid.Row="1" Grid.Column="0">
|
<Border Classes="card" Margin="8" Grid.Row="1" Grid.Column="0">
|
||||||
<Grid VerticalAlignment="Stretch" RowDefinitions="175,95" ColumnDefinitions="150,*">
|
<Grid VerticalAlignment="Stretch" RowDefinitions="150,95" ColumnDefinitions="150,*">
|
||||||
<avalonia:MaterialIcon Kind="Discord" Width="100" Height="100"
|
<avalonia:MaterialIcon Kind="Discord" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1">
|
<StackPanel Grid.Row="0" Grid.Column="1">
|
||||||
<TextBlock FontSize="24" Margin="16 16 16 8">Have a chat</TextBlock>
|
<TextBlock Classes="h3">Have a chat</TextBlock>
|
||||||
<TextBlock TextWrapping="Wrap" Margin="16 0 16 8" VerticalAlignment="Top">
|
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top">
|
||||||
If you need help, have some feedback or have any other questions feel free to contact us through any of the following channels.
|
If you need help, have some feedback or have any other questions feel free to contact us through any of the following channels.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Border Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" BorderThickness="0 1 0 0"
|
|
||||||
BorderBrush="{DynamicResource MaterialDesignDivider}" Padding="8">
|
<DockPanel Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0">
|
||||||
<DockPanel>
|
|
||||||
<Grid Margin="8" RowDefinitions="*,*">
|
<Grid Margin="8" RowDefinitions="*,*">
|
||||||
<controls:HyperlinkButton Grid.Row="0" NavigateUri="https://github.com/Artemis-RGB/Artemis">
|
<controls:HyperlinkButton Grid.Row="0" NavigateUri="https://github.com/Artemis-RGB/Artemis">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
@ -83,7 +73,7 @@
|
|||||||
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">GitHub</TextBlock>
|
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">GitHub</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</controls:HyperlinkButton>
|
</controls:HyperlinkButton>
|
||||||
<controls:HyperlinkButton Grid.Row="0" NavigateUri="https://artemis-rgb.com" HorizontalAlignment="Right">
|
<controls:HyperlinkButton Grid.Row="0" HorizontalAlignment="Right" NavigateUri="https://artemis-rgb.com">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<avalonia:MaterialIcon Kind="Web" />
|
<avalonia:MaterialIcon Kind="Web" />
|
||||||
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Website</TextBlock>
|
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Website</TextBlock>
|
||||||
@ -95,7 +85,7 @@
|
|||||||
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Discord</TextBlock>
|
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Discord</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</controls:HyperlinkButton>
|
</controls:HyperlinkButton>
|
||||||
<controls:HyperlinkButton Grid.Row="1" NavigateUri="mailto:spoinky.nl@gmail.com" HorizontalAlignment="Right">
|
<controls:HyperlinkButton Grid.Row="1" HorizontalAlignment="Right" NavigateUri="mailto:spoinky.nl@gmail.com">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<avalonia:MaterialIcon Kind="Email" />
|
<avalonia:MaterialIcon Kind="Email" />
|
||||||
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">E-mail</TextBlock>
|
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">E-mail</TextBlock>
|
||||||
@ -103,38 +93,37 @@
|
|||||||
</controls:HyperlinkButton>
|
</controls:HyperlinkButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Border>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Classes="card" Margin="8" Grid.Row="1" Grid.Column="1">
|
<Border Classes="card" Margin="8" Grid.Row="1" Grid.Column="1">
|
||||||
<Grid VerticalAlignment="Stretch" RowDefinitions="175,95" ColumnDefinitions="150,*">
|
<Grid VerticalAlignment="Stretch" RowDefinitions="150,95" ColumnDefinitions="150,*">
|
||||||
<avalonia:MaterialIcon Kind="Github" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
<avalonia:MaterialIcon Kind="Github" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1">
|
<StackPanel Grid.Row="0" Grid.Column="1">
|
||||||
<TextBlock FontSize="16" Margin="16 16 16 8">Open Source</TextBlock>
|
<TextBlock Classes="h3">Open Source</TextBlock>
|
||||||
<TextBlock TextWrapping="Wrap" Margin="16 0 16 8" VerticalAlignment="Top">
|
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top">
|
||||||
This project is completely open source. If you like it and want to say thanks you could hit the GitHub Star button, I like numbers. You could even make plugins, there's a full documentation on the website
|
This project is open source. If you like it and want to say thanks you could hit the GitHub Star button, I like numbers.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Border Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" BorderThickness="0 1 0 0"
|
|
||||||
BorderBrush="{DynamicResource MaterialDesignDivider}" Padding="8">
|
<controls:HyperlinkButton Grid.Row="1"
|
||||||
<DockPanel>
|
Grid.Column="0"
|
||||||
<controls:HyperlinkButton NavigateUri="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VQBAEJYUFLU4J"
|
HorizontalAlignment="Center"
|
||||||
HorizontalAlignment="Right">
|
NavigateUri="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VQBAEJYUFLU4J">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<avalonia:MaterialIcon Kind="Gift" />
|
<avalonia:MaterialIcon Kind="Gift" />
|
||||||
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Donate</TextBlock>
|
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Donate</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</controls:HyperlinkButton>
|
</controls:HyperlinkButton>
|
||||||
<TextBlock Foreground="{DynamicResource MaterialDesignBodyLight}"
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
Margin="16"
|
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center">
|
||||||
Feel like you want to make a donation? It would be gratefully received. Click the button to donate via PayPal.
|
Feel like making a donation? It would be gratefully received. Click the button to donate via PayPal.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</DockPanel>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -14,13 +14,13 @@
|
|||||||
<ContentControl Grid.Column="0" Content="{Binding SidebarViewModel}" />
|
<ContentControl Grid.Column="0" Content="{Binding SidebarViewModel}" />
|
||||||
|
|
||||||
<Border Classes="router-container" Grid.Column="1" Margin="0 40 0 0">
|
<Border Classes="router-container" Grid.Column="1" Margin="0 40 0 0">
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" >
|
<!-- <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" > -->
|
||||||
<reactiveUi:RoutedViewHost Router="{Binding Router}">
|
<reactiveUi:RoutedViewHost Router="{Binding Router}">
|
||||||
<reactiveUi:RoutedViewHost.PageTransition>
|
<reactiveUi:RoutedViewHost.PageTransition>
|
||||||
<CrossFade Duration="0.1" />
|
<CrossFade Duration="0.1" />
|
||||||
</reactiveUi:RoutedViewHost.PageTransition>
|
</reactiveUi:RoutedViewHost.PageTransition>
|
||||||
</reactiveUi:RoutedViewHost>
|
</reactiveUi:RoutedViewHost>
|
||||||
</ScrollViewer>
|
<!-- </ScrollViewer> -->
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -12,7 +12,9 @@
|
|||||||
</TabControl.ItemTemplate>
|
</TabControl.ItemTemplate>
|
||||||
<TabControl.ContentTemplate>
|
<TabControl.ContentTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Auto">
|
||||||
<ContentControl Content="{Binding}" />
|
<ContentControl Content="{Binding}" />
|
||||||
|
</ScrollViewer>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</TabControl.ContentTemplate>
|
</TabControl.ContentTemplate>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using ReactiveUI;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels
|
||||||
|
{
|
||||||
|
public class ListDeviceViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private SKColor _color;
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public ListDeviceViewModel(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
Device = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArtemisDevice Device { get; }
|
||||||
|
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _isSelected, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SKColor Color
|
||||||
|
{
|
||||||
|
get => _color;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _color, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,140 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.Avalonia.Ninject.Factories;
|
||||||
|
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using ReactiveUI;
|
||||||
|
using RGB.NET.Core;
|
||||||
|
using SkiaSharp;
|
||||||
|
using Point = Avalonia.Point;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels
|
||||||
|
{
|
||||||
|
public class SurfaceDeviceViewModel : ActivatableViewModelBase
|
||||||
|
{
|
||||||
|
private readonly IRgbService _rgbService;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||||
|
private readonly IWindowService _windowService;
|
||||||
|
private Cursor _cursor;
|
||||||
|
private double _dragOffsetX;
|
||||||
|
private double _dragOffsetY;
|
||||||
|
private SelectionStatus _selectionStatus;
|
||||||
|
|
||||||
|
public SurfaceDeviceViewModel(ArtemisDevice device, IRgbService rgbService, IDeviceService deviceService, ISettingsService settingsService, IDeviceVmFactory deviceVmFactory, IWindowService windowService)
|
||||||
|
{
|
||||||
|
_rgbService = rgbService;
|
||||||
|
_deviceService = deviceService;
|
||||||
|
_settingsService = settingsService;
|
||||||
|
_deviceVmFactory = deviceVmFactory;
|
||||||
|
_windowService = windowService;
|
||||||
|
|
||||||
|
Device = device;
|
||||||
|
|
||||||
|
IdentifyDevice = ReactiveCommand.Create<ArtemisDevice>(ExecuteIdentifyDevice);
|
||||||
|
ViewProperties = ReactiveCommand.CreateFromTask<ArtemisDevice>(ExecuteViewProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReactiveCommand<ArtemisDevice, Unit> IdentifyDevice { get; }
|
||||||
|
public ReactiveCommand<ArtemisDevice, Unit> ViewProperties { get; }
|
||||||
|
|
||||||
|
public ArtemisDevice Device { get; }
|
||||||
|
|
||||||
|
public SelectionStatus SelectionStatus
|
||||||
|
{
|
||||||
|
get => _selectionStatus;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref _selectionStatus, value);
|
||||||
|
this.RaisePropertyChanged(nameof(Highlighted));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Highlighted => SelectionStatus != SelectionStatus.None;
|
||||||
|
|
||||||
|
public bool CanDetectInput => Device.DeviceType == RGBDeviceType.Keyboard || Device.DeviceType == RGBDeviceType.Mouse;
|
||||||
|
|
||||||
|
public Cursor Cursor
|
||||||
|
{
|
||||||
|
get => _cursor;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _cursor, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartMouseDrag(Point mouseStartPosition)
|
||||||
|
{
|
||||||
|
if (SelectionStatus != SelectionStatus.Selected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_dragOffsetX = Device.X - mouseStartPosition.X;
|
||||||
|
_dragOffsetY = Device.Y - mouseStartPosition.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateMouseDrag(Point mousePosition)
|
||||||
|
{
|
||||||
|
if (SelectionStatus != SelectionStatus.Selected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float roundedX = (float) Math.Round((mousePosition.X + _dragOffsetX) / 10d, 0, MidpointRounding.AwayFromZero) * 10f;
|
||||||
|
float roundedY = (float) Math.Round((mousePosition.Y + _dragOffsetY) / 10d, 0, MidpointRounding.AwayFromZero) * 10f;
|
||||||
|
|
||||||
|
if (Fits(roundedX, roundedY))
|
||||||
|
{
|
||||||
|
Device.X = roundedX;
|
||||||
|
Device.Y = roundedY;
|
||||||
|
}
|
||||||
|
else if (Fits(roundedX, Device.Y))
|
||||||
|
{
|
||||||
|
Device.X = roundedX;
|
||||||
|
}
|
||||||
|
else if (Fits(Device.X, roundedY))
|
||||||
|
{
|
||||||
|
Device.Y = roundedY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteIdentifyDevice(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
_deviceService.IdentifyDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteViewProperties(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
await _windowService.ShowDialog<bool>(_deviceVmFactory.DevicePropertiesViewModel(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Fits(float x, float y)
|
||||||
|
{
|
||||||
|
if (x < 0 || y < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double maxTextureSize = 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value;
|
||||||
|
if (x + Device.Rectangle.Width > maxTextureSize || y + Device.Rectangle.Height > maxTextureSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
List<SKRect> own = Device.Leds
|
||||||
|
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height))
|
||||||
|
.ToList();
|
||||||
|
List<SKRect> others = _rgbService.EnabledDevices
|
||||||
|
.Where(d => d != Device && d.IsEnabled)
|
||||||
|
.SelectMany(d => d.Leds)
|
||||||
|
.Select(l => SKRect.Create(l.Rectangle.Left + l.Device.X, l.Rectangle.Top + l.Device.Y, l.Rectangle.Width, l.Rectangle.Height))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
|
||||||
|
return !own.Any(o => others.Any(l => l.IntersectsWith(o)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SelectionStatus
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Hover,
|
||||||
|
Selected
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,36 +1,188 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.Avalonia.Extensions;
|
||||||
|
using Artemis.UI.Avalonia.Ninject.Factories;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Skia;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels
|
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels
|
||||||
{
|
{
|
||||||
public class SurfaceEditorViewModel : MainScreenViewModel
|
public class SurfaceEditorViewModel : MainScreenViewModel
|
||||||
{
|
{
|
||||||
public SurfaceEditorViewModel(IScreen hostScreen, IRgbService rgbService) : base(hostScreen, "surface-editor")
|
private readonly IInputService _inputService;
|
||||||
|
private readonly IRgbService _rgbService;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
private bool _saving;
|
||||||
|
|
||||||
|
public SurfaceEditorViewModel(IScreen hostScreen,
|
||||||
|
IRgbService rgbService,
|
||||||
|
SurfaceVmFactory surfaceVmFactory,
|
||||||
|
IInputService inputService,
|
||||||
|
ISettingsService settingsService) : base(hostScreen, "surface-editor")
|
||||||
{
|
{
|
||||||
|
_rgbService = rgbService;
|
||||||
|
_inputService = inputService;
|
||||||
|
_settingsService = settingsService;
|
||||||
DisplayName = "Surface Editor";
|
DisplayName = "Surface Editor";
|
||||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.Devices);
|
SurfaceDeviceViewModels = new ObservableCollection<SurfaceDeviceViewModel>(rgbService.Devices.Select(surfaceVmFactory.SurfaceDeviceViewModel));
|
||||||
|
ListDeviceViewModels = new ObservableCollection<ListDeviceViewModel>(rgbService.Devices.Select(surfaceVmFactory.ListDeviceViewModel));
|
||||||
|
|
||||||
|
BringToFront = ReactiveCommand.Create<ArtemisDevice>(ExecuteBringToFront);
|
||||||
|
BringForward = ReactiveCommand.Create<ArtemisDevice>(ExecuteBringForward);
|
||||||
|
SendToBack = ReactiveCommand.Create<ArtemisDevice>(ExecuteSendToBack);
|
||||||
|
SendBackward = ReactiveCommand.Create<ArtemisDevice>(ExecuteSendBackward);
|
||||||
|
|
||||||
UpdateSelection = ReactiveCommand.Create<Rect>(ExecuteUpdateSelection);
|
UpdateSelection = ReactiveCommand.Create<Rect>(ExecuteUpdateSelection);
|
||||||
ApplySelection = ReactiveCommand.Create<Rect>(ExecuteApplySelection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<ArtemisDevice> Devices { get; }
|
public ObservableCollection<SurfaceDeviceViewModel> SurfaceDeviceViewModels { get; }
|
||||||
|
public ObservableCollection<ListDeviceViewModel> ListDeviceViewModels { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<ArtemisDevice, Unit> BringToFront { get; }
|
||||||
|
public ReactiveCommand<ArtemisDevice, Unit> BringForward { get; }
|
||||||
|
public ReactiveCommand<ArtemisDevice, Unit> SendToBack { get; }
|
||||||
|
public ReactiveCommand<ArtemisDevice, Unit> SendBackward { get; }
|
||||||
|
|
||||||
public ReactiveCommand<Rect, Unit> UpdateSelection { get; }
|
public ReactiveCommand<Rect, Unit> UpdateSelection { get; }
|
||||||
public ReactiveCommand<Rect, Unit> ApplySelection { get; }
|
|
||||||
|
public double MaxTextureSize => 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value;
|
||||||
|
|
||||||
|
public void ClearSelection()
|
||||||
|
{
|
||||||
|
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
|
||||||
|
surfaceDeviceViewModel.SelectionStatus = SelectionStatus.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartMouseDrag(Point mousePosition)
|
||||||
|
{
|
||||||
|
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
|
||||||
|
surfaceDeviceViewModel.StartMouseDrag(mousePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateMouseDrag(Point mousePosition)
|
||||||
|
{
|
||||||
|
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
|
||||||
|
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopMouseDrag(Point mousePosition)
|
||||||
|
{
|
||||||
|
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
|
||||||
|
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition);
|
||||||
|
|
||||||
|
if (_saving)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: Figure out why the UI still locks up here
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_saving = true;
|
||||||
|
_rgbService.SaveDevices();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_saving = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void ExecuteUpdateSelection(Rect rect)
|
private void ExecuteUpdateSelection(Rect rect)
|
||||||
{
|
{
|
||||||
|
SKRect hitTestRect = rect.ToSKRect();
|
||||||
}
|
foreach (SurfaceDeviceViewModel device in SurfaceDeviceViewModels)
|
||||||
|
|
||||||
private void ExecuteApplySelection(Rect rect)
|
|
||||||
{
|
{
|
||||||
|
if (device.Device.Rectangle.IntersectsWith(hitTestRect))
|
||||||
|
device.SelectionStatus = SelectionStatus.Selected;
|
||||||
|
else if (!_inputService.IsKeyDown(KeyboardKey.LeftShift) && !_inputService.IsKeyDown(KeyboardKey.RightShift))
|
||||||
|
device.SelectionStatus = SelectionStatus.None;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
ApplySurfaceSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplySurfaceSelection()
|
||||||
|
{
|
||||||
|
foreach (ListDeviceViewModel viewModel in ListDeviceViewModels)
|
||||||
|
viewModel.IsSelected = SurfaceDeviceViewModels.Any(s => s.Device == viewModel.Device && s.SelectionStatus == SelectionStatus.Selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Context menu commands
|
||||||
|
|
||||||
|
private void ExecuteBringToFront(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
|
||||||
|
SurfaceDeviceViewModels.Move(SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel), SurfaceDeviceViewModels.Count - 1);
|
||||||
|
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
|
||||||
|
deviceViewModel.Device.ZIndex = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
|
_rgbService.SaveDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteBringForward(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
|
||||||
|
int currentIndex = SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel);
|
||||||
|
int newIndex = Math.Min(currentIndex + 1, SurfaceDeviceViewModels.Count - 1);
|
||||||
|
SurfaceDeviceViewModels.Move(currentIndex, newIndex);
|
||||||
|
|
||||||
|
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
|
||||||
|
deviceViewModel.Device.ZIndex = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
|
_rgbService.SaveDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteSendToBack(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
|
||||||
|
SurfaceDeviceViewModels.Move(SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel), 0);
|
||||||
|
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
|
||||||
|
deviceViewModel.Device.ZIndex = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
|
_rgbService.SaveDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteSendBackward(ArtemisDevice device)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
|
||||||
|
int currentIndex = SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel);
|
||||||
|
int newIndex = Math.Max(currentIndex - 1, 0);
|
||||||
|
SurfaceDeviceViewModels.Move(currentIndex, newIndex);
|
||||||
|
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
|
||||||
|
{
|
||||||
|
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
|
||||||
|
deviceViewModel.Device.ZIndex = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
|
||||||
|
|
||||||
|
_rgbService.SaveDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Avalonia.Screens.SurfaceEditor.Views.ListDeviceView">
|
||||||
|
Welcome to Avalonia!
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.Views
|
||||||
|
{
|
||||||
|
public partial class ListDeviceView : UserControl
|
||||||
|
{
|
||||||
|
public ListDeviceView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:Artemis.UI.Avalonia.Shared.Controls;assembly=Artemis.UI.Avalonia.Shared"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Avalonia.Screens.SurfaceEditor.Views.SurfaceDeviceView">
|
||||||
|
<Grid>
|
||||||
|
<Grid.Styles>
|
||||||
|
<Style Selector="Border.selection-border">
|
||||||
|
<Setter Property="Opacity" Value="0" />
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Transitions>
|
||||||
|
<DoubleTransition Property="Opacity" Duration="0:0:0.2" />
|
||||||
|
</Transitions>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Border.selection-border-selected">
|
||||||
|
<Setter Property="Opacity" Value="1" />
|
||||||
|
</Style>
|
||||||
|
</Grid.Styles>
|
||||||
|
|
||||||
|
<controls:DeviceVisualizer Device="{Binding Device}" ShowColors="True"/>
|
||||||
|
<Border x:Name="SurfaceDeviceBorder"
|
||||||
|
Classes="selection-border"
|
||||||
|
Classes.selection-border-selected="{Binding Highlighted}"
|
||||||
|
BorderThickness="1">
|
||||||
|
<Border.BorderBrush>
|
||||||
|
<SolidColorBrush Color="{DynamicResource SystemAccentColor}"></SolidColorBrush>
|
||||||
|
</Border.BorderBrush>
|
||||||
|
<Border.Background>
|
||||||
|
<SolidColorBrush Color="{DynamicResource SystemAccentColor}" Opacity="0.2"></SolidColorBrush>
|
||||||
|
</Border.Background>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
using Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.Views
|
||||||
|
{
|
||||||
|
public class SurfaceDeviceView : ReactiveUserControl<SurfaceDeviceViewModel>
|
||||||
|
{
|
||||||
|
public SurfaceDeviceView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Overrides of InputElement
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnPointerEnter(PointerEventArgs e)
|
||||||
|
{
|
||||||
|
if (ViewModel?.SelectionStatus == SelectionStatus.None)
|
||||||
|
{
|
||||||
|
ViewModel.SelectionStatus = SelectionStatus.Hover;
|
||||||
|
Cursor = new Cursor(StandardCursorType.Hand);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnPointerEnter(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnPointerLeave(PointerEventArgs e)
|
||||||
|
{
|
||||||
|
if (ViewModel?.SelectionStatus == SelectionStatus.Hover)
|
||||||
|
{
|
||||||
|
ViewModel.SelectionStatus = SelectionStatus.None;
|
||||||
|
Cursor = new Cursor(StandardCursorType.Arrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnPointerLeave(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && ViewModel != null)
|
||||||
|
ViewModel.SelectionStatus = SelectionStatus.Selected;
|
||||||
|
|
||||||
|
base.OnPointerPressed(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,8 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:controls="clr-namespace:Artemis.UI.Avalonia.Shared.Controls;assembly=Artemis.UI.Avalonia.Shared"
|
xmlns:controls="clr-namespace:Artemis.UI.Avalonia.Shared.Controls;assembly=Artemis.UI.Avalonia.Shared"
|
||||||
xmlns:paz="using:Avalonia.Controls.PanAndZoom"
|
xmlns:paz="clr-namespace:Avalonia.Controls.PanAndZoom;assembly=Avalonia.Controls.PanAndZoom"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Avalonia.Screens.SurfaceEditor.Views.SurfaceEditorView">
|
x:Class="Artemis.UI.Avalonia.Screens.SurfaceEditor.Views.SurfaceEditorView">
|
||||||
|
|
||||||
@ -14,7 +15,10 @@
|
|||||||
Focusable="True"
|
Focusable="True"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
ZoomChanged="ZoomBorder_OnZoomChanged">
|
ZoomChanged="ZoomBorder_OnZoomChanged"
|
||||||
|
PointerPressed="ZoomBorder_OnPointerPressed"
|
||||||
|
PointerMoved="ZoomBorder_OnPointerMoved"
|
||||||
|
PointerReleased="ZoomBorder_OnPointerReleased">
|
||||||
<paz:ZoomBorder.Background>
|
<paz:ZoomBorder.Background>
|
||||||
<VisualBrush TileMode="Tile" Stretch="Uniform" SourceRect="0,0,25,25">
|
<VisualBrush TileMode="Tile" Stretch="Uniform" SourceRect="0,0,25,25">
|
||||||
<VisualBrush.Visual>
|
<VisualBrush.Visual>
|
||||||
@ -27,12 +31,13 @@
|
|||||||
</VisualBrush.Visual>
|
</VisualBrush.Visual>
|
||||||
</VisualBrush>
|
</VisualBrush>
|
||||||
</paz:ZoomBorder.Background>
|
</paz:ZoomBorder.Background>
|
||||||
<Grid Background="Transparent">
|
<Grid Name="ContainerGrid"
|
||||||
<ItemsControl Items="{Binding Devices}">
|
Background="Transparent">
|
||||||
|
<ItemsControl Items="{Binding SurfaceDeviceViewModels}" ClipToBounds="False">
|
||||||
<ItemsControl.Styles>
|
<ItemsControl.Styles>
|
||||||
<Style Selector="ContentPresenter">
|
<Style Selector="ContentPresenter">
|
||||||
<Setter Property="Canvas.Left" Value="{Binding X}" />
|
<Setter Property="Canvas.Left" Value="{Binding Device.X}" />
|
||||||
<Setter Property="Canvas.Top" Value="{Binding Y}" />
|
<Setter Property="Canvas.Top" Value="{Binding Device.Y}" />
|
||||||
</Style>
|
</Style>
|
||||||
</ItemsControl.Styles>
|
</ItemsControl.Styles>
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
@ -42,17 +47,68 @@
|
|||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<controls:DeviceVisualizer Device="{Binding}" ShowColors="True" />
|
<ContentControl Content="{Binding}">
|
||||||
|
<ContentControl.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuItem Header="Identify" Command="{Binding IdentifyDevice}" CommandParameter="{Binding Device}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<avalonia:MaterialIcon Kind="AlarmLight" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="-" />
|
||||||
|
<MenuItem Header="Bring to Front" Command="{Binding $parent[4].DataContext.BringToFront}" CommandParameter="{Binding Device}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<avalonia:MaterialIcon Kind="ArrangeBringToFront" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Bring Forward" Command="{Binding $parent[4].DataContext.BringForward}" CommandParameter="{Binding Device}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<avalonia:MaterialIcon Kind="ArrangeBringForward" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Send to Back" Command="{Binding $parent[4].DataContext.SendToBack}" CommandParameter="{Binding Device}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<avalonia:MaterialIcon Kind="ArrangeSendToBack" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Send Backward" Command="{Binding $parent[4].DataContext.SendBackward}" CommandParameter="{Binding Device}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<avalonia:MaterialIcon Kind="ArrangeSendBackward" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="-" />
|
||||||
|
<MenuItem Header="Identify input"
|
||||||
|
Command="{Binding DetectInput}"
|
||||||
|
CommandParameter="{Binding Device}"
|
||||||
|
ToolTip.Tip="Teach Artemis which keypresses and/or button presses belong to this device">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<avalonia:MaterialIcon Kind="GestureDoubleTap" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="View properties" Command="{Binding ViewProperties}" CommandParameter="{Binding Device}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<avalonia:MaterialIcon Kind="Gear" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuFlyout>
|
||||||
|
</ContentControl.ContextFlyout>
|
||||||
|
</ContentControl>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
<controls:SelectionRectangle Name="SelectionRectangle"
|
<controls:SelectionRectangle Name="SelectionRectangle"
|
||||||
InputElement="{Binding #ZoomBorder}"
|
InputElement="{Binding #ZoomBorder}"
|
||||||
SelectionUpdated="{Binding UpdateSelection}"
|
SelectionUpdated="{Binding UpdateSelection}" />
|
||||||
SelectionFinished="{Binding ApplySelection}"/>
|
|
||||||
|
<Border Name="SurfaceBounds"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Width="{Binding MaxTextureSize}"
|
||||||
|
Height="{Binding MaxTextureSize}"
|
||||||
|
BorderThickness="2"
|
||||||
|
BorderBrush="{DynamicResource SystemAccentColorDark3}"
|
||||||
|
CornerRadius="8" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</paz:ZoomBorder>
|
</paz:ZoomBorder>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -3,23 +3,30 @@ using Artemis.UI.Avalonia.Shared.Controls;
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.PanAndZoom;
|
using Avalonia.Controls.PanAndZoom;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.Views
|
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.Views
|
||||||
{
|
{
|
||||||
public class SurfaceEditorView : ReactiveUserControl<SurfaceEditorViewModel>
|
public class SurfaceEditorView : ReactiveUserControl<SurfaceEditorViewModel>
|
||||||
{
|
{
|
||||||
private readonly SelectionRectangle _selectionRectangle;
|
private readonly SelectionRectangle _selectionRectangle;
|
||||||
|
private readonly Grid _containerGrid;
|
||||||
private readonly ZoomBorder _zoomBorder;
|
private readonly ZoomBorder _zoomBorder;
|
||||||
|
private readonly Border _surfaceBounds;
|
||||||
|
|
||||||
public SurfaceEditorView()
|
public SurfaceEditorView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_zoomBorder = this.Find<ZoomBorder>("ZoomBorder");
|
_zoomBorder = this.Find<ZoomBorder>("ZoomBorder");
|
||||||
|
_containerGrid = this.Find<Grid>("ContainerGrid");
|
||||||
_selectionRectangle = this.Find<SelectionRectangle>("SelectionRectangle");
|
_selectionRectangle = this.Find<SelectionRectangle>("SelectionRectangle");
|
||||||
|
_surfaceBounds = this.Find<Border>("SurfaceBounds");
|
||||||
|
|
||||||
|
|
||||||
((VisualBrush) _zoomBorder.Background).DestinationRect = new RelativeRect(_zoomBorder.OffsetX * -1, _zoomBorder.OffsetY * -1, 20, 20, RelativeUnit.Absolute);
|
((VisualBrush) _zoomBorder.Background).DestinationRect = new RelativeRect(_zoomBorder.OffsetX * -1, _zoomBorder.OffsetY * -1, 20, 20, RelativeUnit.Absolute);
|
||||||
}
|
}
|
||||||
@ -33,6 +40,43 @@ namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.Views
|
|||||||
{
|
{
|
||||||
((VisualBrush) _zoomBorder.Background).DestinationRect = new RelativeRect(_zoomBorder.OffsetX * -1, _zoomBorder.OffsetY * -1, 20, 20, RelativeUnit.Absolute);
|
((VisualBrush) _zoomBorder.Background).DestinationRect = new RelativeRect(_zoomBorder.OffsetX * -1, _zoomBorder.OffsetY * -1, 20, 20, RelativeUnit.Absolute);
|
||||||
_selectionRectangle.BorderThickness = 1 / _zoomBorder.ZoomX;
|
_selectionRectangle.BorderThickness = 1 / _zoomBorder.ZoomX;
|
||||||
|
_surfaceBounds.BorderThickness = new Thickness(2 / _zoomBorder.ZoomX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ZoomBorder_OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (e.Source is Border {Name: "SurfaceDeviceBorder"})
|
||||||
|
{
|
||||||
|
e.Pointer.Capture(_zoomBorder);
|
||||||
|
e.Handled = true;
|
||||||
|
ViewModel?.StartMouseDrag(e.GetPosition(_containerGrid));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ViewModel?.ClearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ZoomBorder_OnPointerMoved(object? sender, PointerEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ReferenceEquals(e.Pointer.Captured, sender))
|
||||||
|
ViewModel?.UpdateMouseDrag(e.GetPosition(_containerGrid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ZoomBorder_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.InitialPressMouseButton != MouseButton.Left)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ReferenceEquals(e.Pointer.Captured, sender))
|
||||||
|
{
|
||||||
|
ViewModel?.StopMouseDrag(e.GetPosition(_containerGrid));
|
||||||
|
e.Pointer.Capture(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<Setter Property="FontSize" Value="24" />
|
<Setter Property="FontSize" Value="24" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="TextBlock.h5">
|
<Style Selector="TextBlock.h5">
|
||||||
<Setter Property="FontSize" Value="16" />
|
<Setter Property="FontSize" Value="18" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="TextBlock.h6">
|
<Style Selector="TextBlock.h6">
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using ReactiveUI;
|
using System;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Avalonia
|
namespace Artemis.UI.Avalonia
|
||||||
{
|
{
|
||||||
@ -13,8 +15,35 @@ namespace Artemis.UI.Avalonia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel
|
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel, IDisposable
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected ActivatableViewModelBase()
|
||||||
|
{
|
||||||
|
this.WhenActivated(disposables =>
|
||||||
|
{
|
||||||
|
Disposable.Create(Dispose).DisposeWith(disposables);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public ViewModelActivator Activator { get; } = new();
|
public ViewModelActivator Activator { get; } = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user