mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Basic implementation of a few editor tools
This commit is contained in:
parent
580db3185e
commit
8539f05d90
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Models.Profile.LayerShapes;
|
||||
using Artemis.Core.Models.Surface;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using SkiaSharp;
|
||||
@ -72,6 +73,7 @@ namespace Artemis.Core.Models.Profile
|
||||
public Layer AddLayer(string name)
|
||||
{
|
||||
var layer = new Layer(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1};
|
||||
layer.LayerShape = new Fill(layer);
|
||||
AddChild(layer);
|
||||
return layer;
|
||||
}
|
||||
|
||||
@ -97,7 +97,8 @@ namespace Artemis.Core.Models.Profile
|
||||
set
|
||||
{
|
||||
_layerShape = value;
|
||||
_layerShape.CalculateRenderProperties();
|
||||
if (Path != null)
|
||||
_layerShape.CalculateRenderProperties();
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,15 +147,18 @@ namespace Artemis.Core.Models.Profile
|
||||
LayerEntity.Condition.Clear();
|
||||
|
||||
// Brush
|
||||
LayerEntity.BrushEntity = new BrushEntity
|
||||
if (LayerBrush != null)
|
||||
{
|
||||
BrushPluginGuid = LayerBrush.Descriptor.LayerBrushProvider.PluginInfo.Guid,
|
||||
BrushType = LayerBrush.GetType().Name,
|
||||
Configuration = JsonConvert.SerializeObject(LayerBrush.Settings)
|
||||
};
|
||||
LayerEntity.BrushEntity = new BrushEntity
|
||||
{
|
||||
BrushPluginGuid = LayerBrush.Descriptor.LayerBrushProvider.PluginInfo.Guid,
|
||||
BrushType = LayerBrush.GetType().Name,
|
||||
Configuration = JsonConvert.SerializeObject(LayerBrush.Settings)
|
||||
};
|
||||
}
|
||||
|
||||
// Shape
|
||||
LayerShape.ApplyToEntity();
|
||||
LayerShape?.ApplyToEntity();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -67,6 +67,11 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
}
|
||||
|
||||
public Folder GetRootFolder()
|
||||
{
|
||||
return (Folder) Children.Single();
|
||||
}
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
{
|
||||
ProfileEntity.Id = EntityId;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AppDomainToolkit;
|
||||
using Artemis.Core.Events;
|
||||
using Artemis.Core.Exceptions;
|
||||
@ -215,8 +216,19 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
|
||||
// Get the Plugin implementation from the main assembly and if there is only one, instantiate it
|
||||
var mainAssembly = pluginInfo.Context.Domain.GetAssemblies().First(a => a.Location == mainFile);
|
||||
var pluginTypes = mainAssembly.GetTypes().Where(t => typeof(Plugin).IsAssignableFrom(t)).ToList();
|
||||
List<Type> pluginTypes;
|
||||
var mainAssembly = pluginInfo.Context.Domain.GetAssemblies().FirstOrDefault(a => a.Location == mainFile);
|
||||
if (mainAssembly == null)
|
||||
throw new ArtemisPluginException(pluginInfo, "Found no supported assembly in the plugins main file");
|
||||
try
|
||||
{
|
||||
pluginTypes = mainAssembly.GetTypes().Where(t => typeof(Plugin).IsAssignableFrom(t)).ToList();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
throw new ArtemisPluginException(pluginInfo, "Failed to initialize the plugin assembly", new AggregateException(e.LoaderExceptions));
|
||||
}
|
||||
|
||||
if (pluginTypes.Count > 1)
|
||||
throw new ArtemisPluginException(pluginInfo, $"Plugin contains {pluginTypes.Count} implementations of Plugin, only 1 allowed");
|
||||
if (pluginTypes.Count == 0)
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Events;
|
||||
using Artemis.Core.Plugins.Models;
|
||||
using Artemis.Core.RGB.NET;
|
||||
using Artemis.Core.Services.Interfaces;
|
||||
using RGB.NET.Brushes;
|
||||
using RGB.NET.Brushes.Gradients;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Groups;
|
||||
using Serilog;
|
||||
@ -104,7 +106,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
// Apply the application wide brush and decorator
|
||||
BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value));
|
||||
_surfaceLedGroup = new ListLedGroup(Surface.Leds) { Brush = BitmapBrush };
|
||||
_surfaceLedGroup = new ListLedGroup(Surface.Leds) {Brush = BitmapBrush};
|
||||
return;
|
||||
}
|
||||
|
||||
@ -115,11 +117,11 @@ namespace Artemis.Core.Services
|
||||
|
||||
// Apply the application wide brush and decorator
|
||||
BitmapBrush.Scale = new Scale(_renderScaleSetting.Value);
|
||||
_surfaceLedGroup = new ListLedGroup(Surface.Leds) { Brush = BitmapBrush };
|
||||
_surfaceLedGroup = new ListLedGroup(Surface.Leds) {Brush = BitmapBrush};
|
||||
}
|
||||
|
||||
lock (BitmapBrush)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,14 +50,6 @@
|
||||
<Compile Include="WootingDeviceProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="x64\wooting-rgb-sdk64.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\wooting-rgb-sdk.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
@ -69,6 +61,10 @@
|
||||
<Name>Artemis.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="x64\" />
|
||||
<Folder Include="x86\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>echo Copying resources to plugin output directory
|
||||
|
||||
@ -172,6 +172,7 @@
|
||||
<Compile Include="Converters\NullToImageConverter.cs" />
|
||||
<Compile Include="Converters\NullToVisibilityConverter.cs" />
|
||||
<Compile Include="Events\MainWindowFocusChangedEvent.cs" />
|
||||
<Compile Include="Events\MainWindowKeyEvent.cs" />
|
||||
<Compile Include="Events\WindowsThemeEventArgs.cs" />
|
||||
<Compile Include="Screens\GradientEditor\GradientEditorViewModel.cs" />
|
||||
<Compile Include="Screens\Module\ProfileEditor\LayerProperties\LayerPropertiesView.xaml.cs">
|
||||
@ -222,8 +223,8 @@
|
||||
<Compile Include="Screens\Sidebar\SidebarViewModel.cs" />
|
||||
<Compile Include="Services\Interfaces\IProfileEditorService.cs" />
|
||||
<Compile Include="Services\ProfileEditorService.cs" />
|
||||
<Compile Include="Utilities\BindableSelectedItemBehavior.cs" />
|
||||
<Compile Include="Utilities\ThemeWatcher.cs" />
|
||||
<Compile Include="Behaviors\TreeViewSelectionBehavior.cs" />
|
||||
<Compile Include="Utilities\TriggerTracing.cs" />
|
||||
<Compile Include="Exceptions\ArtemisCoreException.cs" />
|
||||
<Compile Include="Extensions\RgbColorExtensions.cs" />
|
||||
@ -520,6 +521,7 @@
|
||||
<ItemGroup>
|
||||
<None Include="Resources\aero_fill.cur" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
||||
174
src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs
Normal file
174
src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs
Normal file
@ -0,0 +1,174 @@
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interactivity;
|
||||
|
||||
namespace Artemis.UI.Behaviors
|
||||
{
|
||||
public class TreeViewSelectionBehavior : Behavior<TreeView>
|
||||
{
|
||||
public delegate bool IsChildOfPredicate(object nodeA, object nodeB);
|
||||
|
||||
public static readonly DependencyProperty SelectedItemProperty =
|
||||
DependencyProperty.Register(nameof(SelectedItem), typeof(object),
|
||||
typeof(TreeViewSelectionBehavior),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
OnSelectedItemChanged));
|
||||
|
||||
public static readonly DependencyProperty HierarchyPredicateProperty =
|
||||
DependencyProperty.Register(nameof(HierarchyPredicate), typeof(IsChildOfPredicate),
|
||||
typeof(TreeViewSelectionBehavior),
|
||||
new FrameworkPropertyMetadata(null));
|
||||
|
||||
public static readonly DependencyProperty ExpandSelectedProperty =
|
||||
DependencyProperty.Register(nameof(ExpandSelected), typeof(bool),
|
||||
typeof(TreeViewSelectionBehavior),
|
||||
new FrameworkPropertyMetadata(false));
|
||||
|
||||
private readonly EventSetter _treeViewItemEventSetter;
|
||||
private bool _modelHandled;
|
||||
|
||||
public TreeViewSelectionBehavior()
|
||||
{
|
||||
_treeViewItemEventSetter = new EventSetter(
|
||||
FrameworkElement.LoadedEvent,
|
||||
new RoutedEventHandler(OnTreeViewItemLoaded));
|
||||
}
|
||||
|
||||
// Bindable selected item
|
||||
public object SelectedItem
|
||||
{
|
||||
get => GetValue(SelectedItemProperty);
|
||||
set => SetValue(SelectedItemProperty, value);
|
||||
}
|
||||
|
||||
// Predicate that checks if two items are hierarchically related
|
||||
public IsChildOfPredicate HierarchyPredicate
|
||||
{
|
||||
get => (IsChildOfPredicate) GetValue(HierarchyPredicateProperty);
|
||||
set => SetValue(HierarchyPredicateProperty, value);
|
||||
}
|
||||
|
||||
// Should expand selected?
|
||||
public bool ExpandSelected
|
||||
{
|
||||
get => (bool) GetValue(ExpandSelectedProperty);
|
||||
set => SetValue(ExpandSelectedProperty, value);
|
||||
}
|
||||
|
||||
private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
var behavior = (TreeViewSelectionBehavior) sender;
|
||||
if (behavior._modelHandled) return;
|
||||
|
||||
if (behavior.AssociatedObject == null)
|
||||
return;
|
||||
|
||||
behavior._modelHandled = true;
|
||||
behavior.UpdateAllTreeViewItems();
|
||||
behavior._modelHandled = false;
|
||||
}
|
||||
|
||||
// Update state of all items starting with given, with optional recursion
|
||||
private void UpdateTreeViewItem(TreeViewItem item, bool recurse)
|
||||
{
|
||||
if (SelectedItem == null) return;
|
||||
var model = item.DataContext;
|
||||
|
||||
// If the selected item is this model and is not yet selected - select and return
|
||||
if (SelectedItem == model && !item.IsSelected)
|
||||
{
|
||||
item.IsSelected = true;
|
||||
if (ExpandSelected)
|
||||
item.IsExpanded = true;
|
||||
}
|
||||
// If the selected item is a parent of this model - expand
|
||||
else
|
||||
{
|
||||
var isParentOfModel = HierarchyPredicate?.Invoke(SelectedItem, model) ?? true;
|
||||
if (isParentOfModel)
|
||||
item.IsExpanded = true;
|
||||
}
|
||||
|
||||
// Recurse into children
|
||||
if (recurse)
|
||||
{
|
||||
foreach (var subitem in item.Items)
|
||||
{
|
||||
var tvi = item.ItemContainerGenerator.ContainerFromItem(subitem) as TreeViewItem;
|
||||
if (tvi != null)
|
||||
UpdateTreeViewItem(tvi, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update state of all items
|
||||
private void UpdateAllTreeViewItems()
|
||||
{
|
||||
var treeView = AssociatedObject;
|
||||
foreach (var item in treeView.Items)
|
||||
{
|
||||
var tvi = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
|
||||
if (tvi != null)
|
||||
UpdateTreeViewItem(tvi, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Inject Loaded event handler into ItemContainerStyle
|
||||
private void UpdateTreeViewItemStyle()
|
||||
{
|
||||
if (AssociatedObject.ItemContainerStyle == null)
|
||||
{
|
||||
AssociatedObject.ItemContainerStyle = new Style(
|
||||
typeof(TreeViewItem),
|
||||
Application.Current.TryFindResource(typeof(TreeViewItem)) as Style);
|
||||
}
|
||||
|
||||
if (!AssociatedObject.ItemContainerStyle.Setters.Contains(_treeViewItemEventSetter))
|
||||
AssociatedObject.ItemContainerStyle.Setters.Add(_treeViewItemEventSetter);
|
||||
}
|
||||
|
||||
private void OnTreeViewItemsChanged(object sender,
|
||||
NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
UpdateAllTreeViewItems();
|
||||
}
|
||||
|
||||
private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> args)
|
||||
{
|
||||
if (_modelHandled) return;
|
||||
if (AssociatedObject.Items.SourceCollection == null) return;
|
||||
SelectedItem = args.NewValue;
|
||||
}
|
||||
|
||||
private void OnTreeViewItemLoaded(object sender, RoutedEventArgs args)
|
||||
{
|
||||
UpdateTreeViewItem((TreeViewItem) sender, false);
|
||||
}
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
base.OnAttached();
|
||||
|
||||
AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
|
||||
((INotifyCollectionChanged) AssociatedObject.Items).CollectionChanged += OnTreeViewItemsChanged;
|
||||
|
||||
UpdateTreeViewItemStyle();
|
||||
_modelHandled = true;
|
||||
UpdateAllTreeViewItems();
|
||||
_modelHandled = false;
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
base.OnDetaching();
|
||||
|
||||
if (AssociatedObject != null)
|
||||
{
|
||||
AssociatedObject.ItemContainerStyle?.Setters?.Remove(_treeViewItemEventSetter);
|
||||
AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
|
||||
((INotifyCollectionChanged) AssociatedObject.Items).CollectionChanged -= OnTreeViewItemsChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/Artemis.UI/Events/MainWindowKeyEvent.cs
Normal file
16
src/Artemis.UI/Events/MainWindowKeyEvent.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Artemis.UI.Events
|
||||
{
|
||||
public class MainWindowKeyEvent
|
||||
{
|
||||
public bool KeyDown { get; }
|
||||
public KeyEventArgs EventArgs { get; }
|
||||
|
||||
public MainWindowKeyEvent(bool keyDown, KeyEventArgs eventArgs)
|
||||
{
|
||||
KeyDown = keyDown;
|
||||
EventArgs = eventArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,9 +7,9 @@
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:dd="urn:gong-wpf-dragdrop"
|
||||
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
|
||||
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
|
||||
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.ProfileTree"
|
||||
xmlns:treeItem="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem"
|
||||
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type profileTree:ProfileTreeViewModel}}">
|
||||
@ -33,7 +33,7 @@
|
||||
dd:DragDrop.IsDropTarget="True"
|
||||
dd:DragDrop.DropHandler="{Binding}">
|
||||
<i:Interaction.Behaviors>
|
||||
<utilities:BindableSelectedItemBehavior SelectedItem="{Binding SelectedTreeItem, Mode=TwoWay}" />
|
||||
<behaviors:TreeViewSelectionBehavior ExpandSelected="True" SelectedItem="{Binding SelectedTreeItem}" />
|
||||
</i:Interaction.Behaviors>
|
||||
<TreeView.Resources>
|
||||
<HierarchicalDataTemplate DataType="{x:Type treeItem:FolderViewModel}" ItemsSource="{Binding Children}">
|
||||
|
||||
@ -2,10 +2,12 @@
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using Artemis.Core.Models.Profile;
|
||||
using Artemis.UI.Behaviors;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using GongSolutions.Wpf.DragDrop;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
|
||||
{
|
||||
@ -13,12 +15,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IFolderViewModelFactory _folderViewModelFactory;
|
||||
private readonly ILayerViewModelFactory _layerViewModelFactory;
|
||||
private TreeItemViewModel _selectedTreeItem;
|
||||
|
||||
public ProfileTreeViewModel(IProfileEditorService profileEditorService, IFolderViewModelFactory folderViewModelFactory)
|
||||
public ProfileTreeViewModel(IProfileEditorService profileEditorService,
|
||||
IFolderViewModelFactory folderViewModelFactory,
|
||||
ILayerViewModelFactory layerViewModelFactory)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_folderViewModelFactory = folderViewModelFactory;
|
||||
_layerViewModelFactory = layerViewModelFactory;
|
||||
|
||||
CreateRootFolderViewModel();
|
||||
_profileEditorService.SelectedProfileChanged += OnSelectedProfileChanged;
|
||||
@ -139,6 +145,27 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
|
||||
|
||||
// Don't set it using the setter or that will trigger the event again
|
||||
_selectedTreeItem = vms?.FirstOrDefault(vm => vm.ProfileElement == _profileEditorService.SelectedProfileElement);
|
||||
|
||||
if (_selectedTreeItem == null && _profileEditorService.SelectedProfileElement != null)
|
||||
{
|
||||
var parent = vms?.FirstOrDefault(vm => vm.ProfileElement == _profileEditorService.SelectedProfileElement.Parent);
|
||||
if (parent == null)
|
||||
{
|
||||
// Eh.. we did our best.. start over
|
||||
CreateRootFolderViewModel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new TreeItemViewModel for the new ProfileElement
|
||||
TreeItemViewModel treeItemViewModel;
|
||||
if (_profileEditorService.SelectedProfileElement is Folder folder)
|
||||
treeItemViewModel = _folderViewModelFactory.Create(parent, folder);
|
||||
else
|
||||
treeItemViewModel = _layerViewModelFactory.Create(parent, (Layer) _profileEditorService.SelectedProfileElement);
|
||||
parent.AddExistingElement(treeItemViewModel);
|
||||
_selectedTreeItem = treeItemViewModel;
|
||||
}
|
||||
|
||||
NotifyOfPropertyChange(() => SelectedTreeItem);
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
</Path>
|
||||
|
||||
<!-- Selection -->
|
||||
<Path Data="{Binding DisplayGeometry, Mode=OneWay}" ClipToBounds="False" StrokeThickness="2">
|
||||
<Path Data="{Binding DisplayGeometry, Mode=OneWay}" ClipToBounds="False" StrokeThickness="1">
|
||||
<Path.Style>
|
||||
<Style TargetType="{x:Type Path}">
|
||||
<Style.Triggers>
|
||||
@ -83,10 +83,10 @@
|
||||
</Style>
|
||||
</Path.Style>
|
||||
<Path.Fill>
|
||||
<SolidColorBrush Opacity="0.65" />
|
||||
<SolidColorBrush Opacity="0.25" />
|
||||
</Path.Fill>
|
||||
<Path.Stroke>
|
||||
<SolidColorBrush />
|
||||
<SolidColorBrush Opacity="0.5"/>
|
||||
</Path.Stroke>
|
||||
</Path>
|
||||
</Canvas>
|
||||
|
||||
@ -46,17 +46,17 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ToolBarTray Orientation="Vertical" Width="58">
|
||||
<ToolBar Style="{DynamicResource MaterialDesignToolBar}" ClipToBounds="False" ToolBarTray.IsLocked="True" >
|
||||
<ToolBar Style="{DynamicResource MaterialDesignToolBar}" ClipToBounds="False" ToolBarTray.IsLocked="True">
|
||||
<ListBox SelectedIndex="{Binding ActiveToolIndex}" ToolBar.OverflowMode="Never">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"/>
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBoxItem ToolTip="Pan over different parts of the surface" >
|
||||
<ListBoxItem ToolTip="Pan over different parts of the surface">
|
||||
<materialDesign:PackIcon Kind="HandLeft" />
|
||||
</ListBoxItem>
|
||||
<ListBoxItem ToolTip="Change layer selection" >
|
||||
<ListBoxItem ToolTip="Change layer selection">
|
||||
<materialDesign:PackIcon Kind="SelectionDrag" />
|
||||
</ListBoxItem>
|
||||
<ListBoxItem ToolTip="Add to layer selection">
|
||||
@ -84,11 +84,9 @@
|
||||
</ToolBarTray>
|
||||
<Grid Grid.Column="1"
|
||||
ClipToBounds="True"
|
||||
KeyUp="{s:Action CanvasKeyUp}"
|
||||
KeyDown="{s:Action CanvasKeyDown}"
|
||||
MouseWheel="{s:Action CanvasMouseWheel}"
|
||||
MouseUp="{s:Action CanvasMouseDown}"
|
||||
MouseDown="{s:Action CanvasMouseUp}"
|
||||
MouseUp="{s:Action CanvasMouseUp}"
|
||||
MouseDown="{s:Action CanvasMouseDown}"
|
||||
MouseMove="{s:Action CanvasMouseMove}"
|
||||
Cursor="{Binding ActiveToolViewModel.Cursor}"
|
||||
utilities:SizeObserver.Observe="True"
|
||||
@ -123,7 +121,7 @@
|
||||
<TranslateTransform X="{Binding PanZoomViewModel.PanX}" Y="{Binding PanZoomViewModel.PanY}" />
|
||||
</TransformGroup>
|
||||
</Grid.RenderTransform>
|
||||
<ItemsControl ItemsSource="{Binding Devices}">
|
||||
<ItemsControl ItemsSource="{Binding CanvasViewModels}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Canvas />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using Artemis.Core.Events;
|
||||
using Artemis.Core.Models.Profile;
|
||||
using Artemis.Core.Models.Surface;
|
||||
@ -19,11 +19,14 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
{
|
||||
public class ProfileViewModel : ProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>
|
||||
public class ProfileViewModel : ProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent>
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
private int _activeToolIndex;
|
||||
private VisualizationToolViewModel _activeToolViewModel;
|
||||
private int _previousTool;
|
||||
private TimerUpdateTrigger _updateTrigger;
|
||||
|
||||
public ProfileViewModel(IProfileEditorService profileEditorService, ISurfaceService surfaceService, ISettingsService settingsService, IEventAggregator eventAggregator)
|
||||
@ -66,12 +69,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
{
|
||||
// Remove the tool from the canvas
|
||||
if (_activeToolViewModel != null)
|
||||
{
|
||||
CanvasViewModels.Remove(_activeToolViewModel);
|
||||
NotifyOfPropertyChange(() => CanvasViewModels);
|
||||
}
|
||||
|
||||
// Set the new tool
|
||||
_activeToolViewModel = value;
|
||||
// Add the new tool to the canvas
|
||||
if (_activeToolViewModel != null)
|
||||
{
|
||||
CanvasViewModels.Add(_activeToolViewModel);
|
||||
NotifyOfPropertyChange(() => CanvasViewModels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,8 +90,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
get => _activeToolIndex;
|
||||
set
|
||||
{
|
||||
_activeToolIndex = value;
|
||||
ActivateToolByIndex(value);
|
||||
if (_activeToolIndex != value)
|
||||
{
|
||||
_activeToolIndex = value;
|
||||
ActivateToolByIndex(value);
|
||||
NotifyOfPropertyChange(() => ActiveToolIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,19 +229,21 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
case 3:
|
||||
ActiveToolViewModel = new SelectionRemoveToolViewModel(this, _profileEditorService);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
ActiveToolViewModel = new EllipseToolViewModel(this, _profileEditorService);
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
ActiveToolViewModel = new RectangleToolViewModel(this, _profileEditorService);
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
ActiveToolViewModel = new PolygonToolViewModel(this, _profileEditorService);
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
ActiveToolViewModel = new FillToolViewModel(this, _profileEditorService);
|
||||
break;
|
||||
}
|
||||
|
||||
ActiveToolIndex = value;
|
||||
}
|
||||
|
||||
public void ResetZoomAndPan()
|
||||
@ -256,6 +272,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
|
||||
public void CanvasMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
PanZoomViewModel.ProcessMouseScroll(sender, e);
|
||||
ActiveToolViewModel?.MouseWheel(sender, e);
|
||||
}
|
||||
|
||||
@ -300,27 +317,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keys
|
||||
|
||||
private int _previousTool;
|
||||
private int _activeToolIndex;
|
||||
private VisualizationToolViewModel _activeToolViewModel;
|
||||
|
||||
public void CanvasKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
_previousTool = ActiveToolIndex;
|
||||
if ((e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl) && e.IsDown)
|
||||
ActiveToolViewModel = new ViewpointMoveToolViewModel(this, _profileEditorService);
|
||||
}
|
||||
|
||||
public void CanvasKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if ((e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl) && e.IsUp)
|
||||
ActivateToolByIndex(_previousTool);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void HighlightSelectedLayerOnSettingChanged(object sender, EventArgs e)
|
||||
@ -352,6 +348,29 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(MainWindowKeyEvent message)
|
||||
{
|
||||
Debug.WriteLine(message.KeyDown);
|
||||
if (message.KeyDown)
|
||||
{
|
||||
if (ActiveToolIndex != 0)
|
||||
{
|
||||
_previousTool = ActiveToolIndex;
|
||||
if ((message.EventArgs.Key == Key.LeftCtrl || message.EventArgs.Key == Key.RightCtrl) && message.EventArgs.IsDown)
|
||||
ActivateToolByIndex(0);
|
||||
}
|
||||
|
||||
ActiveToolViewModel?.KeyDown(message.EventArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((message.EventArgs.Key == Key.LeftCtrl || message.EventArgs.Key == Key.RightCtrl) && message.EventArgs.IsUp)
|
||||
ActivateToolByIndex(_previousTool);
|
||||
|
||||
ActiveToolViewModel?.KeyUp(message.EventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -8,23 +8,30 @@
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Canvas>
|
||||
<Canvas.Triggers>
|
||||
<EventTrigger RoutedEvent="Canvas.MouseLeftButtonDown">
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetName="Preview" TargetProperty="Opacity">
|
||||
<DoubleAnimation From="0" To="1" Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
<EventTrigger RoutedEvent="Canvas.MouseLeftButtonUp">
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetName="Preview" TargetProperty="Opacity">
|
||||
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
</Canvas.Triggers>
|
||||
<UserControl.Resources>
|
||||
<Style TargetType="Shape" x:Key="ShowIfMouseDown">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsMouseDown}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetProperty="Opacity">
|
||||
<DoubleAnimation From="0" To="1" Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetProperty="Opacity">
|
||||
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Canvas Background="Transparent" Width="50" Height="50">
|
||||
|
||||
<Canvas.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Ellipse action 1" Command="{s:Action CreateLayer}" CommandParameter="{Binding}">
|
||||
@ -39,7 +46,14 @@
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Canvas.ContextMenu>
|
||||
<Ellipse Stroke="{DynamicResource PrimaryHueLightBrush}" StrokeThickness="1" IsHitTestVisible="False" x:Name="Preview" Opacity="1" Width="100" Height="100">
|
||||
<Ellipse Style="{StaticResource ShowIfMouseDown}"
|
||||
Width="{Binding DragRectangle.Width}"
|
||||
Height="{Binding DragRectangle.Height}"
|
||||
Canvas.Left="{Binding DragRectangle.X}"
|
||||
Canvas.Top="{Binding DragRectangle.Y}"
|
||||
Stroke="{DynamicResource PrimaryHueLightBrush}"
|
||||
StrokeThickness="1"
|
||||
Opacity="0">
|
||||
<Ellipse.Fill>
|
||||
<SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" />
|
||||
</Ellipse.Fill>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Artemis.UI.Properties;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
@ -7,6 +8,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
{
|
||||
public class EllipseToolViewModel : VisualizationToolViewModel
|
||||
{
|
||||
private bool _shiftDown;
|
||||
|
||||
public EllipseToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService)
|
||||
{
|
||||
using (var stream = new MemoryStream(Resources.aero_crosshair))
|
||||
@ -14,5 +17,37 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
Cursor = new Cursor(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect DragRectangle { get; set; }
|
||||
|
||||
public override void MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
base.MouseMove(sender, e);
|
||||
|
||||
if (!IsMouseDown)
|
||||
return;
|
||||
|
||||
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
|
||||
if (!_shiftDown)
|
||||
DragRectangle = new Rect(MouseDownStartPosition, position);
|
||||
else
|
||||
DragRectangle = GetSquareRectBetweenPoints(MouseDownStartPosition, position);
|
||||
}
|
||||
|
||||
public override void KeyUp(KeyEventArgs e)
|
||||
{
|
||||
base.KeyUp(e);
|
||||
|
||||
if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
|
||||
_shiftDown = false;
|
||||
}
|
||||
|
||||
public override void KeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.KeyDown(e);
|
||||
|
||||
if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
|
||||
_shiftDown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,59 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<UserControl.Resources>
|
||||
<Style TargetType="Shape" x:Key="ShowIfMouseDown">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsMouseDown}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetProperty="Opacity">
|
||||
<DoubleAnimation From="0" To="1" Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetProperty="Opacity">
|
||||
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Canvas Background="Transparent" Width="50" Height="50">
|
||||
|
||||
</Grid>
|
||||
<Canvas.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Ellipse action 1" Command="{s:Action CreateLayer}" CommandParameter="{Binding}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="LayersPlus" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Ellipse action 2" Command="{s:Action ApplyToLayer}" CommandParameter="{Binding}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="Selection" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Canvas.ContextMenu>
|
||||
<Rectangle Style="{StaticResource ShowIfMouseDown}"
|
||||
Width="{Binding DragRectangle.Width}"
|
||||
Height="{Binding DragRectangle.Height}"
|
||||
Canvas.Left="{Binding DragRectangle.X}"
|
||||
Canvas.Top="{Binding DragRectangle.Y}"
|
||||
Stroke="{DynamicResource PrimaryHueLightBrush}"
|
||||
StrokeThickness="1"
|
||||
Opacity="0">
|
||||
<Rectangle.Fill>
|
||||
<SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" />
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
</Canvas>
|
||||
</UserControl>
|
||||
@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Artemis.UI.Properties;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
@ -7,6 +8,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
{
|
||||
public class RectangleToolViewModel : VisualizationToolViewModel
|
||||
{
|
||||
private bool _shiftDown;
|
||||
|
||||
public RectangleToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService)
|
||||
{
|
||||
using (var stream = new MemoryStream(Resources.aero_crosshair))
|
||||
@ -14,5 +17,37 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
Cursor = new Cursor(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect DragRectangle { get; set; }
|
||||
|
||||
public override void MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
base.MouseMove(sender, e);
|
||||
|
||||
if (!IsMouseDown)
|
||||
return;
|
||||
|
||||
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
|
||||
if (!_shiftDown)
|
||||
DragRectangle = new Rect(MouseDownStartPosition, position);
|
||||
else
|
||||
DragRectangle = GetSquareRectBetweenPoints(MouseDownStartPosition, position);
|
||||
}
|
||||
|
||||
public override void KeyUp(KeyEventArgs e)
|
||||
{
|
||||
base.KeyUp(e);
|
||||
|
||||
if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
|
||||
_shiftDown = false;
|
||||
}
|
||||
|
||||
public override void KeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.KeyDown(e);
|
||||
|
||||
if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
|
||||
_shiftDown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,60 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<UserControl.Resources>
|
||||
<Style TargetType="Shape" x:Key="ShowIfMouseDown">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsMouseDown}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetProperty="Opacity">
|
||||
<DoubleAnimation From="0" To="1" Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard TargetProperty="Opacity">
|
||||
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Canvas Background="Transparent" Width="50" Height="50">
|
||||
|
||||
</Grid>
|
||||
<Canvas.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Ellipse action 1" Command="{s:Action CreateLayer}" CommandParameter="{Binding}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="LayersPlus" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Ellipse action 2" Command="{s:Action ApplyToLayer}" CommandParameter="{Binding}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="Selection" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Canvas.ContextMenu>
|
||||
<Rectangle Style="{StaticResource ShowIfMouseDown}"
|
||||
Width="{Binding DragRectangle.Width}"
|
||||
Height="{Binding DragRectangle.Height}"
|
||||
Canvas.Left="{Binding DragRectangle.X}"
|
||||
Canvas.Top="{Binding DragRectangle.Y}"
|
||||
Stroke="{DynamicResource PrimaryHueLightBrush}"
|
||||
StrokeDashArray="4 4"
|
||||
StrokeThickness="1"
|
||||
Opacity="0">
|
||||
<Rectangle.Fill>
|
||||
<SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" />
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
</Canvas>
|
||||
</UserControl>
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core.Models.Profile;
|
||||
using Artemis.Core.Models.Surface;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Properties;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
|
||||
@ -16,49 +20,79 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
}
|
||||
}
|
||||
|
||||
public override void MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
base.MouseDown(sender, e);
|
||||
// ProfileViewModel.SelectionRectangle.Rect = new Rect();
|
||||
}
|
||||
public Rect DragRectangle { get; set; }
|
||||
|
||||
|
||||
public override void MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
base.MouseUp(sender, e);
|
||||
|
||||
var position = e.GetPosition((IInputElement) sender);
|
||||
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
|
||||
var selectedRect = new Rect(MouseDownStartPosition, position);
|
||||
|
||||
// Get selected LEDs
|
||||
var selectedLeds = new List<ArtemisLed>();
|
||||
foreach (var device in ProfileViewModel.Devices)
|
||||
{
|
||||
foreach (var ledViewModel in device.Leds)
|
||||
{
|
||||
if (ProfileViewModel.PanZoomViewModel.TransformContainingRect(ledViewModel.Led.RgbLed.AbsoluteLedRectangle).IntersectsWith(selectedRect))
|
||||
ledViewModel.IsSelected = true;
|
||||
else if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
|
||||
ledViewModel.IsSelected = false;
|
||||
if (ledViewModel.Led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1).IntersectsWith(selectedRect))
|
||||
selectedLeds.Add(ledViewModel.Led);
|
||||
// Unselect everything
|
||||
ledViewModel.IsSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the selection to the selected layer layer
|
||||
if (ProfileEditorService.SelectedProfileElement is Layer layer)
|
||||
{
|
||||
layer.ClearLeds();
|
||||
layer.AddLeds(selectedLeds);
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
// If no layer selected, apply it to a new layer in the selected folder
|
||||
else if (ProfileEditorService.SelectedProfileElement is Folder folder)
|
||||
{
|
||||
var newLayer = folder.AddLayer("New layer");
|
||||
newLayer.AddLeds(selectedLeds);
|
||||
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
// If no folder selected, apply it to a new layer in the root folder
|
||||
else
|
||||
{
|
||||
var rootFolder = ProfileEditorService.SelectedProfile.GetRootFolder();
|
||||
var newLayer = rootFolder.AddLayer("New layer");
|
||||
newLayer.AddLeds(selectedLeds);
|
||||
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
|
||||
public override void MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
base.MouseMove(sender, e);
|
||||
if (!IsMouseDown)
|
||||
{
|
||||
DragRectangle = new Rect(-1, -1, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var position = e.GetPosition((IInputElement) sender);
|
||||
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
|
||||
var selectedRect = new Rect(MouseDownStartPosition, position);
|
||||
// ProfileViewModel.SelectionRectangle.Rect = selectedRect;
|
||||
|
||||
foreach (var device in ProfileViewModel.Devices)
|
||||
{
|
||||
foreach (var ledViewModel in device.Leds)
|
||||
{
|
||||
if (ProfileViewModel.PanZoomViewModel.TransformContainingRect(ledViewModel.Led.RgbLed.AbsoluteLedRectangle).IntersectsWith(selectedRect))
|
||||
if (ledViewModel.Led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1).IntersectsWith(selectedRect))
|
||||
ledViewModel.IsSelected = true;
|
||||
else if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
|
||||
ledViewModel.IsSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
DragRectangle = selectedRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,19 +8,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
public ViewpointMoveToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService)
|
||||
{
|
||||
Cursor = Cursors.Hand;
|
||||
ProfileViewModel.PanZoomViewModel.LastPanPosition = null;
|
||||
}
|
||||
|
||||
public override void MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
base.MouseMove(sender, e);
|
||||
if (IsMouseDown)
|
||||
ProfileViewModel.PanZoomViewModel.ProcessMouseMove(sender, e);
|
||||
}
|
||||
|
||||
public override void MouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
base.MouseWheel(sender, e);
|
||||
ProfileViewModel.PanZoomViewModel.ProcessMouseScroll(sender, e);
|
||||
ProfileViewModel.PanZoomViewModel.ProcessMouseMove(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Windows;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
|
||||
@ -26,7 +27,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
public virtual void MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
IsMouseDown = true;
|
||||
MouseDownStartPosition = e.GetPosition((IInputElement) sender);
|
||||
MouseDownStartPosition = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
|
||||
}
|
||||
|
||||
public virtual void MouseUp(object sender, MouseButtonEventArgs e)
|
||||
@ -41,5 +42,28 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
|
||||
public virtual void MouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void KeyUp(KeyEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void KeyDown(KeyEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
protected Rect GetSquareRectBetweenPoints(Point start, Point end)
|
||||
{
|
||||
// Find the shortest side
|
||||
var size = Math.Min(Math.Abs(start.X - end.X), Math.Abs(start.Y - end.Y));
|
||||
|
||||
// There's probably a very elegant way to do this, and this is not it
|
||||
if (end.X < start.X && end.Y < start.Y)
|
||||
return new Rect(start.X - size, start.Y - size, size, size);
|
||||
if (end.X < start.X)
|
||||
return new Rect(start.X - size, Math.Min(start.Y, end.Y), size, size);
|
||||
if (end.Y < start.Y)
|
||||
return new Rect(Math.Min(start.X, end.X), start.Y - size, size, size);
|
||||
return new Rect(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y), size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,8 @@
|
||||
UseLayoutRounding="True"
|
||||
Deactivated="{s:Action WindowDeactivated}"
|
||||
Activated="{s:Action WindowActivated}"
|
||||
KeyDown="{s:Action WindowKeyDown}"
|
||||
KeyUp="{s:Action WindowKeyUp}"
|
||||
d:DesignHeight="640"
|
||||
d:DesignWidth="1200"
|
||||
d:DataContext="{d:DesignInstance screens:RootViewModel}">
|
||||
|
||||
@ -3,6 +3,7 @@ using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Artemis.UI.Events;
|
||||
using Artemis.UI.Screens.Sidebar;
|
||||
using Artemis.UI.Utilities;
|
||||
@ -80,5 +81,15 @@ namespace Artemis.UI.Screens
|
||||
_lostFocus = false;
|
||||
_eventAggregator.Publish(new MainWindowFocusChangedEvent(true));
|
||||
}
|
||||
|
||||
public void WindowKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
_eventAggregator.Publish(new MainWindowKeyEvent(true, e));
|
||||
}
|
||||
|
||||
public void WindowKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
_eventAggregator.Publish(new MainWindowKeyEvent(false, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ namespace Artemis.UI.Screens.Shared
|
||||
{
|
||||
public class PanZoomViewModel : PropertyChangedBase
|
||||
{
|
||||
private Point? _lastPanPosition;
|
||||
public Point? LastPanPosition { get; set; }
|
||||
public double Zoom { get; set; } = 1;
|
||||
|
||||
public double ZoomPercentage
|
||||
@ -51,20 +51,20 @@ namespace Artemis.UI.Screens.Shared
|
||||
{
|
||||
if (e.LeftButton == MouseButtonState.Released)
|
||||
{
|
||||
_lastPanPosition = null;
|
||||
LastPanPosition = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lastPanPosition == null)
|
||||
_lastPanPosition = e.GetPosition((IInputElement) sender);
|
||||
if (LastPanPosition == null)
|
||||
LastPanPosition = e.GetPosition((IInputElement) sender);
|
||||
|
||||
var position = e.GetPosition((IInputElement) sender);
|
||||
var delta = _lastPanPosition - position;
|
||||
var delta = LastPanPosition - position;
|
||||
|
||||
PanX = Math.Min(0, PanX - delta.Value.X);
|
||||
PanY = Math.Min(0, PanY - delta.Value.Y);
|
||||
|
||||
_lastPanPosition = position;
|
||||
LastPanPosition = position;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interactivity;
|
||||
|
||||
namespace Artemis.UI.Utilities
|
||||
{
|
||||
public class BindableSelectedItemBehavior : Behavior<TreeView>
|
||||
{
|
||||
protected override void OnAttached()
|
||||
{
|
||||
base.OnAttached();
|
||||
|
||||
AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
base.OnDetaching();
|
||||
|
||||
if (AssociatedObject != null) AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
|
||||
}
|
||||
|
||||
private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
SelectedItem = e.NewValue;
|
||||
}
|
||||
|
||||
#region SelectedItem Property
|
||||
|
||||
public object SelectedItem
|
||||
{
|
||||
get => GetValue(SelectedItemProperty);
|
||||
set => SetValue(SelectedItemProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectedItemProperty =
|
||||
DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged));
|
||||
|
||||
private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var item = e.NewValue as TreeViewItem;
|
||||
if (item != null) item.SetValue(TreeViewItem.IsSelectedProperty, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user