1
0
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:
Robert 2021-03-10 23:17:26 +01:00
commit 902210e48a
49 changed files with 973 additions and 101 deletions

View File

@ -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()

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using RGB.NET.Core;

View File

@ -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);

View File

@ -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>

View File

@ -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));

View File

@ -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()

View File

@ -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;

View File

@ -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>

View 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);
}
}
}

View File

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -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();

View File

@ -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);

View File

@ -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" />

View File

@ -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
}
}

View File

@ -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" />

View File

@ -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

View File

@ -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" />

View File

@ -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();

View File

@ -71,5 +71,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
return wrapper.CreateViewModel(_dataModelUIService, new DataModelUpdateConfiguration(false));
}
public override void Evaluate()
{
throw new NotImplementedException();
}
}
}

View File

@ -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>

View File

@ -42,5 +42,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
return LeftSideSelectionViewModel.DataModelPath?.GetPropertyType();
}
public override void Evaluate()
{
IsConditionMet = DataModelConditionPredicate.Evaluate();
}
}
}

View File

@ -81,5 +81,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
return wrapper.CreateViewModel(_dataModelUIService, new DataModelUpdateConfiguration(true));
}
public override void Evaluate()
{
throw new NotImplementedException();
}
}
}

View File

@ -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">

View File

@ -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();

View File

@ -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}">

View File

@ -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 ||

View File

@ -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>

View File

@ -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));
}
}
}

View File

@ -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; }
}
}

View File

@ -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 -->

View File

@ -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)

View File

@ -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()

View File

@ -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)
{

View File

@ -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>

View File

@ -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)

View File

@ -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>

View File

@ -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()

View File

@ -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>

View File

@ -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)
{

View File

@ -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)

View 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
{
}
}