mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'development'
This commit is contained in:
commit
902210e48a
@ -12,7 +12,6 @@ namespace Artemis.Core
|
||||
{
|
||||
private bool _disposed;
|
||||
private bool _reinitializing;
|
||||
private DateTime _lastTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionEvent" /> class
|
||||
@ -39,6 +38,8 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public DataModelPath? EventPath { get; private set; }
|
||||
|
||||
public DateTime LastTrigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of argument the event provides
|
||||
/// </summary>
|
||||
@ -55,10 +56,10 @@ namespace Artemis.Core
|
||||
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
|
||||
return false;
|
||||
// Only evaluate to true once every time the event has been triggered since the last evaluation
|
||||
if (dataModelEvent.LastTrigger <= _lastTrigger)
|
||||
if (dataModelEvent.LastTrigger <= LastTrigger)
|
||||
return false;
|
||||
|
||||
_lastTrigger = DateTime.Now;
|
||||
LastTrigger = DateTime.Now;
|
||||
|
||||
// If there is a child (root group), it must evaluate to true whenever the event triggered
|
||||
if (Children.Any())
|
||||
@ -171,8 +172,8 @@ namespace Artemis.Core
|
||||
AddChild(new DataModelConditionGroup(this));
|
||||
}
|
||||
|
||||
if (EventPath?.GetValue() is IDataModelEvent dataModelEvent)
|
||||
_lastTrigger = dataModelEvent.LastTrigger;
|
||||
if (EventPath?.GetValue() is IDataModelEvent dataModelEvent)
|
||||
LastTrigger = dataModelEvent.LastTrigger;
|
||||
}
|
||||
|
||||
private Type? GetEventArgumentType()
|
||||
|
||||
@ -287,7 +287,7 @@ namespace Artemis.Core
|
||||
if (!Enabled || Path == null || LayerShape?.Path == null || !General.PropertiesInitialized || !Transform.PropertiesInitialized)
|
||||
return;
|
||||
// Ensure the brush is ready
|
||||
if (LayerBrush?.BaseProperties?.PropertiesInitialized == false)
|
||||
if (LayerBrush == null || LayerBrush?.BaseProperties?.PropertiesInitialized == false)
|
||||
return;
|
||||
|
||||
RenderTimeline(Timeline, canvas);
|
||||
|
||||
@ -306,7 +306,7 @@ namespace Artemis.Core
|
||||
{
|
||||
// Take out invalid file name chars, may not be perfect but neither are you
|
||||
string fileName = System.IO.Path.GetInvalidFileNameChars().Aggregate(RgbDevice.DeviceInfo.Model, (current, c) => current.Replace(c, '-'));
|
||||
if (RgbDevice is IKeyboard)
|
||||
if (RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard)
|
||||
fileName = $"{fileName}-{PhysicalLayout.ToString().ToUpper()}";
|
||||
if (includeExtension)
|
||||
fileName = $"{fileName}.xml";
|
||||
@ -353,7 +353,7 @@ namespace Artemis.Core
|
||||
{
|
||||
RgbDevice.Rotation = DeviceEntity.Rotation;
|
||||
RgbDevice.Scale = DeviceEntity.Scale;
|
||||
|
||||
|
||||
// Workaround for device rotation not applying
|
||||
if (DeviceEntity.X == 0 && DeviceEntity.Y == 0)
|
||||
RgbDevice.Location = new Point(1, 1);
|
||||
@ -388,14 +388,19 @@ namespace Artemis.Core
|
||||
|
||||
private void ApplyKeyboardLayout()
|
||||
{
|
||||
if (!(RgbDevice is IKeyboard keyboard))
|
||||
if (RgbDevice.DeviceInfo.DeviceType != RGBDeviceType.Keyboard)
|
||||
return;
|
||||
|
||||
IKeyboard? keyboard = RgbDevice as IKeyboard;
|
||||
// If supported, detect the device layout so that we can load the correct one
|
||||
if (DeviceProvider.CanDetectLogicalLayout)
|
||||
LogicalLayout = DeviceProvider.GetLogicalLayout(keyboard);
|
||||
if (DeviceProvider.CanDetectPhysicalLayout)
|
||||
if (DeviceProvider.CanDetectPhysicalLayout && keyboard != null)
|
||||
PhysicalLayout = (KeyboardLayoutType) keyboard.DeviceInfo.Layout;
|
||||
else
|
||||
PhysicalLayout = (KeyboardLayoutType) DeviceEntity.PhysicalLayout;
|
||||
if (DeviceProvider.CanDetectLogicalLayout && keyboard != null)
|
||||
LogicalLayout = DeviceProvider.GetLogicalLayout(keyboard);
|
||||
else
|
||||
LogicalLayout = DeviceEntity.LogicalLayout;
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
@ -65,12 +65,6 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public LayoutCustomDeviceData LayoutCustomDeviceData { get; private set; } = null!;
|
||||
|
||||
public void ReloadFromDisk()
|
||||
{
|
||||
Leds.Clear();
|
||||
LoadLayout();
|
||||
}
|
||||
|
||||
internal void ApplyDevice(ArtemisDevice artemisDevice)
|
||||
{
|
||||
Device = artemisDevice;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using RGB.NET.Core;
|
||||
|
||||
|
||||
@ -15,9 +15,18 @@ namespace Artemis.Core.Services
|
||||
/// </summary>
|
||||
public class DataModelJsonPluginEndPoint<T> : PluginEndPoint where T : DataModel
|
||||
{
|
||||
private readonly ProfileModule<T>? _profileModule;
|
||||
private readonly Module<T>? _module;
|
||||
private readonly DataModelExpansion<T>? _dataModelExpansion;
|
||||
|
||||
internal DataModelJsonPluginEndPoint(ProfileModule<T> profileModule, string name, PluginsModule pluginsModule) : base(profileModule, name, pluginsModule)
|
||||
{
|
||||
_profileModule = profileModule ?? throw new ArgumentNullException(nameof(profileModule));
|
||||
|
||||
ThrowOnFail = true;
|
||||
Accepts = MimeType.Json;
|
||||
}
|
||||
|
||||
internal DataModelJsonPluginEndPoint(Module<T> module, string name, PluginsModule pluginsModule) : base(module, name, pluginsModule)
|
||||
{
|
||||
_module = module ?? throw new ArgumentNullException(nameof(module));
|
||||
@ -54,7 +63,9 @@ namespace Artemis.Core.Services
|
||||
using TextReader reader = context.OpenRequestText();
|
||||
try
|
||||
{
|
||||
if (_module != null)
|
||||
if (_profileModule != null)
|
||||
JsonConvert.PopulateObject(await reader.ReadToEndAsync(), _profileModule.DataModel);
|
||||
else if (_module != null)
|
||||
JsonConvert.PopulateObject(await reader.ReadToEndAsync(), _module.DataModel);
|
||||
else
|
||||
JsonConvert.PopulateObject(await reader.ReadToEndAsync(), _dataModelExpansion!.DataModel);
|
||||
|
||||
@ -54,6 +54,15 @@ namespace Artemis.Core.Services
|
||||
/// <returns>The resulting end point</returns>
|
||||
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint that directly maps received JSON to the data model of the provided <paramref name="profileModule" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data model type of the module</typeparam>
|
||||
/// <param name="profileModule">The module whose datamodel to apply the received JSON to</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(ProfileModule<T> profileModule, string endPointName) where T : DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint that directly maps received JSON to the data model of the provided <paramref name="dataModelExpansion" />.
|
||||
/// </summary>
|
||||
|
||||
@ -136,6 +136,15 @@ namespace Artemis.Core.Services
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(ProfileModule<T> profileModule, string endPointName) where T : DataModel
|
||||
{
|
||||
if (profileModule == null) throw new ArgumentNullException(nameof(profileModule));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
DataModelJsonPluginEndPoint<T> endPoint = new(profileModule, endPointName, PluginsModule);
|
||||
PluginsModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(DataModelExpansion<T> dataModelExpansion, string endPointName) where T : DataModel
|
||||
{
|
||||
if (dataModelExpansion == null) throw new ArgumentNullException(nameof(dataModelExpansion));
|
||||
|
||||
@ -288,12 +288,12 @@ namespace Artemis.UI.Shared
|
||||
|
||||
private void DeviceUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
SetupForDevice();
|
||||
Execute.PostToUIThread(SetupForDevice);
|
||||
}
|
||||
|
||||
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
SetupForDevice();
|
||||
Execute.PostToUIThread(SetupForDevice);
|
||||
}
|
||||
|
||||
private void Render()
|
||||
|
||||
@ -12,9 +12,12 @@ namespace Artemis.UI.Shared
|
||||
{
|
||||
internal class DeviceVisualizerLed
|
||||
{
|
||||
private const byte Dimmed = 100;
|
||||
private const byte NonDimmed = 255;
|
||||
|
||||
private SolidColorBrush? _renderColorBrush;
|
||||
private Color _renderColor;
|
||||
|
||||
|
||||
public DeviceVisualizerLed(ArtemisLed led)
|
||||
{
|
||||
Led = led;
|
||||
@ -48,7 +51,8 @@ namespace Artemis.UI.Shared
|
||||
byte g = Led.RgbLed.Color.GetG();
|
||||
byte b = Led.RgbLed.Color.GetB();
|
||||
|
||||
_renderColor.A = isDimmed ? 100 : 255;
|
||||
_renderColor.A = (byte)(isDimmed ? 100 : 255);
|
||||
_renderColor.A = isDimmed ? Dimmed : NonDimmed;
|
||||
_renderColor.R = r;
|
||||
_renderColor.G = g;
|
||||
_renderColor.B = b;
|
||||
|
||||
@ -126,6 +126,11 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Cursors\aero_rotate.cur" />
|
||||
<Resource Include="Resources\Images\PhysicalLayouts\abnt.png" />
|
||||
<Resource Include="Resources\Images\PhysicalLayouts\ansi.png" />
|
||||
<Resource Include="Resources\Images\PhysicalLayouts\iso.png" />
|
||||
<Resource Include="Resources\Images\PhysicalLayouts\jis.png" />
|
||||
<Resource Include="Resources\Images\PhysicalLayouts\ks.png" />
|
||||
<Resource Include="Resources\Images\Sidebar\sidebar-header.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -306,6 +311,11 @@
|
||||
<None Remove="Resources\Fonts\RobotoMono-Regular.ttf" />
|
||||
<None Remove="Resources\Images\Logo\bow.svg" />
|
||||
<None Remove="Resources\Images\Logo\logo-512.ico" />
|
||||
<None Remove="Resources\Images\PhysicalLayouts\abnt.png" />
|
||||
<None Remove="Resources\Images\PhysicalLayouts\ansi.png" />
|
||||
<None Remove="Resources\Images\PhysicalLayouts\iso.png" />
|
||||
<None Remove="Resources\Images\PhysicalLayouts\jis.png" />
|
||||
<None Remove="Resources\Images\PhysicalLayouts\ks.png" />
|
||||
<None Remove="Resources\Images\Sidebar\sidebar-header.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
169
src/Artemis.UI/Behaviors/HighlightTermBehavior.cs
Normal file
169
src/Artemis.UI/Behaviors/HighlightTermBehavior.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Artemis.UI.Behaviors
|
||||
{
|
||||
// Source: https://stackoverflow.com/a/60474831/5015269
|
||||
// Made some changes to add a foreground and background property
|
||||
public static class HighlightTermBehavior
|
||||
{
|
||||
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
|
||||
"Text",
|
||||
typeof(string),
|
||||
typeof(HighlightTermBehavior),
|
||||
new FrameworkPropertyMetadata("", OnTextChanged));
|
||||
|
||||
public static readonly DependencyProperty TermToBeHighlightedProperty = DependencyProperty.RegisterAttached(
|
||||
"TermToBeHighlighted",
|
||||
typeof(string),
|
||||
typeof(HighlightTermBehavior),
|
||||
new FrameworkPropertyMetadata("", OnTextChanged));
|
||||
|
||||
public static readonly DependencyProperty HighlightForegroundProperty = DependencyProperty.RegisterAttached(
|
||||
"HighlightForeground",
|
||||
typeof(Color?),
|
||||
typeof(HighlightTermBehavior),
|
||||
new FrameworkPropertyMetadata(null, OnTextChanged));
|
||||
|
||||
public static readonly DependencyProperty HighlightBackgroundProperty = DependencyProperty.RegisterAttached(
|
||||
"HighlightBackground",
|
||||
typeof(Color?),
|
||||
typeof(HighlightTermBehavior),
|
||||
new FrameworkPropertyMetadata(null, OnTextChanged));
|
||||
|
||||
public static string GetText(FrameworkElement frameworkElement)
|
||||
{
|
||||
return (string) frameworkElement.GetValue(TextProperty);
|
||||
}
|
||||
|
||||
public static void SetText(FrameworkElement frameworkElement, string value)
|
||||
{
|
||||
frameworkElement.SetValue(TextProperty, value);
|
||||
}
|
||||
|
||||
public static string GetTermToBeHighlighted(FrameworkElement frameworkElement)
|
||||
{
|
||||
return (string) frameworkElement.GetValue(TermToBeHighlightedProperty);
|
||||
}
|
||||
|
||||
public static void SetTermToBeHighlighted(FrameworkElement frameworkElement, string value)
|
||||
{
|
||||
frameworkElement.SetValue(TermToBeHighlightedProperty, value);
|
||||
}
|
||||
|
||||
public static void SetHighlightForeground(FrameworkElement frameworkElement, Color? value)
|
||||
{
|
||||
frameworkElement.SetValue(HighlightForegroundProperty, value);
|
||||
}
|
||||
|
||||
public static Color? GetHighlightForeground(FrameworkElement frameworkElement)
|
||||
{
|
||||
return (Color?) frameworkElement.GetValue(HighlightForegroundProperty);
|
||||
}
|
||||
|
||||
public static void SetHighlightBackground(FrameworkElement frameworkElement, Color? value)
|
||||
{
|
||||
frameworkElement.SetValue(HighlightBackgroundProperty, value);
|
||||
}
|
||||
|
||||
public static Color? GetHighlightBackground(FrameworkElement frameworkElement)
|
||||
{
|
||||
return (Color?) frameworkElement.GetValue(HighlightBackgroundProperty);
|
||||
}
|
||||
|
||||
public static List<string> SplitTextIntoTermAndNotTermParts(string text, string term)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return new List<string> {string.Empty};
|
||||
|
||||
return Regex.Split(text, $@"({Regex.Escape(term)})", RegexOptions.IgnoreCase)
|
||||
.Where(p => p != string.Empty)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is TextBlock textBlock)
|
||||
SetTextBlockTextAndHighlightTerm(textBlock, GetText(textBlock), GetTermToBeHighlighted(textBlock));
|
||||
}
|
||||
|
||||
private static void SetTextBlockTextAndHighlightTerm(TextBlock textBlock, string text, string termToBeHighlighted)
|
||||
{
|
||||
textBlock.Text = string.Empty;
|
||||
|
||||
if (TextIsEmpty(text))
|
||||
return;
|
||||
|
||||
if (TextIsNotContainingTermToBeHighlighted(text, termToBeHighlighted))
|
||||
{
|
||||
AddPartToTextBlock(textBlock, text);
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> textParts = SplitTextIntoTermAndNotTermParts(text, termToBeHighlighted);
|
||||
|
||||
foreach (string textPart in textParts)
|
||||
AddPartToTextBlockAndHighlightIfNecessary(textBlock, termToBeHighlighted, textPart);
|
||||
}
|
||||
|
||||
private static bool TextIsEmpty(string text)
|
||||
{
|
||||
return text.Length == 0;
|
||||
}
|
||||
|
||||
private static bool TextIsNotContainingTermToBeHighlighted(string text, string termToBeHighlighted)
|
||||
{
|
||||
if (text == null || termToBeHighlighted == null)
|
||||
return true;
|
||||
return text.Contains(termToBeHighlighted, StringComparison.OrdinalIgnoreCase) == false;
|
||||
}
|
||||
|
||||
private static void AddPartToTextBlockAndHighlightIfNecessary(TextBlock textBlock, string termToBeHighlighted, string textPart)
|
||||
{
|
||||
if (textPart.Equals(termToBeHighlighted, StringComparison.OrdinalIgnoreCase))
|
||||
AddHighlightedPartToTextBlock(textBlock, textPart);
|
||||
else
|
||||
AddPartToTextBlock(textBlock, textPart);
|
||||
}
|
||||
|
||||
private static void AddPartToTextBlock(TextBlock textBlock, string part)
|
||||
{
|
||||
textBlock.Inlines.Add(new Run {Text = part});
|
||||
}
|
||||
|
||||
private static void AddHighlightedPartToTextBlock(TextBlock textBlock, string part)
|
||||
{
|
||||
Color? foreground = GetHighlightForeground(textBlock);
|
||||
Color? background = GetHighlightBackground(textBlock);
|
||||
|
||||
if (background == null)
|
||||
{
|
||||
Run run = new() {Text = part, FontWeight = FontWeights.ExtraBold};
|
||||
if (foreground != null)
|
||||
run.Foreground = new SolidColorBrush(foreground.Value);
|
||||
textBlock.Inlines.Add(run);
|
||||
return;
|
||||
}
|
||||
|
||||
Border border = new()
|
||||
{
|
||||
Background = new SolidColorBrush(background.Value),
|
||||
BorderThickness = new Thickness(0),
|
||||
CornerRadius = new CornerRadius(2),
|
||||
Child = new TextBlock {Text = part, FontWeight = FontWeights.Bold},
|
||||
Padding = new Thickness(1),
|
||||
Margin = new Thickness(-1, -5, -1, -5)
|
||||
};
|
||||
if (foreground != null)
|
||||
((TextBlock) border.Child).Foreground = new SolidColorBrush(foreground.Value);
|
||||
textBlock.Inlines.Add(border);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,6 +90,9 @@ namespace Artemis.UI
|
||||
IRegistrationService registrationService = Kernel.Get<IRegistrationService>();
|
||||
registrationService.RegisterInputProvider();
|
||||
registrationService.RegisterControllers();
|
||||
|
||||
// Initialize background services
|
||||
Kernel.Get<IDeviceLayoutService>();
|
||||
}
|
||||
|
||||
protected override void ConfigureIoC(IKernel kernel)
|
||||
|
||||
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/abnt.png
Normal file
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/abnt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/ansi.png
Normal file
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/ansi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/iso.png
Normal file
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/iso.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/jis.png
Normal file
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/jis.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/ks.png
Normal file
BIN
src/Artemis.UI/Resources/Images/PhysicalLayouts/ks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@ -67,6 +67,11 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
|
||||
|
||||
protected SolidColorBrush LeftSideColor { get; set; }
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
IsConditionMet = DataModelConditionPredicate.Evaluate();
|
||||
}
|
||||
|
||||
public override void Delete()
|
||||
{
|
||||
base.Delete();
|
||||
|
||||
@ -8,6 +8,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
|
||||
public abstract class DataModelConditionViewModel : Conductor<DataModelConditionViewModel>.Collection.AllActive
|
||||
{
|
||||
private DataModelDynamicViewModel _leftSideSelectionViewModel;
|
||||
private bool _isConditionMet;
|
||||
|
||||
protected DataModelConditionViewModel(DataModelConditionPart model)
|
||||
{
|
||||
@ -22,8 +23,17 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
|
||||
set => SetAndNotify(ref _leftSideSelectionViewModel, value);
|
||||
}
|
||||
|
||||
public bool IsConditionMet
|
||||
{
|
||||
get => _isConditionMet;
|
||||
set => SetAndNotify(ref _isConditionMet, value);
|
||||
}
|
||||
|
||||
|
||||
public abstract void Update();
|
||||
|
||||
public abstract void Evaluate();
|
||||
|
||||
public virtual void Delete()
|
||||
{
|
||||
Model.Parent.RemoveChild(Model);
|
||||
|
||||
@ -13,7 +13,14 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Storyboard x:Key="EventAnimation" x:Name="StartAnimation">
|
||||
<DoubleAnimation Duration="0:0:0.5" From="0.5" To="1.5" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)" />
|
||||
<DoubleAnimation Duration="0:0:0.5" From="0.5" To="1.5" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)" />
|
||||
<DoubleAnimation Duration="0:0:0.1" From="0" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" />
|
||||
<DoubleAnimation BeginTime="0:0:0.25" Duration="0:0:0.25" From="1" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" />
|
||||
</Storyboard>
|
||||
</ResourceDictionary>
|
||||
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@ -21,6 +28,7 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
@ -52,7 +60,37 @@
|
||||
triggered
|
||||
</TextBlock>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" Margin="0 3 0 0">
|
||||
<Border Grid.Row="0"
|
||||
Grid.Column="3"
|
||||
Opacity="0"
|
||||
Background="#FFB9A40A"
|
||||
Width="22"
|
||||
Height="22"
|
||||
CornerRadius="11"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip="{Binding LastTrigger, NotifyOnTargetUpdated=True}"
|
||||
ToolTipService.IsEnabled="False">
|
||||
<Border.RenderTransform>
|
||||
<ScaleTransform ScaleX="1" ScaleY="1" CenterX="11" CenterY="11" />
|
||||
</Border.RenderTransform>
|
||||
<Border.Triggers>
|
||||
<!-- Ugly workaround to avoid the event animation showing on load -->
|
||||
<EventTrigger RoutedEvent="Border.Loaded">
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation BeginTime="0:0:0.01" Duration="0:0:0" From="1" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
<EventTrigger RoutedEvent="Binding.TargetUpdated">
|
||||
<BeginStoryboard Storyboard="{StaticResource EventAnimation}" />
|
||||
</EventTrigger>
|
||||
</Border.Triggers>
|
||||
<materialDesign:PackIcon Kind="LightningBolt" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16" />
|
||||
</Border>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding Items}" Margin="0 3 0 0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
|
||||
@ -15,6 +15,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private DateTime _lastTrigger;
|
||||
|
||||
public DataModelConditionEventViewModel(DataModelConditionEvent dataModelConditionEvent,
|
||||
IProfileEditorService profileEditorService,
|
||||
@ -24,10 +25,18 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
_profileEditorService = profileEditorService;
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
|
||||
|
||||
_lastTrigger = DataModelConditionEvent.LastTrigger;
|
||||
}
|
||||
|
||||
public DataModelConditionEvent DataModelConditionEvent => (DataModelConditionEvent) Model;
|
||||
|
||||
public DateTime LastTrigger
|
||||
{
|
||||
get => _lastTrigger;
|
||||
set => SetAndNotify(ref _lastTrigger, value);
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
|
||||
@ -36,7 +45,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
|
||||
IReadOnlyCollection<DataModelVisualizationRegistration> editors = _dataModelUIService.RegisteredDataModelEditors;
|
||||
List<Type> supportedInputTypes = new() {typeof(DataModelEvent), typeof(DataModelEvent<>)};
|
||||
|
||||
|
||||
LeftSideSelectionViewModel.FilterTypes = supportedInputTypes.ToArray();
|
||||
LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(185, 164, 10));
|
||||
LeftSideSelectionViewModel.Placeholder = "Select an event";
|
||||
@ -74,6 +83,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
childViewModel.Update();
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
LastTrigger = DataModelConditionEvent.LastTrigger;
|
||||
IsConditionMet = DataModelConditionEvent.Evaluate();
|
||||
}
|
||||
|
||||
public void ApplyEvent()
|
||||
{
|
||||
DataModelConditionEvent.UpdateEvent(LeftSideSelectionViewModel.DataModelPath);
|
||||
@ -104,7 +119,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
LeftSideSelectionViewModel.Dispose();
|
||||
LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,7 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
@ -115,9 +116,9 @@
|
||||
<materialDesign:PackIcon Kind="Equal" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Add event condition"
|
||||
<MenuItem Header="Add event condition"
|
||||
ToolTip="An event condition that responds to data model events"
|
||||
Command="{s:Action AddEventCondition}"
|
||||
Command="{s:Action AddEventCondition}"
|
||||
Visibility="{Binding Data.CanAddEventCondition, Source={StaticResource DataContextProxy}, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="LightningBolt" />
|
||||
@ -133,7 +134,27 @@
|
||||
<materialDesign:PackIcon Kind="Add" Width="18" Height="18" />
|
||||
</Button>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items, IsAsync=True}" Margin="0 3 0 0">
|
||||
<ToggleButton Grid.Row="0"
|
||||
Grid.Column="3"
|
||||
Style="{StaticResource MaterialDesignActionToggleButton}"
|
||||
Focusable="False"
|
||||
IsHitTestVisible="False"
|
||||
IsChecked="{Binding IsConditionMet}"
|
||||
Visibility="{Binding DisplayEvaluationResult, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"
|
||||
Width="22"
|
||||
Height="22"
|
||||
HorizontalAlignment="Left">
|
||||
<ToggleButton.Content>
|
||||
<Border Background="#E74C4C" Width="22" Height="22">
|
||||
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16"/>
|
||||
</Border>
|
||||
</ToggleButton.Content>
|
||||
<materialDesign:ToggleButtonAssist.OnContent>
|
||||
<materialDesign:PackIcon Kind="Check" Width="16" Height="16"/>
|
||||
</materialDesign:ToggleButtonAssist.OnContent>
|
||||
</ToggleButton>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding Items, IsAsync=True}" Margin="0 3 0 0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
|
||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Humanizer;
|
||||
using Stylet;
|
||||
@ -17,9 +15,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
{
|
||||
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private bool _isEventGroup;
|
||||
private bool _isInitialized;
|
||||
private bool _isRootGroup;
|
||||
private bool _isEventGroup;
|
||||
|
||||
public DataModelConditionGroupViewModel(DataModelConditionGroup dataModelConditionGroup,
|
||||
ConditionGroupType groupType,
|
||||
@ -31,7 +29,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
_profileEditorService = profileEditorService;
|
||||
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
|
||||
|
||||
Items.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
|
||||
Items.CollectionChanged += (_, _) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
|
||||
|
||||
Execute.PostToUIThread(async () =>
|
||||
{
|
||||
@ -58,7 +56,11 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
public bool IsEventGroup
|
||||
{
|
||||
get => _isEventGroup;
|
||||
set => SetAndNotify(ref _isEventGroup, value);
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _isEventGroup, value);
|
||||
NotifyOfPropertyChange(nameof(DisplayEvaluationResult));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInitialized
|
||||
@ -68,6 +70,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
}
|
||||
|
||||
public bool DisplayBooleanOperator => Items.Count > 1;
|
||||
public bool DisplayEvaluationResult => GroupType == ConditionGroupType.General && !IsEventGroup;
|
||||
public string SelectedBooleanOperator => DataModelConditionGroup.BooleanOperator.Humanize();
|
||||
|
||||
public void SelectBooleanOperator(string type)
|
||||
@ -166,14 +169,19 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
|
||||
IsEventGroup = Items.Any(i => i is DataModelConditionEventViewModel);
|
||||
if (IsEventGroup)
|
||||
{
|
||||
if (DataModelConditionGroup.BooleanOperator != BooleanOperator.And)
|
||||
SelectBooleanOperator("And");
|
||||
}
|
||||
|
||||
OnUpdated();
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
IsConditionMet = DataModelConditionGroup.Evaluate();
|
||||
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
|
||||
dataModelConditionViewModel.Evaluate();
|
||||
}
|
||||
|
||||
public void ConvertToConditionList(DataModelConditionViewModel predicateViewModel)
|
||||
{
|
||||
// Store the old index and remove the old predicate
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
@ -79,7 +80,26 @@
|
||||
</Button.ContextMenu>
|
||||
</Button>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" Margin="0 4 0 0">
|
||||
<ToggleButton Grid.Row="0"
|
||||
Grid.Column="3"
|
||||
Style="{StaticResource MaterialDesignActionToggleButton}"
|
||||
Focusable="False"
|
||||
IsHitTestVisible="False"
|
||||
IsChecked="{Binding IsConditionMet}"
|
||||
Width="22"
|
||||
Height="22"
|
||||
HorizontalAlignment="Left">
|
||||
<ToggleButton.Content>
|
||||
<Border Background="#E74C4C" Width="22" Height="22">
|
||||
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16"/>
|
||||
</Border>
|
||||
</ToggleButton.Content>
|
||||
<materialDesign:ToggleButtonAssist.OnContent>
|
||||
<materialDesign:PackIcon Kind="Check" Width="16" Height="16"/>
|
||||
</materialDesign:ToggleButtonAssist.OnContent>
|
||||
</ToggleButton>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding Items}" Margin="0 4 0 0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms.VisualStyles;
|
||||
using System.Windows.Media;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
@ -57,6 +58,13 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
IsConditionMet = DataModelConditionList.Evaluate();
|
||||
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
|
||||
dataModelConditionViewModel.Evaluate();
|
||||
}
|
||||
|
||||
public override void Delete()
|
||||
{
|
||||
base.Delete();
|
||||
|
||||
@ -71,5 +71,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
|
||||
return wrapper.CreateViewModel(_dataModelUIService, new DataModelUpdateConfiguration(false));
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,7 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
@ -90,5 +91,24 @@
|
||||
VerticalContentAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
|
||||
<ToggleButton Grid.Row="0"
|
||||
Grid.Column="4"
|
||||
Style="{StaticResource MaterialDesignActionToggleButton}"
|
||||
Focusable="False"
|
||||
IsHitTestVisible="False"
|
||||
IsChecked="{Binding IsConditionMet}"
|
||||
Width="22"
|
||||
Height="22"
|
||||
HorizontalAlignment="Left">
|
||||
<ToggleButton.Content>
|
||||
<Border Background="#E74C4C" Width="22" Height="22">
|
||||
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16"/>
|
||||
</Border>
|
||||
</ToggleButton.Content>
|
||||
<materialDesign:ToggleButtonAssist.OnContent>
|
||||
<materialDesign:PackIcon Kind="Check" Width="16" Height="16"/>
|
||||
</materialDesign:ToggleButtonAssist.OnContent>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -42,5 +42,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
{
|
||||
return LeftSideSelectionViewModel.DataModelPath?.GetPropertyType();
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
IsConditionMet = DataModelConditionPredicate.Evaluate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,5 +81,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
||||
|
||||
return wrapper.CreateViewModel(_dataModelUIService, new DataModelUpdateConfiguration(true));
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,9 +24,19 @@
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="0 0 0 -4">
|
||||
Display conditions
|
||||
</TextBlock>
|
||||
<DockPanel Grid.Row="0" Margin="0 -2">
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" VerticalAlignment="Center">
|
||||
Display conditions
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle2TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"
|
||||
FontSize="13">
|
||||
Not applied during editing
|
||||
</TextBlock>
|
||||
</DockPanel>
|
||||
|
||||
<Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="-2 0" />
|
||||
|
||||
<Grid Grid.Row="2" Grid.Column="0">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.Conditions;
|
||||
using Artemis.UI.Shared;
|
||||
@ -12,15 +13,17 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
public class DisplayConditionsViewModel : Conductor<DataModelConditionGroupViewModel>, IProfileEditorPanelViewModel
|
||||
{
|
||||
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private RenderProfileElement _renderProfileElement;
|
||||
private bool _displayStartHint;
|
||||
private bool _isEventCondition;
|
||||
|
||||
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelConditionsVmFactory dataModelConditionsVmFactory)
|
||||
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelConditionsVmFactory dataModelConditionsVmFactory, ICoreService coreService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
|
||||
_coreService = coreService;
|
||||
}
|
||||
|
||||
public bool DisplayStartHint
|
||||
@ -87,12 +90,14 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
@ -131,6 +136,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified;
|
||||
}
|
||||
|
||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
ActiveItem?.Evaluate();
|
||||
}
|
||||
|
||||
private void DisplayConditionOnChildrenModified(object sender, EventArgs e)
|
||||
{
|
||||
DisplayStartHint = !RenderProfileElement.DisplayCondition.Children.Any();
|
||||
|
||||
@ -27,10 +27,12 @@
|
||||
<TextBlock Grid.Column="0">
|
||||
This image shows what is being rendered and dispatched to RGB.NET
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,5,0" FontWeight="Bold">
|
||||
FPS:
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,5,0">
|
||||
<Run Text="FPS: "></Run>
|
||||
<Run FontWeight="Bold" Text="{Binding CurrentFps}"></Run>
|
||||
<Run Text=" at "></Run>
|
||||
<Run Text="{Binding RenderWidth}"/><Run Text="x"></Run><Run Text="{Binding RenderHeight}"/>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2" HorizontalAlignment="Right" Text="{Binding CurrentFps}" />
|
||||
</Grid>
|
||||
|
||||
<materialDesign:Card Grid.Row="2" Margin="0,5,0,0" Background="{StaticResource Checkerboard}">
|
||||
|
||||
@ -15,6 +15,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
private readonly ICoreService _coreService;
|
||||
private double _currentFps;
|
||||
private ImageSource _currentFrame;
|
||||
private int _renderWidth;
|
||||
private int _renderHeight;
|
||||
|
||||
public RenderDebugViewModel(ICoreService coreService)
|
||||
{
|
||||
@ -34,6 +36,18 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
set => SetAndNotify(ref _currentFps, value);
|
||||
}
|
||||
|
||||
public int RenderWidth
|
||||
{
|
||||
get => _renderWidth;
|
||||
set => SetAndNotify(ref _renderWidth, value);
|
||||
}
|
||||
|
||||
public int RenderHeight
|
||||
{
|
||||
get => _renderHeight;
|
||||
set => SetAndNotify(ref _renderHeight, value);
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
@ -56,6 +70,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
return;
|
||||
|
||||
SKImageInfo bitmapInfo = e.Texture.Bitmap.Info;
|
||||
RenderHeight = bitmapInfo.Height;
|
||||
RenderWidth = bitmapInfo.Width;
|
||||
|
||||
if (!(CurrentFrame is WriteableBitmap writeableBitmap) ||
|
||||
writeableBitmap.Width != bitmapInfo.Width ||
|
||||
|
||||
@ -0,0 +1,187 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Device.DeviceLayoutDialogView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
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.Settings.Device"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mde="https://spiegelp.github.io/MaterialDesignExtensions/winfx/xaml"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="1200" d:DesignWidth="1200"
|
||||
d:DataContext="{d:DesignInstance local:DeviceLayoutDialogViewModel}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" Margin="16" Width="1200" Height="800" Visibility="{Binding SelectPhysicalLayout, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 0 0 20">
|
||||
Select a physical layout
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap">
|
||||
Artemis couldn't automatically determine the physical layout of your <Run Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName, Mode=OneWay}" />. <LineBreak />
|
||||
In order for Artemis to know which keys are on your keyboard and where they're located, select the matching layout below.
|
||||
<LineBreak />
|
||||
<LineBreak />
|
||||
P.S. Don't worry about missing special keys like num keys/function keys or macro keys, they aren't important here.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" Margin="0 25">
|
||||
<WrapPanel HorizontalAlignment="Center">
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="ISO"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/iso.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
ISO
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in the EU (tall enter)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="ANSI"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/ansi.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
ANSI
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in the US (short enter)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="ABNT"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/abnt.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
ABNT
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in Brazil/Portugal (based on ISO)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="KS"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/ks.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
KS
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in South Korea
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="JIS"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/jis.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
JIS
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in Japan (based on ISO)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
</WrapPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="0" Margin="16" Width="800" Visibility="{Binding SelectPhysicalLayout, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 0 0 20">
|
||||
Select a logical layout
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap">
|
||||
Artemis couldn't automatically determine the logical layout of your <Run Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName, Mode=OneWay}" />. <LineBreak /><LineBreak />
|
||||
While not as important as the physical layout, setting the correct logical layout will allow Artemis to show the right keycaps (if a matching layout file is present)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<mde:Autocomplete Style="{StaticResource MaterialDesignAutocomplete}"
|
||||
AutocompleteSource="{Binding Path=AutocompleteSource}"
|
||||
SearchOnInitialFocus="True"
|
||||
SelectedItem="{Binding SelectedRegion, Mode=TwoWay}"
|
||||
Hint="Select a logical layout"
|
||||
Margin="0,16,0,0"
|
||||
FontSize="15"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Background="{DynamicResource MaterialDesignPaper}">
|
||||
<mde:Autocomplete.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock FontSize="14" VerticalAlignment="Center">
|
||||
<Run Text="{Binding EnglishName, Mode=OneWay}" />
|
||||
(<Run FontWeight="SemiBold" Text="{Binding TwoLetterISORegionName, Mode=OneWay}" />)
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</mde:Autocomplete.ItemTemplate>
|
||||
</mde:Autocomplete>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="16">
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Focusable="False"
|
||||
IsCancel="True"
|
||||
Command="{s:Action Cancel}"
|
||||
Content="CANCEL" />
|
||||
<Button x:Name="ConfirmButton"
|
||||
Style="{StaticResource MaterialDesignFlatButton}"
|
||||
IsDefault="True"
|
||||
Focusable="True"
|
||||
Command="{s:Action Confirm}"
|
||||
Content="CONFIRM" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using MaterialDesignExtensions.Model;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device
|
||||
{
|
||||
public class DeviceLayoutDialogViewModel : DialogViewModelBase
|
||||
{
|
||||
private readonly IRgbService _rgbService;
|
||||
private bool _selectPhysicalLayout;
|
||||
private RegionInfoAutocompleteSource _autocompleteSource;
|
||||
private RegionInfo _selectedRegion;
|
||||
|
||||
public DeviceLayoutDialogViewModel(ArtemisDevice device, IRgbService rgbService)
|
||||
{
|
||||
_rgbService = rgbService;
|
||||
Device = device;
|
||||
SelectPhysicalLayout = !device.DeviceProvider.CanDetectPhysicalLayout;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
AutocompleteSource = new RegionInfoAutocompleteSource();
|
||||
SelectedRegion = AutocompleteSource.Regions.FirstOrDefault(r => r.TwoLetterISORegionName == Device.LogicalLayout ||
|
||||
r.TwoLetterISORegionName == "US" && Device.LogicalLayout == "NA");
|
||||
});
|
||||
}
|
||||
|
||||
public ArtemisDevice Device { get; }
|
||||
|
||||
public RegionInfoAutocompleteSource AutocompleteSource
|
||||
{
|
||||
get => _autocompleteSource;
|
||||
set => SetAndNotify(ref _autocompleteSource, value);
|
||||
}
|
||||
|
||||
public RegionInfo SelectedRegion
|
||||
{
|
||||
get => _selectedRegion;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _selectedRegion, value);
|
||||
NotifyOfPropertyChange(nameof(CanConfirm));
|
||||
}
|
||||
}
|
||||
|
||||
public bool SelectPhysicalLayout
|
||||
{
|
||||
get => _selectPhysicalLayout;
|
||||
set => SetAndNotify(ref _selectPhysicalLayout, value);
|
||||
}
|
||||
|
||||
public bool CanConfirm => SelectedRegion != null;
|
||||
|
||||
public void ApplyPhysicalLayout(string physicalLayout)
|
||||
{
|
||||
Device.PhysicalLayout = Enum.Parse<KeyboardLayoutType>(physicalLayout);
|
||||
|
||||
_rgbService.SaveDevice(Device);
|
||||
_rgbService.ApplyBestDeviceLayout(Device);
|
||||
|
||||
SelectPhysicalLayout = false;
|
||||
}
|
||||
|
||||
private void ApplyLogicalLayout(string logicalLayout)
|
||||
{
|
||||
Device.LogicalLayout = logicalLayout;
|
||||
|
||||
_rgbService.SaveDevice(Device);
|
||||
_rgbService.ApplyBestDeviceLayout(Device);
|
||||
}
|
||||
|
||||
public void Confirm()
|
||||
{
|
||||
if (!CanConfirm || Session == null || Session.IsEnded)
|
||||
return;
|
||||
|
||||
ApplyLogicalLayout(SelectedRegion.TwoLetterISORegionName);
|
||||
Session?.Close(true);
|
||||
}
|
||||
}
|
||||
|
||||
public class RegionInfoAutocompleteSource : IAutocompleteSource<RegionInfo>
|
||||
{
|
||||
public List<RegionInfo> Regions { get; set; }
|
||||
|
||||
public RegionInfoAutocompleteSource()
|
||||
{
|
||||
Regions = CultureInfo.GetCultures(CultureTypes.SpecificCultures).ToList()
|
||||
.Select(c => new RegionInfo(c.LCID))
|
||||
.GroupBy(r => r.EnglishName)
|
||||
.Select(g => g.First())
|
||||
.OrderBy(r => r.EnglishName)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
IEnumerable<RegionInfo> IAutocompleteSource<RegionInfo>.Search(string searchTerm)
|
||||
{
|
||||
if (searchTerm == null)
|
||||
return Regions;
|
||||
|
||||
searchTerm = searchTerm.ToLower();
|
||||
return Regions.Where(r => r.EnglishName.ToLower().Contains(searchTerm) ||
|
||||
r.NativeName.ToLower().Contains(searchTerm) ||
|
||||
r.TwoLetterISORegionName.ToLower().Contains(searchTerm));
|
||||
}
|
||||
|
||||
public IEnumerable Search(string searchTerm)
|
||||
{
|
||||
if (searchTerm == null)
|
||||
return Regions;
|
||||
|
||||
searchTerm = searchTerm.ToLower();
|
||||
return Regions.Where(r => r.EnglishName.ToLower().Contains(searchTerm) ||
|
||||
r.NativeName.ToLower().Contains(searchTerm) ||
|
||||
r.TwoLetterISORegionName.ToLower().Contains(searchTerm));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
DisplayName = "INFO";
|
||||
}
|
||||
|
||||
public bool IsKeyboard => Device.RgbDevice is IKeyboard;
|
||||
public bool IsKeyboard => Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard;
|
||||
public ArtemisDevice Device { get; }
|
||||
}
|
||||
}
|
||||
@ -174,6 +174,10 @@
|
||||
</StackPanel>
|
||||
</materialDesign:HintAssist.Hint>
|
||||
</TextBox>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignRaisedButton}" Margin="0 8 8 0" Command="{s:Action SelectPhysicalLayout}">
|
||||
SELECT PHYSICAL LAYOUT (PLACEHOLDER)
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Buttons -->
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
@ -15,6 +16,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private float _blueScale;
|
||||
private SKColor _currentColor;
|
||||
@ -33,11 +35,13 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
ICoreService coreService,
|
||||
IRgbService rgbService,
|
||||
IMessageService messageService,
|
||||
IDialogService dialogService,
|
||||
IModelValidator<DevicePropertiesTabViewModel> validator) : base(validator)
|
||||
{
|
||||
_coreService = coreService;
|
||||
_rgbService = rgbService;
|
||||
_messageService = messageService;
|
||||
_dialogService = dialogService;
|
||||
|
||||
Device = device;
|
||||
DisplayName = "PROPERTIES";
|
||||
@ -126,6 +130,11 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SelectPhysicalLayout()
|
||||
{
|
||||
await _dialogService.ShowDialog<DeviceLayoutDialogViewModel>(new Dictionary<string, object> {{"device", Device}});
|
||||
}
|
||||
|
||||
public async Task Apply()
|
||||
{
|
||||
await ValidateAsync();
|
||||
@ -167,12 +176,21 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
_initialGreenScale = Device.GreenScale;
|
||||
_initialBlueScale = Device.BlueScale;
|
||||
CurrentColor = SKColors.White;
|
||||
|
||||
_coreService.FrameRendering += OnFrameRendering;
|
||||
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
_coreService.FrameRendering -= OnFrameRendering;
|
||||
Device.PropertyChanged -= DeviceOnPropertyChanged;
|
||||
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
|
||||
@ -24,12 +24,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
// Take it off the UI thread to avoid freezing on tab change
|
||||
Task.Run(async () =>
|
||||
{
|
||||
Items.Clear();
|
||||
await Task.Delay(200);
|
||||
|
||||
List<DeviceSettingsViewModel> instances = _rgbService.Devices.Select(d => _settingsVmFactory.CreateDeviceSettingsViewModel(d)).ToList();
|
||||
@ -37,7 +36,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
|
||||
Items.Add(deviceSettingsViewModel);
|
||||
});
|
||||
|
||||
base.OnActivate();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
public async Task<bool> ShowDeviceDisableDialog()
|
||||
|
||||
@ -84,7 +84,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
|
||||
|
||||
public void ViewProperties()
|
||||
{
|
||||
_windowManager.ShowDialog(_deviceDebugVmFactory.DeviceDialogViewModel(Device));
|
||||
_windowManager.ShowWindow(_deviceDebugVmFactory.DeviceDialogViewModel(Device));
|
||||
}
|
||||
private async Task UpdateIsDeviceEnabled(bool value)
|
||||
{
|
||||
|
||||
@ -27,13 +27,13 @@
|
||||
Width="20"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" />
|
||||
|
||||
<Button Grid.Column="0"
|
||||
Margin="-8"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, FallbackValue=Collapsed}"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Foreground="#E74C4C"
|
||||
ToolTip="An exception occurred while enabling this feature, click to view"
|
||||
@ -46,13 +46,13 @@
|
||||
|
||||
<!-- Enable toggle column -->
|
||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="8"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}">
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding IsEnabled}">
|
||||
Feature enabled
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay, FallbackValue=Collapsed}">
|
||||
<ProgressBar Style="{StaticResource MaterialDesignCircularProgressBar}" Value="0" IsIndeterminate="True" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@ -65,18 +65,20 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
base.OnInitialActivate();
|
||||
_pluginManagementService.PluginFeatureEnabling += OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled += OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed += OnFeatureEnableStopped;
|
||||
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
base.OnClose();
|
||||
_pluginManagementService.PluginFeatureEnabling -= OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed -= OnFeatureEnableStopped;
|
||||
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
private async Task UpdateEnabled(bool enable)
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="0 15" MinWidth="810" Width="{Binding Path=ActualWidth, ElementName=PluginsContainer}">
|
||||
<Grid Margin="0 15" Width="810">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@ -41,21 +41,36 @@
|
||||
IMPORT PLUGIN
|
||||
</Button>
|
||||
</Grid>
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<ItemsControl ItemsSource="{Binding Items}" Margin="15 0 15 15" HorizontalAlignment="Center" MaxWidth="1700" x:Name="PluginsContainer">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Top" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</ScrollViewer>
|
||||
<ListBox Grid.Row="1"
|
||||
ItemsSource="{Binding Items}"
|
||||
materialDesign:RippleAssist.IsDisabled="True"
|
||||
VirtualizingPanel.ScrollUnit="Pixel"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
Margin="15 0 15 15">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}" Margin="5" HorizontalAlignment="Center" IsTabStop="False" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListBoxItem}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ListBoxItem}">
|
||||
<ContentPresenter />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
@ -43,26 +44,36 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
if (_instances == null)
|
||||
return;
|
||||
|
||||
Items.Clear();
|
||||
List<PluginSettingsViewModel> instances = _instances;
|
||||
string search = SearchPluginInput?.ToLower();
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
instances = instances.Where(i => i.Plugin.Info.Name.ToLower().Contains(search) ||
|
||||
i.Plugin.Info.Description != null && i.Plugin.Info.Description.ToLower().Contains(search)).ToList();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(SearchPluginInput))
|
||||
Items.AddRange(_instances);
|
||||
else
|
||||
Items.AddRange(_instances.Where(i => i.Plugin.Info.Name.Contains(SearchPluginInput, StringComparison.OrdinalIgnoreCase) ||
|
||||
i.Plugin.Info.Description.Contains(SearchPluginInput, StringComparison.OrdinalIgnoreCase)));
|
||||
foreach (PluginSettingsViewModel pluginSettingsViewModel in instances)
|
||||
{
|
||||
if (!Items.Contains(pluginSettingsViewModel))
|
||||
Items.Add(pluginSettingsViewModel);
|
||||
}
|
||||
foreach (PluginSettingsViewModel pluginSettingsViewModel in Items.ToList())
|
||||
{
|
||||
if (!instances.Contains(pluginSettingsViewModel))
|
||||
Items.Remove(pluginSettingsViewModel);
|
||||
}
|
||||
|
||||
((BindableCollection<PluginSettingsViewModel>) Items).Sort(i => i.Plugin.Info.Name);
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
// Take it off the UI thread to avoid freezing on tab change
|
||||
Task.Run(async () =>
|
||||
{
|
||||
Items.Clear();
|
||||
await Task.Delay(200);
|
||||
GetPluginInstances();
|
||||
});
|
||||
|
||||
base.OnActivate();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
public void ImportPlugin()
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
xmlns:devices="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
|
||||
d:DataContext="{d:DesignInstance devices:PluginSettingsViewModel}"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
@ -34,20 +35,27 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<shared:ArtemisIcon Icon="{Binding Icon}"
|
||||
Width="48"
|
||||
Height="48"
|
||||
Margin="0 5 0 0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top" />
|
||||
Width="48"
|
||||
Height="48"
|
||||
Margin="0 5 0 0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Style="{StaticResource MaterialDesignBody2TextBlock}" Text="{Binding Plugin.Info.Name}" />
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Style="{StaticResource MaterialDesignBody2TextBlock}"
|
||||
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}"
|
||||
behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Name}"
|
||||
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
|
||||
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}"/>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Plugin.Info.Description}"
|
||||
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}"
|
||||
behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Description}"
|
||||
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
|
||||
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}"
|
||||
Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" />
|
||||
</Grid>
|
||||
@ -55,9 +63,9 @@
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Bottom"
|
||||
VerticalAlignment="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
<Button
|
||||
VerticalAlignment="Bottom"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
ToolTip="Open the plugins settings window"
|
||||
@ -65,7 +73,7 @@
|
||||
Command="{s:Action OpenSettings}">
|
||||
SETTINGS
|
||||
</Button>
|
||||
<Button
|
||||
<Button
|
||||
VerticalAlignment="Bottom"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
ToolTip="Remove plugin"
|
||||
@ -74,7 +82,7 @@
|
||||
<materialDesign:PackIcon Kind="DeleteForever" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
|
||||
<CheckBox Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
@ -85,10 +93,10 @@
|
||||
Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding IsEnabled}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock>Plugin enabled</TextBlock>
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull"
|
||||
Margin="5 0 0 0"
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip="Plugin requires admin rights"
|
||||
Visibility="{Binding Plugin.Info.RequiresAdmin, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"/>
|
||||
Visibility="{Binding Plugin.Info.RequiresAdmin, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
|
||||
@ -128,7 +136,7 @@
|
||||
</b:Interaction.Behaviors>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
@ -76,7 +76,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
try
|
||||
{
|
||||
PluginConfigurationViewModel viewModel = (PluginConfigurationViewModel) Plugin.Kernel.Get(configurationViewModel.Type);
|
||||
_windowManager.ShowDialog(new PluginSettingsWindowViewModel(viewModel, Icon));
|
||||
_windowManager.ShowWindow(new PluginSettingsWindowViewModel(viewModel, Icon));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@ -241,7 +241,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
|
||||
|
||||
public void ViewProperties(ArtemisDevice device)
|
||||
{
|
||||
_windowManager.ShowDialog(_deviceDebugVmFactory.DeviceDialogViewModel(device));
|
||||
_windowManager.ShowWindow(_deviceDebugVmFactory.DeviceDialogViewModel(device));
|
||||
}
|
||||
|
||||
public async Task DetectInput(ArtemisDevice device)
|
||||
|
||||
89
src/Artemis.UI/Services/DeviceLayoutService.cs
Normal file
89
src/Artemis.UI/Services/DeviceLayoutService.cs
Normal file
@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.Settings.Device;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using RGB.NET.Core;
|
||||
using KeyboardLayoutType = Artemis.Core.KeyboardLayoutType;
|
||||
|
||||
namespace Artemis.UI.Services
|
||||
{
|
||||
public class DeviceLayoutService : IDeviceLayoutService
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly List<ArtemisDevice> _ignoredDevices;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowService _windowService;
|
||||
|
||||
public DeviceLayoutService(IDialogService dialogService, IRgbService rgbService, IWindowService windowService, IMessageService messageService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
_rgbService = rgbService;
|
||||
_windowService = windowService;
|
||||
_messageService = messageService;
|
||||
_ignoredDevices = new List<ArtemisDevice>();
|
||||
|
||||
rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
windowService.MainWindowOpened += WindowServiceOnMainWindowOpened;
|
||||
}
|
||||
|
||||
private async Task RequestLayoutInput(ArtemisDevice artemisDevice)
|
||||
{
|
||||
bool configure = await _dialogService.ShowConfirmDialog(
|
||||
"Device requires layout info",
|
||||
$"Artemis could not detect the layout of your {artemisDevice.RgbDevice.DeviceInfo.DeviceName}. Please configure out manually",
|
||||
"Configure",
|
||||
"Ignore for now"
|
||||
);
|
||||
|
||||
if (!configure)
|
||||
{
|
||||
_ignoredDevices.Add(artemisDevice);
|
||||
return;
|
||||
}
|
||||
|
||||
await _dialogService.ShowDialog<DeviceLayoutDialogViewModel>(new Dictionary<string, object> {{"device", artemisDevice}});
|
||||
}
|
||||
|
||||
private bool DeviceNeedsLayout(ArtemisDevice d)
|
||||
{
|
||||
return d.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Keyboard &&
|
||||
(d.LogicalLayout == null || d.PhysicalLayout == KeyboardLayoutType.Unknown) &&
|
||||
(!d.DeviceProvider.CanDetectLogicalLayout || !d.DeviceProvider.CanDetectPhysicalLayout);
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
||||
{
|
||||
List<ArtemisDevice> devices = _rgbService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
await RequestLayoutInput(artemisDevice);
|
||||
}
|
||||
|
||||
private async void RgbServiceOnDeviceAdded(object sender, DeviceEventArgs e)
|
||||
{
|
||||
if (_ignoredDevices.Contains(e.Device) || !DeviceNeedsLayout(e.Device))
|
||||
return;
|
||||
|
||||
if (!_windowService.IsMainWindowOpen)
|
||||
{
|
||||
_messageService.ShowNotification("New device detected", "Detected a new device that needs layout setup", PackIconKind.Keyboard);
|
||||
return;
|
||||
}
|
||||
|
||||
await RequestLayoutInput(e.Device);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface IDeviceLayoutService : IArtemisUIService
|
||||
{
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user