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

Plugins - Implemented enabling/disabling (doesnt work for devices)

Plugins - Disable plugins that caused a crash
Tray icon - Added menu items for quick access
This commit is contained in:
SpoinkyNL 2020-03-02 22:15:25 +01:00
parent 16b221d2f8
commit 57d82fafa8
29 changed files with 442 additions and 164 deletions

View File

@ -9,11 +9,11 @@ namespace Artemis.Core.Ninject
{
internal class PluginSettingsProvider : Provider<PluginSettings>
{
private readonly IPluginSettingRepository _pluginSettingRepository;
private readonly IPluginRepository _pluginRepository;
internal PluginSettingsProvider(IPluginSettingRepository pluginSettingRepository)
internal PluginSettingsProvider(IPluginRepository pluginRepository)
{
_pluginSettingRepository = pluginSettingRepository;
_pluginRepository = pluginRepository;
}
protected override PluginSettings CreateInstance(IContext context)
@ -25,7 +25,7 @@ namespace Artemis.Core.Ninject
if (pluginInfo == null)
throw new ArtemisCoreException("A plugin needs to be initialized with PluginInfo as a parameter");
return new PluginSettings(pluginInfo, _pluginSettingRepository);
return new PluginSettings(pluginInfo, _pluginRepository);
}
}
}

View File

@ -2,6 +2,7 @@
using System.IO;
using System.Reflection;
using Artemis.Core.Plugins.Abstract;
using Artemis.Storage.Entities.Plugins;
using McMaster.NETCore.Plugins;
using Newtonsoft.Json;
@ -72,6 +73,12 @@ namespace Artemis.Core.Plugins.Models
[JsonIgnore]
internal Assembly Assembly { get; set; }
/// <summary>
/// The entity representing the plugin
/// </summary>
[JsonIgnore]
internal PluginEntity PluginEntity { get; set; }
public override string ToString()
{
return $"{nameof(Guid)}: {Guid}, {nameof(Name)}: {Name}, {nameof(Version)}: {Version}";

View File

@ -1,5 +1,6 @@
using System;
using Artemis.Storage.Entities;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json;
@ -10,13 +11,13 @@ namespace Artemis.Core.Plugins.Models
// ReSharper disable once NotAccessedField.Local
private readonly PluginInfo _pluginInfo;
private readonly PluginSettingEntity _pluginSettingEntity;
private readonly IPluginSettingRepository _pluginSettingRepository;
private readonly IPluginRepository _pluginRepository;
private T _value;
internal PluginSetting(PluginInfo pluginInfo, IPluginSettingRepository pluginSettingRepository, PluginSettingEntity pluginSettingEntity)
internal PluginSetting(PluginInfo pluginInfo, IPluginRepository pluginRepository, PluginSettingEntity pluginSettingEntity)
{
_pluginInfo = pluginInfo;
_pluginSettingRepository = pluginSettingRepository;
_pluginRepository = pluginRepository;
_pluginSettingEntity = pluginSettingEntity;
Name = pluginSettingEntity.Name;
@ -73,7 +74,7 @@ namespace Artemis.Core.Plugins.Models
return;
_pluginSettingEntity.Value = JsonConvert.SerializeObject(Value);
_pluginSettingRepository.Save(_pluginSettingEntity);
_pluginRepository.SaveSetting(_pluginSettingEntity);
}
public event EventHandler<EventArgs> SettingChanged;

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Artemis.Storage.Entities;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json;
@ -12,13 +13,13 @@ namespace Artemis.Core.Plugins.Models
public class PluginSettings
{
private readonly PluginInfo _pluginInfo;
private readonly IPluginSettingRepository _pluginSettingRepository;
private readonly IPluginRepository _pluginRepository;
private readonly Dictionary<string, object> _settingEntities;
internal PluginSettings(PluginInfo pluginInfo, IPluginSettingRepository pluginSettingRepository)
internal PluginSettings(PluginInfo pluginInfo, IPluginRepository pluginRepository)
{
_pluginInfo = pluginInfo;
_pluginSettingRepository = pluginSettingRepository;
_pluginRepository = pluginRepository;
_settingEntities = new Dictionary<string, object>();
}
@ -37,15 +38,15 @@ namespace Artemis.Core.Plugins.Models
if (_settingEntities.ContainsKey(name))
return (PluginSetting<T>) _settingEntities[name];
// Try to find in database
var settingEntity = _pluginSettingRepository.GetByNameAndPluginGuid(name, _pluginInfo.Guid);
var settingEntity = _pluginRepository.GetSettingByNameAndGuid(name, _pluginInfo.Guid);
// If not found, create a new one
if (settingEntity == null)
{
settingEntity = new PluginSettingEntity {Name = name, PluginGuid = _pluginInfo.Guid, Value = JsonConvert.SerializeObject(defaultValue)};
_pluginSettingRepository.Add(settingEntity);
_pluginRepository.AddSetting(settingEntity);
}
var pluginSetting = new PluginSetting<T>(_pluginInfo, _pluginSettingRepository, settingEntity);
var pluginSetting = new PluginSetting<T>(_pluginInfo, _pluginRepository, settingEntity);
_settingEntities.Add(name, pluginSetting);
return pluginSetting;
}

View File

@ -42,6 +42,18 @@ namespace Artemis.Core.Services.Interfaces
/// <param name="pluginInfo">The plugin info defining the plugin to unload</param>
void UnloadPlugin(PluginInfo pluginInfo);
/// <summary>
/// Enables the provided plugin
/// </summary>
/// <param name="plugin"></param>
void EnablePlugin(Plugin plugin);
/// <summary>
/// Disables the provided plugin
/// </summary>
/// <param name="plugin"></param>
void DisablePlugin(Plugin plugin);
/// <summary>
/// Finds the plugin info related to the plugin
/// </summary>

View File

@ -10,6 +10,8 @@ using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Exceptions;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using McMaster.NETCore.Plugins;
using Newtonsoft.Json;
using Ninject;
@ -27,13 +29,15 @@ namespace Artemis.Core.Services
{
private readonly IKernel _kernel;
private readonly ILogger _logger;
private readonly IPluginRepository _pluginRepository;
private readonly List<PluginInfo> _plugins;
private IKernel _childKernel;
internal PluginService(IKernel kernel, ILogger logger)
internal PluginService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
{
_kernel = kernel;
_logger = logger;
_pluginRepository = pluginRepository;
_plugins = new List<PluginInfo>();
// Ensure the plugins directory exists
@ -146,6 +150,18 @@ namespace Artemis.Core.Services
// Activate plugins after they are all loaded
foreach (var pluginInfo in _plugins.Where(p => p.Enabled))
{
if (!pluginInfo.PluginEntity.LastEnableSuccessful)
{
pluginInfo.Enabled = false;
_logger.Warning("Plugin failed to load last time, disabling it now to avoid instability. Plugin info: {pluginInfo}", pluginInfo);
continue;
}
// Mark this as false until the plugin enabled successfully and save it in case the plugin drags us down into a crash
pluginInfo.PluginEntity.LastEnableSuccessful = false;
_pluginRepository.SavePlugin(pluginInfo.PluginEntity);
var threwException = false;
try
{
pluginInfo.Instance.EnablePlugin();
@ -153,6 +169,15 @@ namespace Artemis.Core.Services
catch (Exception e)
{
_logger.Warning(new ArtemisPluginException(pluginInfo, "Failed to load enable plugin", e), "Plugin exception");
pluginInfo.Enabled = false;
threwException = true;
}
// We got this far so the plugin enabled and we didn't crash horribly, yay
if (!threwException)
{
pluginInfo.PluginEntity.LastEnableSuccessful = true;
_pluginRepository.SavePlugin(pluginInfo.PluginEntity);
}
OnPluginEnabled(new PluginEventArgs(pluginInfo));
@ -191,8 +216,13 @@ namespace Artemis.Core.Services
if (_plugins.Contains(pluginInfo))
UnloadPlugin(pluginInfo);
// TODO Just temporarily until settings are in place
pluginInfo.Enabled = true;
var pluginEntity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid);
if (pluginEntity == null)
pluginEntity = new PluginEntity {PluginGuid = pluginInfo.Guid, IsEnabled = true, LastEnableSuccessful = true};
pluginInfo.PluginEntity = pluginEntity;
pluginInfo.Enabled = pluginEntity.IsEnabled;
var mainFile = Path.Combine(pluginInfo.Directory.FullName, pluginInfo.Main);
if (!File.Exists(mainFile))
throw new ArtemisPluginException(pluginInfo, "Couldn't find the plugins main entry at " + mainFile);
@ -277,6 +307,49 @@ namespace Artemis.Core.Services
}
}
public void EnablePlugin(Plugin plugin)
{
plugin.PluginInfo.Enabled = true;
plugin.PluginInfo.PluginEntity.IsEnabled = true;
plugin.PluginInfo.PluginEntity.LastEnableSuccessful = false;
_pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity);
var threwException = false;
try
{
plugin.EnablePlugin();
}
catch (Exception e)
{
_logger.Warning(new ArtemisPluginException(plugin.PluginInfo, "Failed to enable plugin", e), "Plugin exception");
plugin.PluginInfo.Enabled = false;
threwException = true;
}
// We got this far so the plugin enabled and we didn't crash horribly, yay
if (!threwException)
{
plugin.PluginInfo.PluginEntity.LastEnableSuccessful = true;
_pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity);
}
OnPluginEnabled(new PluginEventArgs(plugin.PluginInfo));
}
public void DisablePlugin(Plugin plugin)
{
plugin.PluginInfo.Enabled = false;
plugin.PluginInfo.PluginEntity.IsEnabled = false;
_pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity);
plugin.DisablePlugin();
// We got this far so the plugin enabled and we didn't crash horribly, yay
_pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity);
OnPluginDisabled(new PluginEventArgs(plugin.PluginInfo));
}
/// <inheritdoc />
public PluginInfo GetPluginInfo(Plugin plugin)
{

View File

@ -61,6 +61,7 @@ namespace Artemis.Core.Services
catch (Exception e)
{
_logger.Error(e, "Exception during device loading for device provider {deviceProvider}", deviceProvider.GetType().Name);
throw e;
}
if (deviceProvider.Devices == null)

View File

@ -9,9 +9,9 @@ namespace Artemis.Core.Services
{
private readonly PluginSettings _pluginSettings;
internal SettingsService(IPluginSettingRepository pluginSettingRepository)
internal SettingsService(IPluginRepository pluginRepository)
{
_pluginSettings = new PluginSettings(Constants.CorePluginInfo, pluginSettingRepository);
_pluginSettings = new PluginSettings(Constants.CorePluginInfo, pluginRepository);
}
public PluginSetting<T> GetSetting<T>(string name, T defaultValue = default)

View File

@ -0,0 +1,16 @@
using System;
namespace Artemis.Storage.Entities.Plugins
{
/// <summary>
/// Represents the configuration of a plugin, each plugin has one configuration
/// </summary>
public class PluginEntity
{
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
public bool IsEnabled { get; set; }
public bool LastEnableSuccessful { get; set; }
}
}

View File

@ -1,7 +1,10 @@
using System;
namespace Artemis.Storage.Entities
namespace Artemis.Storage.Entities.Plugins
{
/// <summary>
/// Represents the setting of a plugin, a plugin can have multiple settings
/// </summary>
public class PluginSettingEntity
{
public Guid Id { get; set; }

View File

@ -0,0 +1,16 @@
using System;
using Artemis.Storage.Entities.Plugins;
namespace Artemis.Storage.Repositories.Interfaces
{
public interface IPluginRepository : IRepository
{
void AddPlugin(PluginEntity pluginEntity);
PluginEntity GetPluginByGuid(Guid pluginGuid);
void SavePlugin(PluginEntity pluginEntity);
void AddSetting(PluginSettingEntity pluginSettingEntity);
PluginSettingEntity GetSettingByGuid(Guid pluginGuid);
PluginSettingEntity GetSettingByNameAndGuid(string name, Guid pluginGuid);
void SaveSetting(PluginSettingEntity pluginSettingEntity);
}
}

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using Artemis.Storage.Entities;
namespace Artemis.Storage.Repositories.Interfaces
{
public interface IPluginSettingRepository : IRepository
{
void Add(PluginSettingEntity pluginSettingEntity);
List<PluginSettingEntity> GetByPluginGuid(Guid pluginGuid);
PluginSettingEntity GetByNameAndPluginGuid(string name, Guid pluginGuid);
void Save(PluginSettingEntity pluginSettingEntity);
}
}

View File

@ -0,0 +1,56 @@
using System;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
namespace Artemis.Storage.Repositories
{
public class PluginRepository : IPluginRepository
{
private readonly LiteRepository _repository;
internal PluginRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<PluginEntity>().EnsureIndex(s => s.PluginGuid);
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => s.Name);
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => s.PluginGuid);
}
public void AddPlugin(PluginEntity pluginEntity)
{
_repository.Insert(pluginEntity);
}
public PluginEntity GetPluginByGuid(Guid pluginGuid)
{
return _repository.FirstOrDefault<PluginEntity>(p => p.PluginGuid == pluginGuid);
}
public void SavePlugin(PluginEntity pluginEntity)
{
_repository.Upsert(pluginEntity);
}
public void AddSetting(PluginSettingEntity pluginSettingEntity)
{
_repository.Insert(pluginSettingEntity);
}
public PluginSettingEntity GetSettingByGuid(Guid pluginGuid)
{
return _repository.FirstOrDefault<PluginSettingEntity>(p => p.PluginGuid == pluginGuid);
}
public PluginSettingEntity GetSettingByNameAndGuid(string name, Guid pluginGuid)
{
return _repository.FirstOrDefault<PluginSettingEntity>(p => p.Name == name && p.PluginGuid == pluginGuid);
}
public void SaveSetting(PluginSettingEntity pluginSettingEntity)
{
_repository.Upsert(pluginSettingEntity);
}
}
}

View File

@ -1,40 +0,0 @@
using System;
using System.Collections.Generic;
using Artemis.Storage.Entities;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
namespace Artemis.Storage.Repositories
{
public class PluginSettingRepository : IPluginSettingRepository
{
private readonly LiteRepository _repository;
internal PluginSettingRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => s.Name);
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => s.PluginGuid);
}
public void Add(PluginSettingEntity pluginSettingEntity)
{
_repository.Insert(pluginSettingEntity);
}
public List<PluginSettingEntity> GetByPluginGuid(Guid pluginGuid)
{
return _repository.Query<PluginSettingEntity>().Where(p => p.PluginGuid == pluginGuid).ToList();
}
public PluginSettingEntity GetByNameAndPluginGuid(string name, Guid pluginGuid)
{
return _repository.FirstOrDefault<PluginSettingEntity>(p => p.Name == name && p.PluginGuid == pluginGuid);
}
public void Save(PluginSettingEntity pluginSettingEntity)
{
_repository.Upsert(pluginSettingEntity);
}
}
}

View File

@ -49,11 +49,6 @@
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="MaterialDesignExtensions">
<HintPath>..\..\..\..\.nuget\materialdesignextensions\3.0.0\lib\netcoreapp3.0\MaterialDesignExtensions.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Fonts\RobotoMono-Regular.ttf" />
</ItemGroup>

View File

@ -7,6 +7,7 @@
xmlns:controls="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:shared="clr-namespace:Artemis.UI.Shared"
mc:Ignorable="d"
Title="Gradient Editor"
Background="{DynamicResource MaterialDesignPaper}"
@ -18,7 +19,41 @@
Icon="/Resources/Images/Logo/logo-512.png"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid>
<StackPanel>
<materialDesign:Card >
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Margin="16">
<materialDesign:PackIcon Kind="Crane" Width="80" Height="80" HorizontalAlignment="Center" />
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="0 15">
Gradient saving not implemented yet
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center">
Soon you'll be able to store different gradients for usage throughout your profiles and quickly select them
</TextBlock>
</StackPanel>
</materialDesign:Card>
</Grid>
<materialDesign:Card>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Separator Grid.Row="1" Grid.ColumnSpan="4" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="8 0 8 0" />
<Label Grid.Row="0" Grid.Column="0">Color:</Label>
<shared:ColorPicker x:Name="CurrentColor" Grid.Row="0" Grid.Column="1" />
<Label Grid.Row="0" Grid.Column="2">Location:</Label>
<TextBox x:Name="CurrentLocation" Grid.Row="0" Grid.Column="3" />
</Grid>
</materialDesign:Card>
</StackPanel>
</controls:MaterialWindow>

View File

@ -0,0 +1,12 @@
namespace Artemis.UI.Events
{
public class RequestSelectSidebarItemEvent
{
public string Label { get; }
public RequestSelectSidebarItemEvent(string label)
{
Label = label;
}
}
}

View File

@ -23,6 +23,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
private readonly ICoreService _coreService;
private readonly List<LayerPropertyViewModel> _layerPropertyViewModels;
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private readonly IPropertyTreeVmFactory _propertyTreeVmFactory;
private readonly IPropertyTimelineVmFactory _propertyTimelineVmFactory;
private readonly IProfileEditorService _profileEditorService;
private readonly ISettingsService _settingsService;
@ -37,14 +39,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
_coreService = coreService;
_settingsService = settingsService;
_layerPropertyVmFactory = layerPropertyVmFactory;
_propertyTreeVmFactory = propertyTreeVmFactory;
_propertyTimelineVmFactory = propertyTimelineVmFactory;
_layerPropertyViewModels = new List<LayerPropertyViewModel>();
PixelsPerSecond = 31;
PropertyTree = propertyTreeVmFactory.Create(this);
PropertyTimeline = propertyTimelineVmFactory.Create(this);
PopulateProperties(_profileEditorService.SelectedProfileElement, null);
}
}
public bool Playing { get; set; }
public bool RepeatAfterLastKeyframe { get; set; }
@ -71,6 +71,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
protected override void OnInitialActivate()
{
PropertyTree = _propertyTreeVmFactory.Create(this);
PropertyTimeline = _propertyTimelineVmFactory.Create(this);
PopulateProperties(_profileEditorService.SelectedProfileElement, null);
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
_profileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;

View File

@ -2,7 +2,6 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Utilities;

View File

@ -186,10 +186,9 @@ namespace Artemis.UI.Screens.Settings
foreach (var device in _surfaceService.ActiveSurface.Devices)
DeviceSettingsViewModels.Add(_deviceSettingsVmFactory.Create(device));
// TODO: GetPluginsOfType isn't ideal here as it doesn't include disabled plugins
Plugins.Clear();
foreach (var plugin in _pluginService.GetPluginsOfType<Plugin>())
Plugins.Add(new PluginSettingsViewModel(plugin, _windowManager, _dialogService));
foreach (var pluginInfo in _pluginService.GetAllPluginInfo())
Plugins.Add(new PluginSettingsViewModel(pluginInfo.Instance, _windowManager, _dialogService, _pluginService));
base.OnInitialActivate();
}

View File

@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Shared.Services.Interfaces;
using Stylet;
@ -10,21 +11,27 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
{
private readonly IDialogService _dialogService;
private readonly Plugin _plugin;
private readonly IPluginService _pluginService;
private readonly IWindowManager _windowManager;
public PluginSettingsViewModel(Plugin plugin, IWindowManager windowManager, IDialogService dialogService)
public PluginSettingsViewModel(Plugin plugin, IWindowManager windowManager, IDialogService dialogService, IPluginService pluginService)
{
_plugin = plugin;
_windowManager = windowManager;
_dialogService = dialogService;
IsEnabled = true;
_pluginService = pluginService;
}
public string Type => _plugin.GetType().BaseType?.Name ?? _plugin.GetType().Name;
public string Name => _plugin.PluginInfo.Name;
public string Description => _plugin.PluginInfo.Description;
public Version Version => _plugin.PluginInfo.Version;
public bool IsEnabled { get; set; }
public bool IsEnabled
{
get => _plugin.PluginInfo.Enabled;
set => Task.Run(() => UpdateEnabled(value));
}
public bool CanOpenSettings => IsEnabled && _plugin.HasConfigurationViewModel;
@ -42,5 +49,16 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
throw;
}
}
private void UpdateEnabled(in bool enable)
{
if (_plugin.PluginInfo.Enabled == enable)
return;
if (enable)
_pluginService.EnablePlugin(_plugin);
else
_pluginService.DisablePlugin(_plugin);
}
}
}

View File

@ -25,7 +25,7 @@
</ComboBox>
</StackPanel>
<controls:SideNavigation Items="{Binding SidebarItems}" WillSelectNavigationItemCommand="{s:Action SelectItem}" />
<controls:SideNavigation Items="{Binding SidebarItems}" SelectedItem="{Binding SelectedItem}" WillSelectNavigationItemCommand="{s:Action SelectItem}" />
</StackPanel>
</UserControl>

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using Artemis.Core.Events;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Events;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Home;
using Artemis.UI.Screens.News;
@ -18,13 +19,13 @@ using Stylet;
namespace Artemis.UI.Screens.Sidebar
{
public class SidebarViewModel : PropertyChangedBase
public class SidebarViewModel : PropertyChangedBase, IHandle<RequestSelectSidebarItemEvent>
{
private readonly IKernel _kernel;
private readonly IModuleVmFactory _moduleVmFactory;
private readonly IPluginService _pluginService;
public SidebarViewModel(IKernel kernel, IModuleVmFactory moduleVmFactory, IPluginService pluginService)
public SidebarViewModel(IKernel kernel, IEventAggregator eventAggregator, IModuleVmFactory moduleVmFactory, IPluginService pluginService)
{
_kernel = kernel;
_moduleVmFactory = moduleVmFactory;
@ -36,6 +37,8 @@ namespace Artemis.UI.Screens.Sidebar
SetupSidebar();
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled += PluginServiceOnPluginDisabled;
eventAggregator.Subscribe(this);
}
public BindableCollection<INavigationItem> SidebarItems { get; set; }
@ -66,53 +69,6 @@ namespace Artemis.UI.Screens.Sidebar
Task.Run(() => SelectSidebarItem(SidebarItems[1]));
}
private async Task SelectSidebarItem(INavigationItem sidebarItem)
{
// A module was selected if the dictionary contains the selected item
if (SidebarModules.ContainsKey(sidebarItem))
await ActivateModule(sidebarItem);
else if (sidebarItem is FirstLevelNavigationItem navigationItem)
{
if (navigationItem.Label == "Home")
await ActivateViewModel<HomeViewModel>();
else if (navigationItem.Label == "News")
await ActivateViewModel<NewsViewModel>();
else if (navigationItem.Label == "Workshop")
await ActivateViewModel<WorkshopViewModel>();
else if (navigationItem.Label == "Surface Editor")
await ActivateViewModel<SurfaceEditorViewModel>();
else if (navigationItem.Label == "Settings")
await ActivateViewModel<SettingsViewModel>();
}
else if (await CloseCurrentItem())
SelectedItem = null;
}
private async Task<bool> CloseCurrentItem()
{
if (SelectedItem == null)
return true;
var canClose = await SelectedItem.CanCloseAsync();
if (!canClose)
return false;
SelectedItem.Close();
return true;
}
private async Task ActivateViewModel<T>()
{
if (await CloseCurrentItem())
SelectedItem = (IScreen) _kernel.Get<T>();
}
private async Task ActivateModule(INavigationItem sidebarItem)
{
if (await CloseCurrentItem())
SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.Create(SidebarModules[sidebarItem]) : null;
}
// ReSharper disable once UnusedMember.Global - Called by view
public async Task SelectItem(WillSelectNavigationItemEventArgs args)
{
@ -151,6 +107,56 @@ namespace Artemis.UI.Screens.Sidebar
SidebarModules.Remove(existing.Key);
}
private async Task SelectSidebarItem(INavigationItem sidebarItem)
{
// A module was selected if the dictionary contains the selected item
if (SidebarModules.ContainsKey(sidebarItem))
await ActivateModule(sidebarItem);
else if (sidebarItem is FirstLevelNavigationItem navigationItem)
await ActivateViewModel(navigationItem.Label);
else if (await CloseCurrentItem())
SelectedItem = null;
}
private async Task<bool> CloseCurrentItem()
{
if (SelectedItem == null)
return true;
var canClose = await SelectedItem.CanCloseAsync();
if (!canClose)
return false;
SelectedItem.Close();
return true;
}
private async Task ActivateViewModel(string label)
{
if (label == "Home")
await ActivateViewModel<HomeViewModel>();
else if (label == "News")
await ActivateViewModel<NewsViewModel>();
else if (label == "Workshop")
await ActivateViewModel<WorkshopViewModel>();
else if (label == "Surface Editor")
await ActivateViewModel<SurfaceEditorViewModel>();
else if (label == "Settings")
await ActivateViewModel<SettingsViewModel>();
}
private async Task ActivateViewModel<T>()
{
if (await CloseCurrentItem())
SelectedItem = (IScreen) _kernel.Get<T>();
}
private async Task ActivateModule(INavigationItem sidebarItem)
{
if (await CloseCurrentItem())
SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.Create(SidebarModules[sidebarItem]) : null;
}
#region Event handlers
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
@ -165,6 +171,11 @@ namespace Artemis.UI.Screens.Sidebar
RemoveModule(module);
}
public void Handle(RequestSelectSidebarItemEvent message)
{
Execute.OnUIThread(async () => await ActivateViewModel(message.Label));
}
#endregion
}
}

View File

@ -20,9 +20,29 @@
DoubleClickCommand="{s:Action TrayBringToForeground}">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="Bring to foreground" Command="{s:Action TrayBringToForeground}">
<MenuItem Header="Home" Command="{s:Action TrayActivateSidebarItem}" CommandParameter="Home">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ArrangeBringToFront" />
<materialDesign:PackIcon Kind="Home" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="News" Command="{s:Action TrayActivateSidebarItem}" CommandParameter="News">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Newspaper" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Workshop" Command="{s:Action TrayActivateSidebarItem}" CommandParameter="Workshop">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="TestTube" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Surface Editor" Command="{s:Action TrayActivateSidebarItem}" CommandParameter="Surface Editor">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Edit" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Settings" Command="{s:Action TrayActivateSidebarItem}" CommandParameter="Settings">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Settings" />
</MenuItem.Icon>
</MenuItem>
<Separator />

View File

@ -1,6 +1,7 @@
using System.Windows;
using Artemis.Core.Services;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Events;
using Artemis.UI.Screens.Splash;
using Ninject;
using Stylet;
@ -9,15 +10,16 @@ namespace Artemis.UI.Screens
{
public class TrayViewModel : Screen
{
private readonly ICoreService _coreService;
private readonly IKernel _kernel;
private readonly IWindowManager _windowManager;
private readonly IEventAggregator _eventAggregator;
private SplashViewModel _splashViewModel;
public TrayViewModel(IKernel kernel, IWindowManager windowManager, ICoreService coreService, ISettingsService settingsService)
public TrayViewModel(IKernel kernel, IWindowManager windowManager, IEventAggregator eventAggregator, ICoreService coreService, ISettingsService settingsService)
{
_kernel = kernel;
_windowManager = windowManager;
_coreService = coreService;
_eventAggregator = eventAggregator;
CanShowRootViewModel = true;
var autoRunning = Bootstrapper.StartupArguments.Contains("-autorun");
@ -25,7 +27,7 @@ namespace Artemis.UI.Screens
if (!autoRunning || showOnAutoRun)
{
ShowSplashScreen();
_coreService.Initialized += (sender, args) => TrayBringToForeground();
coreService.Initialized += (sender, args) => TrayBringToForeground();
}
}
@ -35,16 +37,24 @@ namespace Artemis.UI.Screens
{
if (!CanShowRootViewModel)
return;
CanShowRootViewModel = false;
CanShowRootViewModel = false;
Execute.OnUIThread(() =>
{
_splashViewModel?.RequestClose();
_splashViewModel = null;
var rootViewModel = _kernel.Get<RootViewModel>();
rootViewModel.Closed += RootViewModelOnClosed;
_windowManager.ShowWindow(rootViewModel);
});
}
public void TrayActivateSidebarItem(string sidebarItem)
{
TrayBringToForeground();
_eventAggregator.Publish(new RequestSelectSidebarItemEvent(sidebarItem));
}
public void TrayExit()
{
Application.Current.Shutdown();
@ -54,8 +64,8 @@ namespace Artemis.UI.Screens
{
Execute.OnUIThread(() =>
{
var splashViewModel = _kernel.Get<SplashViewModel>();
_windowManager.ShowWindow(splashViewModel);
_splashViewModel = _kernel.Get<SplashViewModel>();
_windowManager.ShowWindow(_splashViewModel);
});
}

View File

@ -5,13 +5,19 @@ VisualStudioVersion = 16.0.28729.10
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Artemis.UI", "Artemis.UI\Artemis.UI.csproj", "{46B74153-77CF-4489-BDF9-D53FDB1F7ACB}"
ProjectSection(ProjectDependencies) = postProject
{07678400-2FE1-4C6E-A8D4-4F9F3C0630EA} = {07678400-2FE1-4C6E-A8D4-4F9F3C0630EA}
{AB80F106-5444-46AA-A255-F765DD2F04F1} = {AB80F106-5444-46AA-A255-F765DD2F04F1}
{8DC7960F-6DDF-4007-A155-17E124F39374} = {8DC7960F-6DDF-4007-A155-17E124F39374}
{DCF7C321-95DC-4507-BB61-A7C5356E58EC} = {DCF7C321-95DC-4507-BB61-A7C5356E58EC}
{E592F239-FAA0-4840-9C85-46E5867D06D5} = {E592F239-FAA0-4840-9C85-46E5867D06D5}
{36C10640-A31F-4DEE-9F0E-9B9E3F12753D} = {36C10640-A31F-4DEE-9F0E-9B9E3F12753D}
{0F288A66-6EB0-4589-8595-E33A3A3EAEA2} = {0F288A66-6EB0-4589-8595-E33A3A3EAEA2}
{A46F278A-FC2C-4342-8455-994D957DDA03} = {A46F278A-FC2C-4342-8455-994D957DDA03}
{26902C94-3EBC-4132-B7F0-FFCAB8E150DA} = {26902C94-3EBC-4132-B7F0-FFCAB8E150DA}
{7F4C7AB0-4C9B-452D-AFED-34544C903DEF} = {7F4C7AB0-4C9B-452D-AFED-34544C903DEF}
{235A45C7-24AD-4F47-B9D4-CD67E610A04D} = {235A45C7-24AD-4F47-B9D4-CD67E610A04D}
{D004FEC9-0CF8-4828-B620-95DBA73201A3} = {D004FEC9-0CF8-4828-B620-95DBA73201A3}
{FA5815D3-EA87-4A64-AD6C-A5AE96C61F29} = {FA5815D3-EA87-4A64-AD6C-A5AE96C61F29}
{C6BDB6D9-062D-4C28-A280-F3BD6197F07F} = {C6BDB6D9-062D-4C28-A280-F3BD6197F07F}
{A779B2F8-C253-4C4B-8634-6EB8F594E96D} = {A779B2F8-C253-4C4B-8634-6EB8F594E96D}
EndProjectSection

View File

@ -0,0 +1,14 @@
namespace Artemis.Plugins.Devices.WS281X.Settings
{
public class DeviceDefinition
{
public DeviceDefinitionType Type { get; set; }
public string Port { get; set; }
}
public enum DeviceDefinitionType
{
Arduino,
Bitwizard
}
}

View File

@ -1,17 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Generic;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Abstract.ViewModels;
using Artemis.Core.Plugins.Models;
using Artemis.Plugins.Devices.WS281X.Settings;
namespace Artemis.Plugins.Devices.WS281X.ViewModels
{
public class WS281XConfigurationViewModel : PluginConfigurationViewModel
{
public WS281XConfigurationViewModel(Plugin plugin) : base(plugin)
private PluginSetting<List<DeviceDefinition>> _definitions;
public WS281XConfigurationViewModel(Plugin plugin, PluginSettings settings) : base(plugin)
{
var WS281XInstance = RGB.NET.Devices.WS281X.WS281XDeviceProvider.Instance;
_definitions = settings.GetSetting<List<DeviceDefinition>>("DeviceDefinitions");
}
}
}
}

View File

@ -1,26 +1,47 @@
using Artemis.Core.Plugins.Abstract;
using System;
using System.Collections.Generic;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Abstract.ViewModels;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces;
using Artemis.Plugins.Devices.WS281X.Settings;
using Artemis.Plugins.Devices.WS281X.ViewModels;
using RGB.NET.Devices.WS281X.Arduino;
using RGB.NET.Devices.WS281X.Bitwizard;
namespace Artemis.Plugins.Devices.WS281X
{
// ReSharper disable once UnusedMember.Global
public class WS281XDeviceProvider : DeviceProvider
{
public PluginSettings Settings { get; }
private readonly IRgbService _rgbService;
public WS281XDeviceProvider(PluginInfo pluginInfo, IRgbService rgbService) : base(pluginInfo, RGB.NET.Devices.WS281X.WS281XDeviceProvider.Instance)
public WS281XDeviceProvider(PluginInfo pluginInfo, IRgbService rgbService, PluginSettings settings) : base(pluginInfo, RGB.NET.Devices.WS281X.WS281XDeviceProvider.Instance)
{
Settings = settings;
_rgbService = rgbService;
HasConfigurationViewModel = true;
}
public override void EnablePlugin()
{
// TODO: Load from configuration
//RGB.NET.Devices.WS281X.WS281XDeviceProvider.Instance.AddDeviceDefinition();
var definitions = Settings.GetSetting<List<DeviceDefinition>>("DeviceDefinitions");
foreach (var deviceDefinition in definitions.Value)
{
switch (deviceDefinition.Type)
{
case DeviceDefinitionType.Arduino:
RGB.NET.Devices.WS281X.WS281XDeviceProvider.Instance.AddDeviceDefinition(new ArduinoWS281XDeviceDefinition(deviceDefinition.Port));
break;
case DeviceDefinitionType.Bitwizard:
RGB.NET.Devices.WS281X.WS281XDeviceProvider.Instance.AddDeviceDefinition(new BitwizardWS281XDeviceDefinition(deviceDefinition.Port));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
_rgbService.AddDeviceProvider(RgbDeviceProvider);
}
@ -38,7 +59,7 @@ namespace Artemis.Plugins.Devices.WS281X
public override PluginConfigurationViewModel GetConfigurationViewModel()
{
return new WS281XConfigurationViewModel(this);
return new WS281XConfigurationViewModel(this, Settings);
}
}
}