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

Added device identification to surface editor

Fixed dialog rendering
Cleaned up XAML
This commit is contained in:
SpoinkyNL 2019-11-22 20:46:34 +01:00
parent 733c9bf1a5
commit 25f8f1e72f
24 changed files with 165 additions and 79 deletions

View File

@ -188,6 +188,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RGB.NET\DirectBitmap.cs" />
<Compile Include="RGB.NET\GraphicsDecorator.cs" />
<Compile Include="Services\DeviceService.cs" />
<Compile Include="Services\Interfaces\IProtectedArtemisService.cs" />
<Compile Include="Services\Interfaces\IMainDataModelService.cs" />
<Compile Include="Services\CoreService.cs" />

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Artemis.Core.Events;
using Artemis.Core.Models.Surface;
using Artemis.Core.Services.Interfaces;
namespace Artemis.Core.Services
{
public class DeviceService : IDeviceService
{
private readonly ICoreService _coreService;
public DeviceService(ICoreService coreService)
{
_coreService = coreService;
}
public void IdentifyDevice(Device device)
{
BlinkDevice(device, 0);
}
private void BlinkDevice(Device device, int blinkCount)
{
// Draw a white overlay over the device
void DrawOverlay(object sender, FrameRenderingEventArgs args)
{
using (var g = Graphics.FromImage(args.Bitmap))
{
g.FillPath(new SolidBrush(Color.White), device.RenderPath);
}
}
_coreService.FrameRendering += DrawOverlay;
// After 200ms, stop drawing the overlay
Task.Run(async () =>
{
await Task.Delay(200);
_coreService.FrameRendering -= DrawOverlay;
if (blinkCount < 5)
{
// After another 200ms, draw the overlay again, repeat six times
await Task.Delay(200);
BlinkDevice(device, blinkCount + 1);
}
});
}
}
public interface IDeviceService : IArtemisService
{
/// <summary>
/// Identifies the device by making it blink white 5 times
/// </summary>
/// <param name="device"></param>
void IdentifyDevice(Device device);
}
}

View File

@ -151,6 +151,8 @@
<Compile Include="Converters\NullToVisibilityConverter.cs" />
<Compile Include="Extensions\RgbColorExtensions.cs" />
<Compile Include="Extensions\RgbRectangleExtensions.cs" />
<Compile Include="Ninject\Factories\IArtemisUIFactory.cs" />
<Compile Include="Ninject\Factories\IDeviceSettingsViewModelFactory.cs" />
<Compile Include="Ninject\Factories\IModuleViewModelFactory.cs" />
<Compile Include="Ninject\Factories\IProfileEditorViewModelFactory.cs" />
<Compile Include="Ninject\UIModule.cs" />

View File

@ -0,0 +1,6 @@
namespace Artemis.UI.Ninject.Factories
{
public interface IArtemisUIFactory
{
}
}

View File

@ -0,0 +1,10 @@
using Artemis.Core.Models.Surface;
using Artemis.UI.Screens.Settings.Tabs.Devices;
namespace Artemis.UI.Ninject.Factories
{
public interface IDeviceSettingsViewModelFactory : IArtemisUIFactory
{
DeviceSettingsViewModel Create(Device device);
}
}

View File

@ -3,8 +3,8 @@ using Artemis.UI.Screens.Module;
namespace Artemis.UI.Ninject.Factories
{
public interface IModuleViewModelFactory
public interface IModuleViewModelFactory : IArtemisUIFactory
{
ModuleRootViewModel CreateModuleViewModel(Module module);
ModuleRootViewModel Create(Module module);
}
}

View File

@ -3,8 +3,8 @@ using Artemis.UI.Screens.Module.ProfileEditor;
namespace Artemis.UI.Ninject.Factories
{
public interface IProfileEditorViewModelFactory
public interface IProfileEditorViewModelFactory : IArtemisUIFactory
{
ProfileEditorViewModel CreateModuleViewModel(ProfileModule module);
ProfileEditorViewModel Create(ProfileModule module);
}
}

View File

@ -1,4 +1,5 @@
using Artemis.UI.Ninject.Factories;
using System;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens;
using Artemis.UI.Screens.Module.ProfileEditor;
using Artemis.UI.Services.Interfaces;
@ -17,6 +18,9 @@ namespace Artemis.UI.Ninject
{
public override void Load()
{
if (Kernel == null)
throw new ArgumentNullException("Kernel shouldn't be null here.");
// Bind all built-in VMs
Kernel.Bind(x =>
{
@ -35,10 +39,19 @@ namespace Artemis.UI.Ninject
.BindAllBaseClasses();
});
// Bind the module VM
Bind<IModuleViewModelFactory>().ToFactory();
Bind<IProfileEditorViewModelFactory>().ToFactory();
// Bind UI factories
Kernel.Bind(x =>
{
x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<IArtemisUIFactory>()
.BindToFactory();
});
Kernel.Bind<IDeviceSettingsViewModelFactory>().ToFactory();
Kernel.Bind<IModuleViewModelFactory>().ToFactory();
Kernel.Bind<IProfileEditorViewModelFactory>().ToFactory();
// Bind profile editor VMs
Kernel.Bind(x =>
{

View File

@ -11,8 +11,7 @@
d:DataContext="{d:DesignInstance dialogs:ConfirmDialogViewModel}">
<StackPanel Margin="16">
<TextBlock Style="{StaticResource MaterialDesignTitleTextBlock}" Text="{Binding Header}" TextWrapping="Wrap" />
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Margin="0 20 0 20" Text="{Binding Text}"
TextWrapping="Wrap" />
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Margin="0 20 0 20" Text="{Binding Text}" TextWrapping="Wrap" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0"

View File

@ -30,7 +30,7 @@ namespace Artemis.UI.Screens.Module
// Create the profile editor and module VMs
if (Module is ProfileModule profileModule)
{
var profileEditor = _profileEditorViewModelFactory.CreateModuleViewModel(profileModule);
var profileEditor = _profileEditorViewModelFactory.Create(profileModule);
Items.Add(profileEditor);
}

View File

@ -12,8 +12,10 @@
Add a new profile
</TextBlock>
<TextBox materialDesign:HintAssist.Hint="Profile name" Margin="0 8 0 16"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" Text="{Binding ProfileName}" />
<TextBox materialDesign:HintAssist.Hint="Profile name"
Margin="0 8 0 16"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding ProfileName, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Cancel}">

View File

@ -135,7 +135,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
profiles = profiles.Where(p => p.EntityId != activeProfile.EntityId).ToList();
profiles.Add(activeProfile);
Execute.OnUIThread(() =>
Execute.PostToUIThread(() =>
{
// Populate the UI collection
Profiles.Clear();

View File

@ -20,7 +20,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
Width = Led.ActualSize.Width;
Height = Led.ActualSize.Height;
Execute.OnUIThread(CreateLedGeometry);
Execute.PostToUIThread(CreateLedGeometry);
}
public Led Led { get; }
@ -106,7 +106,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
public void Update()
{
var newColor = Led.Color.ToMediaColor();
Execute.OnUIThread(() =>
Execute.PostToUIThread(() =>
{
if (!DisplayColor.Equals(newColor))
DisplayColor = newColor;

View File

@ -24,7 +24,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
public ProfileViewModel(ISurfaceService surfaceService, ISettingsService settingsService)
{
Devices = new ObservableCollection<ProfileDeviceViewModel>();
Execute.OnUIThread(() =>
Execute.PostToUIThread(() =>
{
SelectionRectangle = new RectangleGeometry();
PanZoomViewModel = new PanZoomViewModel();
@ -63,7 +63,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
// Create outside the UI thread to avoid slowdowns as much as possible
var profileDeviceViewModel = new ProfileDeviceViewModel(surfaceDeviceConfiguration);
Execute.OnUIThread(() =>
Execute.PostToUIThread(() =>
{
// Gotta call IsInitializing on the UI thread or its never gets picked up
IsInitializing = true;
@ -79,7 +79,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
}
// Sort the devices by ZIndex
Execute.OnUIThread(() =>
Execute.PostToUIThread(() =>
{
lock (Devices)
{

View File

@ -16,7 +16,8 @@
d:DesignWidth="1200"
d:DataContext="{d:DesignInstance screens:RootViewModel}"
SaveWindowPosition="True"
Icon="/Artemis.UI;component/Resources/logo-512.png">
Icon="/Artemis.UI;component/Resources/logo-512.png"
UseLayoutRounding="True">
<metro:MetroWindow.Resources>
<Style TargetType="ContentControl" x:Key="InitializingFade">
<Style.Triggers>

View File

@ -58,7 +58,7 @@ namespace Artemis.UI.Screens
SelectedPage = null;
// Create a view model for the given plugin info (which will be a module)
var viewModel = await Task.Run(() => _moduleViewModelFactory.CreateModuleViewModel(SelectedModule));
var viewModel = await Task.Run(() => _moduleViewModelFactory.Create(SelectedModule));
ActivateItem(viewModel);
}

View File

@ -40,7 +40,7 @@ namespace Artemis.UI.Screens.Settings.Debug
var imageSource = ImageSourceFromBitmap(e.Bitmap);
imageSource.Freeze();
Execute.OnUIThread(() => { CurrentFrame = imageSource; });
Execute.PostToUIThread(() => { CurrentFrame = imageSource; });
}
private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)

View File

@ -1,7 +1,6 @@
using Artemis.Core.Services;
using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage;
using Artemis.Core.Services.Storage.Interfaces;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Settings.Debug;
using Artemis.UI.Screens.Settings.Tabs.Devices;
using Ninject;
@ -11,19 +10,23 @@ namespace Artemis.UI.Screens.Settings
{
public class SettingsViewModel : Screen, IScreenViewModel
{
private readonly ICoreService _coreService;
private readonly IDeviceSettingsViewModelFactory _deviceSettingsViewModelFactory;
private readonly IKernel _kernel;
private readonly ISettingsService _settingsService;
private readonly ISurfaceService _surfaceService;
private readonly IWindowManager _windowManager;
public SettingsViewModel(IKernel kernel, ICoreService coreService, ISurfaceService surfaceService, IWindowManager windowManager, ISettingsService settingsService)
public SettingsViewModel(IKernel kernel,
ISurfaceService surfaceService,
IWindowManager windowManager,
ISettingsService settingsService,
IDeviceSettingsViewModelFactory deviceSettingsViewModelFactory)
{
_kernel = kernel;
_coreService = coreService;
_surfaceService = surfaceService;
_windowManager = windowManager;
_settingsService = settingsService;
_deviceSettingsViewModelFactory = deviceSettingsViewModelFactory;
DeviceSettingsViewModels = new BindableCollection<DeviceSettingsViewModel>();
}
@ -56,7 +59,7 @@ namespace Artemis.UI.Screens.Settings
{
DeviceSettingsViewModels.Clear();
foreach (var device in _surfaceService.ActiveSurface.Devices)
DeviceSettingsViewModels.Add(new DeviceSettingsViewModel(device, _coreService));
DeviceSettingsViewModels.Add(_deviceSettingsViewModelFactory.Create(device));
base.OnActivate();
}

View File

@ -5,7 +5,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
d:DataContext="{d:DesignInstance settings:DeviceSettingsViewModel}"
xmlns:devices="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Devices"
d:DataContext="{d:DesignInstance devices:DeviceSettingsViewModel}"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@ -36,7 +37,7 @@
VerticalAlignment="Bottom"
Margin="0 0 16 -20"
ToolTip="Identify"
Command="{s:Action Identify}">
Command="{s:Action IdentifyDevice}">
<materialDesign:PackIcon Kind="AlarmLight" />
</Button>
<StackPanel Grid.Row="1" Margin="8 24 8 0">

View File

@ -1,21 +1,17 @@
using System.Diagnostics;
using System.Drawing;
using System.Threading.Tasks;
using Artemis.Core.Events;
using Artemis.Core.Models.Surface;
using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services;
using Humanizer;
namespace Artemis.UI.Screens.Settings.Tabs.Devices
{
public class DeviceSettingsViewModel
{
private readonly ICoreService _coreService;
private readonly IDeviceService _deviceService;
public DeviceSettingsViewModel(Device device, ICoreService coreService)
public DeviceSettingsViewModel(Device device, IDeviceService deviceService)
{
_coreService = coreService;
_deviceService = deviceService;
Device = device;
Type = Device.RgbDevice.DeviceInfo.DeviceType.ToString().Humanize();
@ -31,38 +27,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
public string Manufacturer { get; set; }
public bool IsDeviceEnabled { get; set; }
public void Identify()
public void IdentifyDevice()
{
BlinkDevice(0);
_deviceService.IdentifyDevice(Device);
}
private void BlinkDevice(int blinkCount)
{
// Draw a white overlay over the device
void DrawOverlay(object sender, FrameRenderingEventArgs args)
{
using (var g = Graphics.FromImage(args.Bitmap))
{
g.FillPath(new SolidBrush(Color.White), Device.RenderPath);
}
}
_coreService.FrameRendering += DrawOverlay;
// After 200ms, stop drawing the overlay
Task.Run(async () =>
{
await Task.Delay(200);
_coreService.FrameRendering -= DrawOverlay;
if (blinkCount < 5)
{
// After another 200ms, draw the overlay again, repeat six times
await Task.Delay(200);
BlinkDevice(blinkCount + 1);
}
});
}
public void ShowDeviceDebugger()
{

View File

@ -6,14 +6,16 @@
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="213.053" d:DesignWidth="254.425">
<StackPanel Margin="16" UseLayoutRounding="True">
<TextBlock>
d:DesignHeight="213.053" d:DesignWidth="254.425" >
<StackPanel Margin="16">
<TextBlock Style="{StaticResource MaterialDesignTitleTextBlock}">
Add a new surface layout
</TextBlock>
<TextBox materialDesign:HintAssist.Hint="Layout name" Margin="0 8 0 16"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" Text="{Binding SurfaceName}" />
<TextBox materialDesign:HintAssist.Hint="Layout name"
Margin="0 8 0 16"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding SurfaceName, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0"

View File

@ -10,7 +10,7 @@
mc:Ignorable="d"
d:DesignHeight="351.305" d:DesignWidth="262.163"
d:DataContext="{d:DesignInstance {x:Type surfaceEditor:SurfaceDeviceConfigViewModel}}">
<StackPanel Margin="16" UseLayoutRounding="True">
<StackPanel Margin="16">
<TextBlock Text="{Binding Title}" Style="{StaticResource MaterialDesignTitleTextBlock}" />
<Label>X-coordinate</Label>

View File

@ -111,6 +111,12 @@
<ContentControl s:View.Model="{Binding}">
<ContentControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Identify" Command="{s:Action IdentifyDevice}" CommandParameter="{Binding}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="AlarmLight" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Bring to Front" Command="{s:Action BringToFront}" CommandParameter="{Binding}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ArrangeBringToFront" />

View File

@ -23,9 +23,10 @@ namespace Artemis.UI.Screens.SurfaceEditor
{
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private readonly IDeviceService _deviceService;
private readonly ISurfaceService _surfaceService;
public SurfaceEditorViewModel(ISurfaceService surfaceService, IDialogService dialogService, ISettingsService settingsService)
public SurfaceEditorViewModel(ISurfaceService surfaceService, IDialogService dialogService, ISettingsService settingsService, IDeviceService deviceService)
{
Devices = new ObservableCollection<SurfaceDeviceViewModel>();
SurfaceConfigurations = new ObservableCollection<Surface>();
@ -36,6 +37,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
_surfaceService = surfaceService;
_dialogService = dialogService;
_settingsService = settingsService;
_deviceService = deviceService;
}
public ObservableCollection<SurfaceDeviceViewModel> Devices { get; set; }
@ -60,7 +62,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
public Surface CreateSurfaceConfiguration(string name)
{
var config = _surfaceService.CreateSurfaceConfiguration(name);
Execute.OnUIThread(() => SurfaceConfigurations.Add(config));
Execute.PostToUIThread(() => SurfaceConfigurations.Add(config));
return config;
}
@ -88,7 +90,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
_surfaceService.SetActiveSurfaceConfiguration(activeConfig);
}
Execute.OnUIThread(() =>
Execute.PostToUIThread(() =>
{
// Populate the UI collection
SurfaceConfigurations.Clear();
@ -104,7 +106,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
{
if (SelectedSurface == null)
{
Execute.OnUIThread(Devices.Clear);
Execute.PostToUIThread(Devices.Clear);
return;
}
@ -114,14 +116,14 @@ namespace Artemis.UI.Screens.SurfaceEditor
// Create VMs for missing devices
var viewModel = Devices.FirstOrDefault(vm => vm.Device.RgbDevice == surfaceDeviceConfiguration.RgbDevice);
if (viewModel == null)
Execute.OnUIThread(() => Devices.Add(new SurfaceDeviceViewModel(surfaceDeviceConfiguration)));
Execute.PostToUIThread(() => Devices.Add(new SurfaceDeviceViewModel(surfaceDeviceConfiguration)));
// Update existing devices
else
viewModel.Device = surfaceDeviceConfiguration;
}
// Sort the devices by ZIndex
Execute.OnUIThread(() =>
Execute.PostToUIThread(() =>
{
foreach (var device in Devices.OrderBy(d => d.Device.ZIndex).ToList())
Devices.Move(Devices.IndexOf(device), device.Device.ZIndex - 1);
@ -174,6 +176,11 @@ namespace Artemis.UI.Screens.SurfaceEditor
#region Context menu actions
public void IdentifyDevice(SurfaceDeviceViewModel surfaceDeviceViewModel)
{
_deviceService.IdentifyDevice(surfaceDeviceViewModel.Device);
}
public void BringToFront(SurfaceDeviceViewModel surfaceDeviceViewModel)
{
Devices.Move(Devices.IndexOf(surfaceDeviceViewModel), Devices.Count - 1);