mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Profile editor - Implemented timeline segments
Profile editor - Added snapping to multiple elements of the timeline
This commit is contained in:
1 parent
77dd804e74
commit
732ee2f429
17 files changed
+527
-154
No files matched your search
@@ -8,6 +8,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using Artemis.Core.Models.Surface;
|
||||
using RGB.NET.Core;
|
||||
using Size = System.Windows.Size;
|
||||
@@ -28,17 +29,20 @@ namespace Artemis.UI.Shared.Controls
|
||||
private readonly DrawingGroup _backingStore;
|
||||
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
|
||||
private BitmapImage _deviceImage;
|
||||
private bool _subscribed;
|
||||
private ArtemisDevice _oldDevice;
|
||||
private Task _lastRenderTask;
|
||||
private readonly DispatcherTimer _timer;
|
||||
|
||||
public DeviceVisualizer()
|
||||
{
|
||||
_backingStore = new DrawingGroup();
|
||||
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
|
||||
|
||||
Loaded += (sender, args) => SubscribeToUpdate(true);
|
||||
Unloaded += (sender, args) => SubscribeToUpdate(false);
|
||||
// Run an update timer at 25 fps
|
||||
_timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(40)};
|
||||
_timer.Tick += TimerOnTick;
|
||||
|
||||
Loaded += (sender, args) => _timer.Start();
|
||||
Unloaded += (sender, args) => _timer.Stop();
|
||||
}
|
||||
|
||||
public ArtemisDevice Device
|
||||
@@ -61,7 +65,7 @@ namespace Artemis.UI.Shared.Controls
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RGBSurface.Instance.Updated -= RgbSurfaceOnUpdated;
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
@@ -73,7 +77,7 @@ namespace Artemis.UI.Shared.Controls
|
||||
var measureSize = MeasureOverride(Size.Empty);
|
||||
var scale = Math.Min(DesiredSize.Width / measureSize.Width, DesiredSize.Height / measureSize.Height);
|
||||
var scaledRect = new Rect(0, 0, measureSize.Width * scale, measureSize.Height * scale);
|
||||
|
||||
|
||||
// Center and scale the visualization in the desired bounding box
|
||||
if (DesiredSize.Width > 0 && DesiredSize.Height > 0)
|
||||
{
|
||||
@@ -95,7 +99,7 @@ namespace Artemis.UI.Shared.Controls
|
||||
// Render device and LED images
|
||||
if (_deviceImage != null)
|
||||
drawingContext.DrawImage(_deviceImage, new Rect(0, 0, Device.RgbDevice.Size.Width, Device.RgbDevice.Size.Height));
|
||||
|
||||
|
||||
foreach (var deviceVisualizerLed in _deviceVisualizerLeds)
|
||||
deviceVisualizerLed.RenderImage(drawingContext);
|
||||
|
||||
@@ -113,26 +117,18 @@ namespace Artemis.UI.Shared.Controls
|
||||
return rotationRect.Size;
|
||||
}
|
||||
|
||||
|
||||
private void TimerOnTick(object sender, EventArgs e)
|
||||
{
|
||||
if (ShowColors && Visibility == Visibility.Visible)
|
||||
Render();
|
||||
}
|
||||
|
||||
private void UpdateTransform()
|
||||
{
|
||||
InvalidateVisual();
|
||||
InvalidateMeasure();
|
||||
}
|
||||
|
||||
private void SubscribeToUpdate(bool subscribe)
|
||||
{
|
||||
if (_subscribed == subscribe)
|
||||
return;
|
||||
|
||||
if (subscribe)
|
||||
RGBSurface.Instance.Updated += RgbSurfaceOnUpdated;
|
||||
else
|
||||
RGBSurface.Instance.Updated -= RgbSurfaceOnUpdated;
|
||||
|
||||
_subscribed = subscribe;
|
||||
}
|
||||
|
||||
private static void DevicePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var deviceVisualizer = (DeviceVisualizer) d;
|
||||
@@ -208,17 +204,7 @@ namespace Artemis.UI.Shared.Controls
|
||||
if (e.PropertyName == nameof(Device.RgbDevice.Scale) || e.PropertyName == nameof(Device.RgbDevice.Rotation))
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
private void RgbSurfaceOnUpdated(UpdatedEventArgs e)
|
||||
{
|
||||
_lastRenderTask?.Wait();
|
||||
|
||||
_lastRenderTask = Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (ShowColors && Visibility == Visibility.Visible)
|
||||
Render();
|
||||
}).Task;
|
||||
}
|
||||
|
||||
|
||||
private void Render()
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Artemis.UI.Shared.Controls
|
||||
var r = Led.RgbLed.Color.GetR();
|
||||
var g = Led.RgbLed.Color.GetG();
|
||||
var b = Led.RgbLed.Color.GetB();
|
||||
|
||||
|
||||
drawingContext.DrawRectangle(isDimmed
|
||||
? new SolidColorBrush(Color.FromArgb(100, r, g, b))
|
||||
: new SolidColorBrush(Color.FromRgb(r, g, b)), null, LedRect);
|
||||
|
||||
@@ -74,5 +74,13 @@ namespace Artemis.UI.Shared.Services.Interfaces
|
||||
PropertyInputRegistration RegisterPropertyInput<T>(PluginInfo pluginInfo) where T : PropertyInputViewModel;
|
||||
|
||||
void RemovePropertyInput(PropertyInputRegistration registration);
|
||||
|
||||
/// <summary>
|
||||
/// Snaps the given time to the closest relevant element in the timeline, this can be the cursor, a keyframe or a segment end.
|
||||
/// </summary>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="tolerance">How close the time must be to snap</param>
|
||||
/// <returns></returns>
|
||||
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace Artemis.UI.Shared.Services
|
||||
_registeredPropertyEditors = new List<PropertyInputRegistration>();
|
||||
|
||||
Kernel = kernel;
|
||||
PixelsPerSecond = 31;
|
||||
PixelsPerSecond = 100;
|
||||
}
|
||||
|
||||
public IKernel Kernel { get; }
|
||||
@@ -48,7 +48,7 @@ namespace Artemis.UI.Shared.Services
|
||||
set
|
||||
{
|
||||
if (_currentTime.Equals(value)) return;
|
||||
if (value > SelectedProfileElement.TimelineLength)
|
||||
if (SelectedProfileElement != null && value > SelectedProfileElement.TimelineLength)
|
||||
_currentTime = SelectedProfileElement.TimelineLength;
|
||||
else
|
||||
_currentTime = value;
|
||||
@@ -205,6 +205,47 @@ namespace Artemis.UI.Shared.Services
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes)
|
||||
{
|
||||
if (snapToSegments)
|
||||
{
|
||||
// Snap to the end of the start segment
|
||||
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.StartSegmentLength.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.StartSegmentLength;
|
||||
|
||||
// Snap to the end of the main segment
|
||||
var mainSegmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
|
||||
if (Math.Abs(time.TotalMilliseconds - mainSegmentEnd.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return mainSegmentEnd;
|
||||
|
||||
// Snap to the end of the end segment (end of the timeline)
|
||||
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.TimelineLength.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.TimelineLength;
|
||||
}
|
||||
|
||||
if (snapToCurrentTime)
|
||||
{
|
||||
// Snap to the current time
|
||||
if (Math.Abs(time.TotalMilliseconds - CurrentTime.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.StartSegmentLength;
|
||||
}
|
||||
|
||||
if (snapToKeyframes)
|
||||
{
|
||||
// Get all visible keyframes
|
||||
var keyframes = SelectedProfileElement.GetAllKeyframes()
|
||||
.Where(k => SelectedProfileElement.IsPropertyGroupExpanded(k.BaseLayerProperty.Parent))
|
||||
.ToList();
|
||||
|
||||
// Find the closest keyframe
|
||||
var closeKeyframe = keyframes.FirstOrDefault(k => Math.Abs(time.TotalMilliseconds - k.Position.TotalMilliseconds) < tolerance.TotalMilliseconds);
|
||||
if (closeKeyframe != null)
|
||||
return closeKeyframe.Position;
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
public Module GetCurrentModule()
|
||||
{
|
||||
return (Module) SelectedProfile?.PluginInfo.Instance;
|
||||
|
||||
Reference in new issue
Block a user