Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertBeekman committed May 18, 2024
2 parents d9a3a23 + 3022c7d commit 7052ee3
Show file tree
Hide file tree
Showing 18 changed files with 407 additions and 56 deletions.
16 changes: 15 additions & 1 deletion src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using RGB.NET.Core;

namespace Artemis.Core.DeviceProviders;
Expand All @@ -15,7 +16,7 @@ public abstract class DeviceProvider : PluginFeature
/// The RGB.NET device provider backing this Artemis device provider
/// </summary>
public abstract IRGBDeviceProvider RgbDeviceProvider { get; }

/// <summary>
/// A boolean indicating whether this device provider detects the physical layout of connected keyboards.
/// <para>
Expand Down Expand Up @@ -48,6 +49,11 @@ public abstract class DeviceProvider : PluginFeature
/// </summary>
public bool RemoveExcessiveLedsSupported { get; protected set; } = true;

/// <summary>
/// Gets or sets a boolean indicating whether suspending the device provider is supported
/// </summary>
public bool SuspendSupported { get; protected set; }

/// <summary>
/// Loads a layout for the specified device and wraps it in an <see cref="ArtemisLayout" />
/// </summary>
Expand Down Expand Up @@ -109,4 +115,12 @@ public virtual string GetDeviceLayoutName(ArtemisDevice device)

return fileName + ".xml";
}

/// <summary>
/// Called when the device provider is being suspended, like when the system is going to sleep.
/// Note: This will be called while the plugin is disabled.
/// </summary>
public virtual void Suspend()
{
}
}
91 changes: 85 additions & 6 deletions src/Artemis.Core/Services/DeviceService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Core.DeviceProviders;
using Artemis.Core.Providers;
using Artemis.Core.Services.Models;
using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces;
using DryIoc;
using RGB.NET.Core;
using Serilog;

Expand All @@ -21,8 +21,10 @@ internal class DeviceService : IDeviceService
private readonly IDeviceRepository _deviceRepository;
private readonly Lazy<IRenderService> _renderService;
private readonly Func<List<ILayoutProvider>> _getLayoutProviders;
private readonly List<ArtemisDevice> _enabledDevices = new();
private readonly List<ArtemisDevice> _devices = new();
private readonly List<ArtemisDevice> _enabledDevices = [];
private readonly List<ArtemisDevice> _devices = [];
private readonly List<DeviceProvider> _suspendedDeviceProviders = [];
private readonly object _suspensionLock = new();

public DeviceService(ILogger logger,
IPluginManagementService pluginManagementService,
Expand Down Expand Up @@ -69,7 +71,7 @@ public void AddDeviceProvider(DeviceProvider deviceProvider)
OnDeviceRemoved(new DeviceEventArgs(device));
}

List<Exception> providerExceptions = new();
List<Exception> providerExceptions = [];

void DeviceProviderOnException(object? sender, ExceptionEventArgs e)
{
Expand All @@ -95,7 +97,7 @@ void DeviceProviderOnException(object? sender, ExceptionEventArgs e)
return;
}

List<ArtemisDevice> addedDevices = new();
List<ArtemisDevice> addedDevices = [];
foreach (IRGBDevice rgbDevice in rgbDeviceProvider.Devices)
{
ArtemisDevice artemisDevice = GetArtemisDevice(rgbDevice);
Expand Down Expand Up @@ -184,7 +186,7 @@ public void LoadDeviceLayout(ArtemisDevice device)
device.ApplyLayout(null, false, false);
else
provider?.ApplyLayout(device, layout);

UpdateLeds();
}
catch (Exception e)
Expand Down Expand Up @@ -241,6 +243,83 @@ public void SaveDevices()
UpdateLeds();
}

/// <inheritdoc />
public void SuspendDeviceProviders()
{
lock (_suspensionLock)
{
_logger.Information("Suspending all device providers");

bool wasPaused = _renderService.Value.IsPaused;
try
{
_renderService.Value.IsPaused = true;
foreach (DeviceProvider deviceProvider in _pluginManagementService.GetFeaturesOfType<DeviceProvider>().Where(d => d.SuspendSupported))
SuspendDeviceProvider(deviceProvider);
}
finally
{
_renderService.Value.IsPaused = wasPaused;
}
}
}

/// <inheritdoc />
public void ResumeDeviceProviders()
{
lock (_suspensionLock)
{
_logger.Information("Resuming all device providers");

bool wasPaused = _renderService.Value.IsPaused;
try
{
_renderService.Value.IsPaused = true;
foreach (DeviceProvider deviceProvider in _suspendedDeviceProviders.ToList())
ResumeDeviceProvider(deviceProvider);
}
finally
{
_renderService.Value.IsPaused = wasPaused;
}
}
}

private void SuspendDeviceProvider(DeviceProvider deviceProvider)
{
if (_suspendedDeviceProviders.Contains(deviceProvider))
{
_logger.Warning("Device provider {DeviceProvider} is already suspended", deviceProvider.Info.Name);
return;
}

try
{
_pluginManagementService.DisablePluginFeature(deviceProvider, false);
deviceProvider.Suspend();
_suspendedDeviceProviders.Add(deviceProvider);
_logger.Information("Device provider {DeviceProvider} suspended", deviceProvider.Info.Name);
}
catch (Exception e)
{
_logger.Error(e, "Device provider {DeviceProvider} failed to suspend", deviceProvider.Info.Name);
}
}

private void ResumeDeviceProvider(DeviceProvider deviceProvider)
{
try
{
_pluginManagementService.EnablePluginFeature(deviceProvider, false, true);
_suspendedDeviceProviders.Remove(deviceProvider);
_logger.Information("Device provider {DeviceProvider} resumed", deviceProvider.Info.Name);
}
catch (Exception e)
{
_logger.Error(e, "Device provider {DeviceProvider} failed to resume", deviceProvider.Info.Name);
}
}

private ArtemisDevice GetArtemisDevice(IRGBDevice rgbDevice)
{
string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
Expand Down
11 changes: 11 additions & 0 deletions src/Artemis.Core/Services/Interfaces/IDeviceService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Artemis.Core.DeviceProviders;

namespace Artemis.Core.Services;
Expand Down Expand Up @@ -71,6 +72,16 @@ public interface IDeviceService : IArtemisService
/// </summary>
void SaveDevices();

/// <summary>
/// Suspends all active device providers
/// </summary>
void SuspendDeviceProviders();

/// <summary>
/// Resumes all previously active device providers
/// </summary>
void ResumeDeviceProviders();

/// <summary>
/// Occurs when a single device was added.
/// </summary>
Expand Down
91 changes: 80 additions & 11 deletions src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
Expand Down Expand Up @@ -69,12 +70,19 @@ public class DataModelPicker : TemplatedControl
public static readonly StyledProperty<bool> IsEventPickerProperty =
AvaloniaProperty.Register<DataModelPicker, bool>(nameof(IsEventPicker));

private readonly ObservableCollection<DataModelVisualizationViewModel> _searchResults = [];
private TextBlock? _currentPathDescription;
private TextBlock? _currentPathDisplay;

private MaterialIcon? _currentPathIcon;
private TreeView? _dataModelTreeView;
private ListBox? _searchListBox;
private TextBox? _searchBox;
private Panel? _searchContainer;
private StackPanel? _searchEmpty;
private DispatcherTimer? _updateTimer;
private string? _lastSearch;
private bool _firstUpdate;

static DataModelPicker()
{
Expand Down Expand Up @@ -200,7 +208,14 @@ private void Update(object? sender, EventArgs e)
if (DataModelUIService == null)
return;

DataModelViewModel?.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker));
DataModelViewModel?.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker, !string.IsNullOrEmpty(_searchBox?.Text)));
ApplySearch();

if (_firstUpdate)
{
_firstUpdate = false;
UpdateCurrentPath(true);
}
}

private void GetDataModel()
Expand Down Expand Up @@ -229,14 +244,15 @@ private void DataModelOnUpdateRequested(object? sender, EventArgs e)
if (DataModelUIService == null || DataModelViewModel == null)
return;

DataModelViewModel.Update(DataModelUIService, new DataModelUpdateConfiguration(IsEventPicker));
DataModelViewModel.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker, !string.IsNullOrEmpty(_searchBox?.Text)));
DataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
ApplySearch();
}

private void DataModelTreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
private void TreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
// Multi-select isn't a think so grab the first one
object? selected = _dataModelTreeView?.SelectedItems[0];
// Multi-select isn't a thing so grab the first one
object? selected = e.AddedItems[0];
if (selected == null)
return;

Expand All @@ -255,12 +271,48 @@ private void DataModelTreeViewOnDoubleTapped(object? sender, RoutedEventArgs e)
treeViewItem.IsExpanded = !treeViewItem.IsExpanded;
}

private void ApplySearch()
{
if (DataModelViewModel == null || string.IsNullOrWhiteSpace(_searchBox?.Text))
{
if (_dataModelTreeView != null)
_dataModelTreeView.IsVisible = true;
if (_searchContainer != null)
_searchContainer.IsVisible = false;
return;
}

if (_dataModelTreeView != null)
_dataModelTreeView.IsVisible = false;
if (_searchContainer != null)
_searchContainer.IsVisible = true;

if (_searchBox.Text != _lastSearch)
{
_searchResults.Clear();
foreach (DataModelVisualizationViewModel searchResult in DataModelViewModel.GetSearchResults(_searchBox.Text).DistinctBy(r => r.Path).Take(20))
_searchResults.Add(searchResult);
_lastSearch = _searchBox.Text;
}

if (_searchListBox != null)
{
_searchListBox.IsVisible = _searchResults.Count > 0;
_searchListBox.SelectedItem = _searchResults.FirstOrDefault(r => r.DataModelPath?.Path == DataModelPath?.Path);
}

if (_searchEmpty != null)
_searchEmpty.IsVisible = _searchResults.Count == 0;


}

private void UpdateCurrentPath(bool selectCurrentPath)
{
if (DataModelPath == null)
if (DataModelPath == null || _dataModelTreeView == null)
return;

if (_dataModelTreeView != null && selectCurrentPath)
if (selectCurrentPath)
{
// Expand the path
DataModel? start = DataModelPath.Target;
Expand All @@ -286,19 +338,30 @@ private void UpdateCurrentPath(bool selectCurrentPath)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_dataModelTreeView != null)
_dataModelTreeView.SelectionChanged -= DataModelTreeViewOnSelectionChanged;
_dataModelTreeView.SelectionChanged -= TreeViewOnSelectionChanged;
if (_dataModelTreeView != null)
_dataModelTreeView.DoubleTapped -= DataModelTreeViewOnDoubleTapped;

_currentPathIcon = e.NameScope.Find<MaterialIcon>("CurrentPathIcon");
_currentPathDisplay = e.NameScope.Find<TextBlock>("CurrentPathDisplay");
_currentPathDescription = e.NameScope.Find<TextBlock>("CurrentPathDescription");
_dataModelTreeView = e.NameScope.Find<TreeView>("DataModelTreeView");
_searchBox = e.NameScope.Find<TextBox>("SearchBox");
_searchContainer = e.NameScope.Find<Panel>("SearchContainer");
_searchListBox = e.NameScope.Find<ListBox>("SearchListBox");
_searchEmpty = e.NameScope.Find<StackPanel>("SearchEmpty");

if (_dataModelTreeView != null)
_dataModelTreeView.SelectionChanged += DataModelTreeViewOnSelectionChanged;
if (_dataModelTreeView != null)
{
_dataModelTreeView.SelectionChanged += TreeViewOnSelectionChanged;
_dataModelTreeView.DoubleTapped += DataModelTreeViewOnDoubleTapped;
}

if (_searchListBox != null)
{
_searchListBox.ItemsSource = _searchResults;
_searchListBox.SelectionChanged += TreeViewOnSelectionChanged;
}
}

#region Overrides of Visual
Expand All @@ -307,17 +370,23 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
GetDataModel();
UpdateCurrentPath(true);

_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Background, Update);
_updateTimer.Start();
_firstUpdate = true;
}

/// <inheritdoc />
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
DataModelViewModel?.Dispose();

_updateTimer?.Stop();
_updateTimer = null;

_lastSearch = null;
if (_searchBox != null)
_searchBox.Text = null;
}

#endregion
Expand Down
Loading

0 comments on commit 7052ee3

Please sign in to comment.