mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Profile editor - Redesigned adding/removing timeline segments
General module - Added some basic window information to the data model Profiles - Don't render when opacity is 0 Profile editor - Render non-selected layers in their main segment Profile editor - Condition editor fixes
This commit is contained in:
parent
527fef3dc6
commit
72d606f40d
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Windows.Interop;
|
||||
using Artemis.Core.Plugins.Models;
|
||||
|
||||
namespace Artemis.Core
|
||||
@ -67,7 +68,9 @@ namespace Artemis.Core
|
||||
{
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(decimal)
|
||||
typeof(decimal)
|
||||
};
|
||||
|
||||
public static IntPtr MainWindowHandle { get; internal set; }
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Events
|
||||
{
|
||||
public class PropertyGroupUpdatingEventArgs : EventArgs
|
||||
{
|
||||
public PropertyGroupUpdatingEventArgs(double deltaTime)
|
||||
{
|
||||
DeltaTime = deltaTime;
|
||||
}
|
||||
|
||||
public PropertyGroupUpdatingEventArgs(TimeSpan overrideTime)
|
||||
{
|
||||
OverrideTime = overrideTime;
|
||||
}
|
||||
|
||||
public double DeltaTime { get; }
|
||||
public TimeSpan OverrideTime { get; }
|
||||
}
|
||||
}
|
||||
@ -159,7 +159,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
||||
if (DisplayConditionPredicateEntity.LeftDataModelGuid != null)
|
||||
{
|
||||
var dataModel = dataModelService.GetPluginDataModelByGuid(DisplayConditionPredicateEntity.LeftDataModelGuid.Value);
|
||||
if (dataModel != null)
|
||||
if (dataModel != null && dataModel.ContainsPath(DisplayConditionPredicateEntity.LeftPropertyPath))
|
||||
UpdateLeftSide(dataModel, DisplayConditionPredicateEntity.LeftPropertyPath);
|
||||
}
|
||||
|
||||
@ -175,7 +175,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
||||
if (DisplayConditionPredicateEntity.RightDataModelGuid != null)
|
||||
{
|
||||
var dataModel = dataModelService.GetPluginDataModelByGuid(DisplayConditionPredicateEntity.RightDataModelGuid.Value);
|
||||
if (dataModel != null)
|
||||
if (dataModel != null && dataModel.ContainsPath(DisplayConditionPredicateEntity.RightPropertyPath))
|
||||
UpdateRightSide(dataModel, DisplayConditionPredicateEntity.RightPropertyPath);
|
||||
}
|
||||
// Right side static
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Models.Profile.LayerProperties;
|
||||
using Artemis.Core.Plugins.LayerEffect.Abstract;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
@ -69,8 +68,15 @@ namespace Artemis.Core.Models.Profile
|
||||
|
||||
UpdateDisplayCondition();
|
||||
|
||||
// Update the layer timeline, this will give us a new delta time which could be negative in case the main segment wrapped back
|
||||
// to it's start
|
||||
var timelineDeltaTime = UpdateTimeline(deltaTime);
|
||||
|
||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
baseLayerEffect.Update(deltaTime);
|
||||
{
|
||||
baseLayerEffect.BaseProperties?.Update();
|
||||
baseLayerEffect.Update(timelineDeltaTime);
|
||||
}
|
||||
|
||||
// Iterate the children in reverse because that's how they must be rendered too
|
||||
for (var index = Children.Count - 1; index > -1; index--)
|
||||
@ -80,9 +86,49 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
}
|
||||
|
||||
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
var beginTime = TimelinePosition;
|
||||
|
||||
if (stickToMainSegment)
|
||||
{
|
||||
if (!RepeatMainSegment)
|
||||
{
|
||||
var position = timeOverride + StartSegmentLength;
|
||||
if (position > StartSegmentLength + EndSegmentLength)
|
||||
TimelinePosition = StartSegmentLength + EndSegmentLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
var progress = timeOverride.TotalMilliseconds % MainSegmentLength.TotalMilliseconds;
|
||||
if (progress > 0)
|
||||
TimelinePosition = TimeSpan.FromMilliseconds(progress) + StartSegmentLength;
|
||||
else
|
||||
TimelinePosition = StartSegmentLength;
|
||||
}
|
||||
}
|
||||
else
|
||||
TimelinePosition = timeOverride;
|
||||
|
||||
var delta = (TimelinePosition - beginTime).TotalSeconds;
|
||||
|
||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
{
|
||||
baseLayerEffect.BaseProperties?.Update();
|
||||
baseLayerEffect.Update(delta);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
if (!Enabled || Path == null || !Children.Any(c => c.Enabled))
|
||||
if (Path == null || !Enabled || !Children.Any(c => c.Enabled))
|
||||
return;
|
||||
|
||||
// No need to render if at the end of the timeline
|
||||
if (TimelinePosition > TimelineLength)
|
||||
return;
|
||||
|
||||
if (_folderBitmap == null)
|
||||
@ -103,6 +149,10 @@ namespace Artemis.Core.Models.Profile
|
||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
baseLayerEffect.PreProcess(folderCanvas, _folderBitmap.Info, folderPath, folderPaint);
|
||||
|
||||
// No point rendering if the alpha was set to zero by one of the effects
|
||||
if (folderPaint.Color.Alpha == 0)
|
||||
return;
|
||||
|
||||
// Iterate the children in reverse because the first layer must be rendered last to end up on top
|
||||
for (var index = Children.Count - 1; index > -1; index--)
|
||||
{
|
||||
@ -176,6 +226,7 @@ namespace Artemis.Core.Models.Profile
|
||||
OnRenderPropertiesUpdated();
|
||||
}
|
||||
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
{
|
||||
FolderEntity.Id = EntityId;
|
||||
@ -196,6 +247,16 @@ namespace Artemis.Core.Models.Profile
|
||||
DisplayConditionGroup?.ApplyToEntity();
|
||||
}
|
||||
|
||||
internal void Deactivate()
|
||||
{
|
||||
_folderBitmap?.Dispose();
|
||||
_folderBitmap = null;
|
||||
|
||||
var layerEffects = new List<BaseLayerEffect>(LayerEffects);
|
||||
foreach (var baseLayerEffect in layerEffects)
|
||||
DeactivateLayerEffect(baseLayerEffect);
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler RenderPropertiesUpdated;
|
||||
@ -206,15 +267,5 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal void Deactivate()
|
||||
{
|
||||
_folderBitmap?.Dispose();
|
||||
_folderBitmap = null;
|
||||
|
||||
var layerEffects = new List<BaseLayerEffect>(LayerEffects);
|
||||
foreach (var baseLayerEffect in layerEffects)
|
||||
DeactivateLayerEffect(baseLayerEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,7 +117,7 @@ namespace Artemis.Core.Models.Profile
|
||||
get => _layerBrush;
|
||||
internal set => SetAndNotify(ref _layerBrush, value);
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
|
||||
@ -134,7 +134,7 @@ namespace Artemis.Core.Models.Profile
|
||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
||||
foreach (var baseLayerProperty in LayerBrush.BaseProperties.GetAllLayerProperties())
|
||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
||||
|
||||
|
||||
return keyframes;
|
||||
}
|
||||
|
||||
@ -214,34 +214,66 @@ namespace Artemis.Core.Models.Profile
|
||||
/// <inheritdoc />
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
if (LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
|
||||
if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
|
||||
return;
|
||||
|
||||
// Ensure the layer must still be displayed
|
||||
UpdateDisplayCondition();
|
||||
deltaTime = UpdateTimeline(deltaTime);
|
||||
|
||||
General.Update(deltaTime);
|
||||
Transform.Update(deltaTime);
|
||||
LayerBrush.BaseProperties?.Update(deltaTime);
|
||||
LayerBrush.Update(deltaTime);
|
||||
// TODO: No point updating further than this if the layer is not going to be rendered
|
||||
|
||||
// Update the layer timeline, this will give us a new delta time which could be negative in case the main segment wrapped back
|
||||
// to it's start
|
||||
var timelineDeltaTime = UpdateTimeline(deltaTime);
|
||||
|
||||
General.Update();
|
||||
Transform.Update();
|
||||
LayerBrush.BaseProperties?.Update();
|
||||
LayerBrush.Update(timelineDeltaTime);
|
||||
|
||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
{
|
||||
baseLayerEffect.BaseProperties?.Update(deltaTime);
|
||||
baseLayerEffect.Update(deltaTime);
|
||||
baseLayerEffect.BaseProperties?.Update();
|
||||
baseLayerEffect.Update(timelineDeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void OverrideProgress(TimeSpan timeOverride)
|
||||
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
|
||||
{
|
||||
General.Override(timeOverride);
|
||||
Transform.Override(timeOverride);
|
||||
LayerBrush?.BaseProperties?.Override(timeOverride);
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.BaseProperties?.Override(timeOverride);
|
||||
if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
|
||||
return;
|
||||
|
||||
var beginTime = TimelinePosition;
|
||||
|
||||
if (stickToMainSegment)
|
||||
{
|
||||
if (!RepeatMainSegment)
|
||||
{
|
||||
var position = timeOverride + StartSegmentLength;
|
||||
if (position > StartSegmentLength + EndSegmentLength)
|
||||
TimelinePosition = StartSegmentLength + EndSegmentLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
var progress = timeOverride.TotalMilliseconds % MainSegmentLength.TotalMilliseconds;
|
||||
TimelinePosition = TimeSpan.FromMilliseconds(progress) + StartSegmentLength;
|
||||
}
|
||||
}
|
||||
else
|
||||
TimelinePosition = timeOverride;
|
||||
|
||||
var delta = (TimelinePosition - beginTime).TotalSeconds;
|
||||
|
||||
General.Update();
|
||||
Transform.Update();
|
||||
LayerBrush.BaseProperties?.Update();
|
||||
LayerBrush.Update(delta);
|
||||
|
||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
{
|
||||
baseLayerEffect.BaseProperties?.Update();
|
||||
baseLayerEffect.Update(delta);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -280,6 +312,10 @@ namespace Artemis.Core.Models.Profile
|
||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
baseLayerEffect.PreProcess(layerCanvas, _layerBitmap.Info, layerPath, layerPaint);
|
||||
|
||||
// No point rendering if the alpha was set to zero by one of the effects
|
||||
if (layerPaint.Color.Alpha == 0)
|
||||
return;
|
||||
|
||||
layerCanvas.ClipPath(layerPath);
|
||||
|
||||
if (!LayerBrush.SupportsTransformation)
|
||||
|
||||
@ -65,11 +65,6 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
/// </summary>
|
||||
public bool IsLoadedFromStorage { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total progress on the timeline
|
||||
/// </summary>
|
||||
public TimeSpan TimelineProgress { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to declare that this property doesn't belong to a plugin and should use the core plugin GUID
|
||||
/// </summary>
|
||||
|
||||
@ -101,7 +101,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
currentKeyframe.Value = value;
|
||||
|
||||
// Update the property so that the new keyframe is reflected on the current value
|
||||
Update(0);
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,17 +184,15 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the property, moving the timeline forwards by the provided <paramref name="deltaTime" />
|
||||
/// Updates the property, applying keyframes to the current value
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The amount of time to move the timeline forwards</param>
|
||||
internal void Update(double deltaTime)
|
||||
internal void Update()
|
||||
{
|
||||
TimelineProgress = TimelineProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
||||
if (!KeyframesSupported || !KeyframesEnabled)
|
||||
return;
|
||||
|
||||
// The current keyframe is the last keyframe before the current time
|
||||
CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= TimelineProgress);
|
||||
CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= ProfileElement.TimelinePosition);
|
||||
// Keyframes are sorted by position so we can safely assume the next keyframe's position is after the current
|
||||
var nextIndex = _keyframes.IndexOf(CurrentKeyframe) + 1;
|
||||
NextKeyframe = _keyframes.Count > nextIndex ? _keyframes[nextIndex] : null;
|
||||
@ -208,7 +206,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
else
|
||||
{
|
||||
var timeDiff = NextKeyframe.Position - CurrentKeyframe.Position;
|
||||
var keyframeProgress = (float)((TimelineProgress - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds);
|
||||
var keyframeProgress = (float)((ProfileElement.TimelinePosition - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds);
|
||||
var keyframeProgressEased = (float)Easings.Interpolate(keyframeProgress, CurrentKeyframe.EasingFunction);
|
||||
UpdateCurrentValue(keyframeProgress, keyframeProgressEased);
|
||||
}
|
||||
@ -216,16 +214,6 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
OnUpdated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the timeline progress to match the provided <paramref name="overrideTime" />
|
||||
/// </summary>
|
||||
/// <param name="overrideTime">The new progress to set the layer property timeline to.</param>
|
||||
internal void OverrideProgress(TimeSpan overrideTime)
|
||||
{
|
||||
TimelineProgress = TimeSpan.Zero;
|
||||
Update(overrideTime.TotalSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the keyframes in ascending order by position
|
||||
/// </summary>
|
||||
@ -242,8 +230,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
|
||||
PropertyEntity = entity;
|
||||
LayerPropertyGroup = layerPropertyGroup;
|
||||
LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(args.DeltaTime);
|
||||
LayerPropertyGroup.PropertyGroupOverriding += (sender, args) => OverrideProgress(args.OverrideTime);
|
||||
LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@ -223,19 +223,13 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update(double deltaTime)
|
||||
internal void Update()
|
||||
{
|
||||
// Since at this point we don't know what properties the group has without using reflection,
|
||||
// let properties subscribe to the update event and update themselves
|
||||
OnPropertyGroupUpdating(new PropertyGroupUpdatingEventArgs(deltaTime));
|
||||
OnPropertyGroupUpdating();
|
||||
}
|
||||
|
||||
internal void Override(TimeSpan overrideTime)
|
||||
{
|
||||
// Same as above, but now the progress is overridden
|
||||
OnPropertyGroupOverriding(new PropertyGroupUpdatingEventArgs(overrideTime));
|
||||
}
|
||||
|
||||
|
||||
private void InitializeProperty(RenderProfileElement profileElement, string path, BaseLayerProperty instance)
|
||||
{
|
||||
Guid pluginGuid;
|
||||
@ -260,8 +254,7 @@ namespace Artemis.Core.Models.Profile
|
||||
|
||||
#region Events
|
||||
|
||||
internal event EventHandler<PropertyGroupUpdatingEventArgs> PropertyGroupUpdating;
|
||||
internal event EventHandler<PropertyGroupUpdatingEventArgs> PropertyGroupOverriding;
|
||||
internal event EventHandler PropertyGroupUpdating;
|
||||
public event EventHandler PropertyGroupInitialized;
|
||||
|
||||
/// <summary>
|
||||
@ -269,14 +262,9 @@ namespace Artemis.Core.Models.Profile
|
||||
/// </summary>
|
||||
public event EventHandler VisibilityChanged;
|
||||
|
||||
internal virtual void OnPropertyGroupUpdating(PropertyGroupUpdatingEventArgs e)
|
||||
protected virtual void OnPropertyGroupUpdating()
|
||||
{
|
||||
PropertyGroupUpdating?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyGroupOverriding(PropertyGroupUpdatingEventArgs e)
|
||||
{
|
||||
PropertyGroupOverriding?.Invoke(this, e);
|
||||
PropertyGroupUpdating?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnVisibilityChanged()
|
||||
|
||||
@ -164,7 +164,7 @@ namespace Artemis.Core.Models.Profile
|
||||
public TimeSpan TimelinePosition
|
||||
{
|
||||
get => _timelinePosition;
|
||||
private set => SetAndNotify(ref _timelinePosition, value);
|
||||
protected set => SetAndNotify(ref _timelinePosition, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -212,13 +212,19 @@ namespace Artemis.Core.Models.Profile
|
||||
// Skip to the last segment if conditions are no longer met
|
||||
if (!AlwaysFinishTimeline && TimelinePosition < mainSegmentEnd)
|
||||
TimelinePosition = mainSegmentEnd;
|
||||
else if (TimelinePosition >= TimelineLength)
|
||||
TimelinePosition = TimelineLength;
|
||||
}
|
||||
|
||||
return (TimelinePosition - oldPosition).TotalSeconds;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the progress of the element
|
||||
/// </summary>
|
||||
/// <param name="timeOverride"></param>
|
||||
/// <param name="stickToMainSegment"></param>
|
||||
public abstract void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Effects
|
||||
|
||||
@ -34,5 +34,10 @@ namespace Artemis.Core.Plugins.Abstract.DataModels.Attributes
|
||||
/// Gets or sets an optional minimum value, this value is not enforced but used for percentage calculations.
|
||||
/// </summary>
|
||||
public object MinValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this property resets the max depth of the data model, defaults to true
|
||||
/// </summary>
|
||||
public bool ResetsDepth { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,7 @@ namespace Artemis.Core.Services
|
||||
_profileService = profileService;
|
||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||
_frameStopWatch = new Stopwatch();
|
||||
|
||||
|
||||
UpdatePluginCache();
|
||||
ConfigureJsonConvert();
|
||||
|
||||
@ -96,6 +96,12 @@ namespace Artemis.Core.Services
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
|
||||
public void SetMainWindowHandle(IntPtr handle)
|
||||
{
|
||||
Constants.MainWindowHandle = handle;
|
||||
}
|
||||
|
||||
protected virtual void OnFrameRendering(FrameRenderingEventArgs e)
|
||||
{
|
||||
FrameRendering?.Invoke(this, e);
|
||||
@ -109,7 +115,7 @@ namespace Artemis.Core.Services
|
||||
private void UpdatePluginCache()
|
||||
{
|
||||
_modules = _pluginService.GetPluginsOfType<Module>().Where(p => p.Enabled).ToList();
|
||||
_dataModelExpansions = _pluginService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.Enabled).ToList();
|
||||
_dataModelExpansions = _pluginService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.Enabled).ToList();
|
||||
}
|
||||
|
||||
private void ConfigureJsonConvert()
|
||||
|
||||
@ -50,5 +50,11 @@ namespace Artemis.Core.Services.Interfaces
|
||||
/// Occurs whenever a frame is finished rendering and processed by RGB.NET
|
||||
/// </summary>
|
||||
event EventHandler<FrameRenderedEventArgs> FrameRendered;
|
||||
|
||||
/// <summary>
|
||||
/// To be called by the UI to setup the main window handle
|
||||
/// </summary>
|
||||
/// <param name="handle"></param>
|
||||
void SetMainWindowHandle(IntPtr handle);
|
||||
}
|
||||
}
|
||||
@ -167,7 +167,7 @@ namespace Artemis.UI.Shared.Controls
|
||||
private void Input_PreviewTextInput(object sender, TextCompositionEventArgs e)
|
||||
{
|
||||
var seperator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
|
||||
var regex = new Regex("^[" + seperator + "][0-9]+$|^[0-9]*[" + seperator + "]{0,1}[0-9]*$");
|
||||
var regex = new Regex("^[" + seperator + "][-|0-9]+$|^-?[0-9]*[" + seperator + "]{0,1}[0-9]*$");
|
||||
e.Handled = !regex.IsMatch(e.Text);
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
|
||||
foreach (var propertyInfo in ListType.GetProperties())
|
||||
{
|
||||
var child = CreateChild(dataModelVisualizationService, propertyInfo);
|
||||
var child = CreateChild(dataModelVisualizationService, propertyInfo, GetChildDepth());
|
||||
if (child != null)
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
@ -35,6 +35,9 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
|
||||
public override void Update(IDataModelVisualizationService dataModelVisualizationService)
|
||||
{
|
||||
if (Parent != null && !Parent.IsVisualizationExpanded)
|
||||
return;
|
||||
|
||||
List = GetCurrentValue() as IList;
|
||||
if (List == null)
|
||||
return;
|
||||
|
||||
@ -10,10 +10,16 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
internal DataModelPropertiesViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) : base(dataModel, parent, propertyInfo)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Update(IDataModelVisualizationService dataModelVisualizationService)
|
||||
{
|
||||
// Always populate properties
|
||||
PopulateProperties(dataModelVisualizationService);
|
||||
|
||||
// Only update children if the parent is expanded
|
||||
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
|
||||
return;
|
||||
|
||||
foreach (var dataModelVisualizationViewModel in Children)
|
||||
dataModelVisualizationViewModel.Update(dataModelVisualizationService);
|
||||
}
|
||||
@ -22,7 +28,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
return Parent.IsRootViewModel ? DataModel : base.GetCurrentValue();
|
||||
}
|
||||
|
||||
|
||||
private void PopulateProperties(IDataModelVisualizationService dataModelVisualizationService)
|
||||
{
|
||||
if (Children.Any())
|
||||
@ -31,10 +37,15 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
var modelType = Parent.IsRootViewModel ? DataModel.GetType() : PropertyInfo.PropertyType;
|
||||
foreach (var propertyInfo in modelType.GetProperties())
|
||||
{
|
||||
var child = CreateChild(dataModelVisualizationService, propertyInfo);
|
||||
var child = CreateChild(dataModelVisualizationService, propertyInfo, GetChildDepth());
|
||||
if (child != null)
|
||||
Children.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
protected int GetChildDepth()
|
||||
{
|
||||
return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,6 +49,9 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
|
||||
public override void Update(IDataModelVisualizationService dataModelVisualizationService)
|
||||
{
|
||||
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
|
||||
return;
|
||||
|
||||
if (DisplayViewModel == null && dataModelVisualizationService.RegisteredDataModelDisplays.Any(d => d.SupportedType == PropertyInfo.PropertyType))
|
||||
dataModelVisualizationService.GetDataModelDisplayViewModel(PropertyInfo.PropertyType);
|
||||
|
||||
|
||||
@ -15,9 +15,11 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
public abstract class DataModelVisualizationViewModel : PropertyChangedBase
|
||||
{
|
||||
private const int MaxDepth = 4;
|
||||
private BindableCollection<DataModelVisualizationViewModel> _children;
|
||||
private DataModel _dataModel;
|
||||
private bool _isMatchingFilteredTypes;
|
||||
private bool _isVisualizationExpanded;
|
||||
private DataModelVisualizationViewModel _parent;
|
||||
private DataModelPropertyAttribute _propertyDescription;
|
||||
private PropertyInfo _propertyInfo;
|
||||
@ -37,6 +39,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
}
|
||||
|
||||
public bool IsRootViewModel { get; }
|
||||
public int Depth { get; set; }
|
||||
|
||||
public DataModel DataModel
|
||||
{
|
||||
@ -74,6 +77,16 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
set => SetAndNotify(ref _isMatchingFilteredTypes, value);
|
||||
}
|
||||
|
||||
public bool IsVisualizationExpanded
|
||||
{
|
||||
get => _isVisualizationExpanded;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _isVisualizationExpanded, value)) return;
|
||||
RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public string PropertyPath
|
||||
{
|
||||
get
|
||||
@ -104,11 +117,26 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the datamodel and if in an parent, any children
|
||||
/// </summary>
|
||||
/// <param name="dataModelVisualizationService"></param>
|
||||
public abstract void Update(IDataModelVisualizationService dataModelVisualizationService);
|
||||
|
||||
public virtual object GetCurrentValue()
|
||||
{
|
||||
return Parent == null ? null : PropertyInfo.GetValue(Parent.GetCurrentValue());
|
||||
try
|
||||
{
|
||||
if (PropertyInfo.GetGetMethod() == null)
|
||||
return null;
|
||||
|
||||
return Parent == null ? null : PropertyInfo.GetValue(Parent.GetCurrentValue());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored, who knows what kind of shit can go wrong here...
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyTypeFilter(bool looseMatch, params Type[] filteredTypes)
|
||||
@ -157,6 +185,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
if (predicate.LeftDataModel == null || predicate.LeftPropertyPath == null)
|
||||
return null;
|
||||
|
||||
return GetChildByPath(predicate.LeftDataModel.PluginInfo.Guid, predicate.LeftPropertyPath);
|
||||
}
|
||||
|
||||
@ -167,6 +196,14 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
|
||||
public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath)
|
||||
{
|
||||
// Ensure children are populated by requesting an update
|
||||
if (!IsVisualizationExpanded)
|
||||
{
|
||||
IsVisualizationExpanded = true;
|
||||
RequestUpdate();
|
||||
IsVisualizationExpanded = false;
|
||||
}
|
||||
|
||||
var path = propertyPath.Split(".");
|
||||
var currentPart = path.First();
|
||||
|
||||
@ -187,8 +224,10 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
}
|
||||
}
|
||||
|
||||
protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, PropertyInfo propertyInfo)
|
||||
protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, PropertyInfo propertyInfo, int depth)
|
||||
{
|
||||
if (depth > MaxDepth)
|
||||
return null;
|
||||
// Skip properties decorated with DataModelIgnore
|
||||
if (Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute)))
|
||||
return null;
|
||||
@ -196,19 +235,36 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
// If a display VM was found, prefer to use that in any case
|
||||
var typeViewModel = dataModelVisualizationService.GetDataModelDisplayViewModel(propertyInfo.PropertyType);
|
||||
if (typeViewModel != null)
|
||||
return new DataModelPropertyViewModel(DataModel, this, propertyInfo) {DisplayViewModel = typeViewModel};
|
||||
return new DataModelPropertyViewModel(DataModel, this, propertyInfo) {DisplayViewModel = typeViewModel, Depth = depth};
|
||||
// For primitives, create a property view model, it may be null that is fine
|
||||
if (propertyInfo.PropertyType.IsPrimitive || propertyInfo.PropertyType == typeof(string))
|
||||
return new DataModelPropertyViewModel(DataModel, this, propertyInfo);
|
||||
return new DataModelPropertyViewModel(DataModel, this, propertyInfo) {Depth = depth};
|
||||
if (typeof(IList).IsAssignableFrom(propertyInfo.PropertyType))
|
||||
return new DataModelListViewModel(DataModel, this, propertyInfo);
|
||||
return new DataModelListViewModel(DataModel, this, propertyInfo) {Depth = depth};
|
||||
// For other value types create a child view model
|
||||
if (propertyInfo.PropertyType.IsClass || propertyInfo.PropertyType.IsStruct())
|
||||
return new DataModelPropertiesViewModel(DataModel, this, propertyInfo);
|
||||
return new DataModelPropertiesViewModel(DataModel, this, propertyInfo) {Depth = depth};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler UpdateRequested;
|
||||
|
||||
protected virtual void OnUpdateRequested()
|
||||
{
|
||||
UpdateRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void RequestUpdate()
|
||||
{
|
||||
Parent?.RequestUpdate();
|
||||
OnUpdateRequested();
|
||||
}
|
||||
|
||||
private void GetDescription()
|
||||
{
|
||||
// If this is the first child of a root view model, use the data model description
|
||||
@ -218,7 +274,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
else if (PropertyInfo != null)
|
||||
{
|
||||
PropertyDescription = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(PropertyInfo, typeof(DataModelPropertyAttribute)) ??
|
||||
new DataModelPropertyAttribute {Name = PropertyInfo.Name.Humanize()};
|
||||
new DataModelPropertyAttribute {Name = PropertyInfo.Name.Humanize(), ResetsDepth = false};
|
||||
}
|
||||
else
|
||||
throw new ArtemisSharedUIException("Failed to get property description because plugin info is null but the parent has a datamodel");
|
||||
|
||||
@ -42,6 +42,7 @@ namespace Artemis.UI.Shared.Services
|
||||
|
||||
// Update to populate children
|
||||
viewModel.Update(this);
|
||||
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
@ -56,6 +57,7 @@ namespace Artemis.UI.Shared.Services
|
||||
|
||||
// Update to populate children
|
||||
viewModel.Update(this);
|
||||
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
@ -185,6 +187,7 @@ namespace Artemis.UI.Shared.Services
|
||||
if (initialValue == null)
|
||||
initialValue = Activator.CreateInstance(registration.SupportedType);
|
||||
}
|
||||
|
||||
// This assumes the type can be converted, that has been checked when the VM was created
|
||||
if (initialValue != null && initialValue.GetType() != registration.SupportedType)
|
||||
initialValue = Convert.ChangeType(initialValue, registration.SupportedType);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Models.Profile;
|
||||
using Artemis.Core.Models.Profile.LayerProperties;
|
||||
using Artemis.Core.Plugins.Abstract;
|
||||
using Artemis.Core.Plugins.Models;
|
||||
using Artemis.UI.Shared.Events;
|
||||
@ -53,7 +54,7 @@ namespace Artemis.UI.Shared.Services.Interfaces
|
||||
/// Occurs when the current editor time is changed
|
||||
/// </summary>
|
||||
event EventHandler CurrentTimeChanged;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the pixels per second (zoom level) is changed
|
||||
/// </summary>
|
||||
@ -76,11 +77,16 @@ namespace Artemis.UI.Shared.Services.Interfaces
|
||||
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.
|
||||
/// 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>
|
||||
/// <param name="snapToSegments">Enable snapping to timeline segments</param>
|
||||
/// <param name="snapToCurrentTime">Enable snapping to the current time of the editor</param>
|
||||
/// <param name="snapToKeyframes">Enable snapping to visible keyframes</param>
|
||||
/// <param name="excludedKeyframe">A keyframe to exclude during keyframe snapping</param>
|
||||
/// <returns></returns>
|
||||
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes);
|
||||
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null);
|
||||
}
|
||||
}
|
||||
@ -113,20 +113,11 @@ namespace Artemis.UI.Shared.Services
|
||||
if (SelectedProfile == null)
|
||||
return;
|
||||
|
||||
var delta = CurrentTime - _lastUpdateTime;
|
||||
foreach (var folder in SelectedProfile.GetAllFolders())
|
||||
{
|
||||
foreach (var baseLayerEffect in folder.LayerEffects)
|
||||
baseLayerEffect.Update(delta.TotalSeconds);
|
||||
}
|
||||
|
||||
foreach (var layer in SelectedProfile.GetAllLayers())
|
||||
{
|
||||
layer.OverrideProgress(CurrentTime);
|
||||
layer.LayerBrush?.Update(delta.TotalSeconds);
|
||||
foreach (var baseLayerEffect in layer.LayerEffects)
|
||||
baseLayerEffect.Update(delta.TotalSeconds);
|
||||
}
|
||||
// Stick to the main segment for any element that is not currently selected
|
||||
foreach (var folder in SelectedProfile.GetAllFolders())
|
||||
folder.OverrideProgress(CurrentTime, folder != SelectedProfileElement);
|
||||
foreach (var layer in SelectedProfile.GetAllLayers())
|
||||
layer.OverrideProgress(CurrentTime, layer != SelectedProfileElement);
|
||||
|
||||
_lastUpdateTime = CurrentTime;
|
||||
OnProfilePreviewUpdated();
|
||||
@ -205,7 +196,7 @@ namespace Artemis.UI.Shared.Services
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes)
|
||||
public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null)
|
||||
{
|
||||
if (snapToSegments)
|
||||
{
|
||||
|
||||
@ -12,10 +12,12 @@ namespace Artemis.UI.Converters
|
||||
public object Convert(object value, Type targetType, object parameter,
|
||||
CultureInfo culture)
|
||||
{
|
||||
if (targetType != typeof(bool))
|
||||
throw new InvalidOperationException("The target must be a boolean");
|
||||
if (targetType == typeof(bool))
|
||||
return !(bool) value;
|
||||
if (targetType == typeof(bool?))
|
||||
return !(bool?) value;
|
||||
|
||||
return !(bool) value;
|
||||
throw new InvalidOperationException("The target must be a boolean");
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter,
|
||||
|
||||
@ -10,9 +10,33 @@
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance Type=local:DisplayConditionGroupViewModel, IsDesignTimeCreatable=False}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
|
||||
<ResourceDictionary>
|
||||
<Style TargetType="Grid" x:Key="InitializingFade">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsInitialized}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.25" BeginTime="0:0:0.1">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<QuadraticEase EasingMode="EaseInOut" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
|
||||
<Grid Style="{StaticResource InitializingFade}" Opacity="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core.Models.Profile.Conditions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.Abstract;
|
||||
using Humanizer;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
{
|
||||
@ -11,11 +13,17 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
{
|
||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||
private bool _isRootGroup;
|
||||
private bool _isInitialized;
|
||||
|
||||
public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent, IDisplayConditionsVmFactory displayConditionsVmFactory) : base(
|
||||
displayConditionGroup, parent)
|
||||
{
|
||||
_displayConditionsVmFactory = displayConditionsVmFactory;
|
||||
Execute.PostToUIThread(async () =>
|
||||
{
|
||||
await Task.Delay(50);
|
||||
IsInitialized = true;
|
||||
});
|
||||
}
|
||||
|
||||
public DisplayConditionGroup DisplayConditionGroup => (DisplayConditionGroup) Model;
|
||||
@ -26,6 +34,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
set => SetAndNotify(ref _isRootGroup, value);
|
||||
}
|
||||
|
||||
public bool IsInitialized
|
||||
{
|
||||
get => _isInitialized;
|
||||
set => SetAndNotify(ref _isInitialized, value);
|
||||
}
|
||||
|
||||
public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize();
|
||||
|
||||
public void SelectBooleanOperator(string type)
|
||||
|
||||
@ -20,11 +20,28 @@
|
||||
<ResourceDictionary>
|
||||
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
|
||||
<Style TargetType="Grid" x:Key="InitializingFade">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsInitialized}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.25" BeginTime="0:0:0.1">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<QuadraticEase EasingMode="EaseInOut" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0 3">
|
||||
<Grid Margin="0 3" Style="{StaticResource InitializingFade}" Opacity="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@ -61,6 +78,7 @@
|
||||
<Setter Property="CommandParameter" Value="{Binding}" />
|
||||
<Setter Property="CommandTarget" Value="{Binding}" />
|
||||
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
|
||||
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
|
||||
</Style>
|
||||
</ContextMenu.ItemContainerStyle>
|
||||
</ContextMenu>
|
||||
|
||||
@ -28,12 +28,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
private DataModelPropertiesViewModel _rightSideDataModel;
|
||||
private DataModelInputViewModel _rightSideInputViewModel;
|
||||
private int _rightSideTransitionIndex;
|
||||
private object _rightStaticValue;
|
||||
private DataModelVisualizationViewModel _selectedLeftSideProperty;
|
||||
private DisplayConditionOperator _selectedOperator;
|
||||
private DataModelVisualizationViewModel _selectedRightSideProperty;
|
||||
|
||||
private List<Type> _supportedInputTypes;
|
||||
private object _rightStaticValue;
|
||||
private bool _isInitialized;
|
||||
|
||||
public DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent, IProfileEditorService profileEditorService,
|
||||
IDataModelVisualizationService dataModelVisualizationService, IDataModelService dataModelService, IEventAggregator eventAggregator)
|
||||
@ -56,7 +57,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
public bool ShowRightSidePropertySelection => DisplayConditionPredicate.PredicateType == PredicateType.Dynamic;
|
||||
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
|
||||
|
||||
public bool IsInitialized { get; private set; }
|
||||
public bool IsInitialized
|
||||
{
|
||||
get => _isInitialized;
|
||||
private set => SetAndNotify(ref _isInitialized, value);
|
||||
}
|
||||
|
||||
public DataModelPropertiesViewModel LeftSideDataModel
|
||||
{
|
||||
@ -156,6 +161,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
|
||||
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
|
||||
|
||||
|
||||
LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested;
|
||||
RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested;
|
||||
|
||||
IsInitialized = true;
|
||||
Update();
|
||||
}
|
||||
@ -192,6 +201,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
public void ApplyLeftSide()
|
||||
{
|
||||
DisplayConditionPredicate.UpdateLeftSide(SelectedLeftSideProperty.DataModel, SelectedLeftSideProperty.PropertyPath);
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
|
||||
SelectedOperator = DisplayConditionPredicate.Operator;
|
||||
Update();
|
||||
}
|
||||
@ -199,6 +210,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
public void ApplyRightSideDynamic()
|
||||
{
|
||||
DisplayConditionPredicate.UpdateRightSide(SelectedRightSideProperty.DataModel, SelectedRightSideProperty.PropertyPath);
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
@ -207,6 +220,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
if (isSubmitted)
|
||||
{
|
||||
DisplayConditionPredicate.UpdateRightSide(value);
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
@ -219,6 +234,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
public void ApplyOperator()
|
||||
{
|
||||
DisplayConditionPredicate.UpdateOperator(SelectedOperator);
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
@ -237,6 +254,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
_eventAggregator.Subscribe(this);
|
||||
}
|
||||
|
||||
private void RightDataModelUpdateRequested(object sender, EventArgs e)
|
||||
{
|
||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
|
||||
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
|
||||
}
|
||||
|
||||
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
|
||||
{
|
||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
|
||||
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
||||
}
|
||||
|
||||
private void ExecuteSelectLeftProperty(object context)
|
||||
{
|
||||
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
|
||||
|
||||
@ -1,73 +1,71 @@
|
||||
<UserControl
|
||||
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.Module.ProfileEditor.DisplayConditions"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:Converters="clr-namespace:Artemis.UI.Converters" x:Class="Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.DisplayConditionsView"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type local:DisplayConditionsViewModel}}">
|
||||
<UserControl.Resources>
|
||||
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
|
||||
</UserControl.Resources>
|
||||
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.Module.ProfileEditor.DisplayConditions"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
x:Class="Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.DisplayConditionsView"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type local:DisplayConditionsViewModel}}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.ColumnSpan="2" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="10 5 0 -4"><Run Text="Display conditions"/></TextBlock>
|
||||
<Separator Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="8 0" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="10 5 0 -4">
|
||||
Display conditions
|
||||
</TextBlock>
|
||||
<Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="8 0" />
|
||||
|
||||
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
|
||||
<Grid Grid.Row="2" Grid.Column="0">
|
||||
<ScrollViewer Margin="8 0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<ContentControl s:View.Model="{Binding RootGroup}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="3" Grid.Column="0" Margin="13" Orientation="Horizontal" VerticalAlignment="Bottom" ToolTip="When conditions are met, only go through the entire timeline once.">
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" Content="Play once" IsChecked="{Binding RenderProfileElement.RepeatMainSegment, Converter={StaticResource InverseBooleanConverter}, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="3" Grid.Column="1" Margin="10" HorizontalAlignment="Right">
|
||||
<TextBlock><Run Text="When conditions no longer met"/></TextBlock>
|
||||
<ListBox Style="{StaticResource MaterialDesignToolToggleListBox}" SelectedIndex="{Binding CurrentTimelineIndex}" Height="20" Margin="0 5 0 0">
|
||||
<StackPanel Grid.Row="3" Margin="10" HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Margin="0 0 10 0">When conditions no longer met</TextBlock>
|
||||
<ListBox Style="{StaticResource MaterialDesignToolToggleListBox}" SelectedIndex="{Binding ConditionBehaviourIndex}" IsEnabled="{Binding ConditionBehaviourEnabled}" Height="22">
|
||||
<ListBoxItem Padding="10 0">
|
||||
<ListBoxItem.ToolTip>
|
||||
<ToolTip Placement="Top" VerticalOffset="-5">
|
||||
<StackPanel>
|
||||
<TextBlock><Run Text="When conditions are no longer met, finish the timelines and then stop displaying."/></TextBlock>
|
||||
<TextBlock>
|
||||
When conditions are no longer met, finish the timelines and then stop displaying.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ToolTip>
|
||||
</ListBoxItem.ToolTip>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="PlayArrow" Width="20" Height="20" Margin="0 -4" />
|
||||
<TextBlock Margin="5 0 0 0" FontSize="11"><Run Text="WAIT FOR FINISH"/></TextBlock>
|
||||
<TextBlock Margin="5 0 0 0" FontSize="11">
|
||||
WAIT FOR FINISH
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ListBoxItem>
|
||||
<ListBoxItem Padding="10 0">
|
||||
<ListBoxItem.ToolTip>
|
||||
<ToolTip Placement="Top" VerticalOffset="-5">
|
||||
<StackPanel>
|
||||
<TextBlock><Run Text="When conditions are no longer met, stop displaying immediately."/></TextBlock>
|
||||
<TextBlock>
|
||||
When conditions are no longer met, stop displaying immediately.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ToolTip>
|
||||
</ListBoxItem.ToolTip>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="SkipNext" Width="20" Height="20" Margin="0 -4" />
|
||||
<TextBlock Margin="5 0 0 0" FontSize="11"><Run Text="SKIP"/></TextBlock>
|
||||
<TextBlock Margin="5 0 0 0" FontSize="11">
|
||||
SKIP
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ListBoxItem>
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -9,12 +9,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
{
|
||||
public class DisplayConditionsViewModel : ProfileEditorPanelViewModel
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||
private DisplayConditionGroupViewModel _rootGroup;
|
||||
private RenderProfileElement _renderProfileElement;
|
||||
|
||||
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_displayConditionsVmFactory = displayConditionsVmFactory;
|
||||
profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
|
||||
}
|
||||
@ -31,9 +33,28 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
||||
set => SetAndNotify(ref _renderProfileElement, value);
|
||||
}
|
||||
|
||||
public int ConditionBehaviourIndex
|
||||
{
|
||||
get => RenderProfileElement != null && RenderProfileElement.AlwaysFinishTimeline ? 0 : 1;
|
||||
set
|
||||
{
|
||||
if (RenderProfileElement == null)
|
||||
return;
|
||||
|
||||
RenderProfileElement.AlwaysFinishTimeline = value == 0;
|
||||
NotifyOfPropertyChange(nameof(ConditionBehaviourIndex));
|
||||
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ConditionBehaviourEnabled => RenderProfileElement != null;
|
||||
|
||||
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
|
||||
{
|
||||
RenderProfileElement = e.RenderProfileElement;
|
||||
NotifyOfPropertyChange(nameof(ConditionBehaviourIndex));
|
||||
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
|
||||
|
||||
if (e.RenderProfileElement == null)
|
||||
{
|
||||
|
||||
@ -8,7 +8,9 @@
|
||||
xmlns:timeline="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
|
||||
xmlns:Converters="clr-namespace:Artemis.UI.Converters" x:Class="Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.LayerPropertiesView"
|
||||
xmlns:Converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
|
||||
x:Class="Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.LayerPropertiesView"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
@ -16,6 +18,7 @@
|
||||
behaviors:InputBindingBehavior.PropagateInputBindingsToWindow="True">
|
||||
<UserControl.Resources>
|
||||
<Converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
|
||||
<Style x:Key="SvStyle" TargetType="{x:Type ScrollViewer}">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="Template">
|
||||
@ -199,58 +202,78 @@
|
||||
Width="{Binding ActualWidth, ElementName=PropertyTimeLine}" />
|
||||
|
||||
<!-- Start segment -->
|
||||
<TextBlock Grid.Column="0"
|
||||
TextAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Padding="0 3"
|
||||
FontSize="11"
|
||||
Background="{StaticResource MaterialDesignPaper}"
|
||||
Visibility="{Binding StartSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
ToolTip="This segment is played when a layer starts displaying because it's conditions are met">
|
||||
Start
|
||||
</TextBlock>
|
||||
<Grid Grid.Column="0" VerticalAlignment="Top" Background="{StaticResource MaterialDesignPaper}"
|
||||
Visibility="{Binding StartSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock TextAlignment="Center" Padding="0 3" FontSize="12" ToolTip="This segment is played when a layer starts displaying because it's conditions are met">
|
||||
Start
|
||||
</TextBlock>
|
||||
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconButton}" ToolTip="Disable start segment" Width="20" Height="20" Margin="0 0 6 0"
|
||||
Command="{s:Action DisableSegment}" CommandParameter="Start">
|
||||
<materialDesign:PackIcon Kind="Close" Height="18" Width="18" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- Main segment -->
|
||||
<TextBlock Grid.Column="1"
|
||||
TextAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Padding="0 3"
|
||||
FontSize="11"
|
||||
Background="{StaticResource MaterialDesignPaper}"
|
||||
ToolTip="This segment is played while a condition is met, either once or on a repeating loop">
|
||||
Main
|
||||
</TextBlock>
|
||||
<Grid Grid.Column="1" VerticalAlignment="Top" Background="{StaticResource MaterialDesignPaper}"
|
||||
Visibility="{Binding MainSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock TextAlignment="Center" Padding="0 3" FontSize="12" ToolTip="This segment is played while a condition is met, either once or on a repeating loop">
|
||||
Main
|
||||
</TextBlock>
|
||||
|
||||
<ToggleButton Grid.Column="1" Style="{StaticResource MaterialDesignFlatToggleButton}" ToolTip="Toggle main segment repeat" Width="16" Height="16"
|
||||
IsChecked="{Binding RepeatMainSegment}" VerticalAlignment="Center">
|
||||
<materialDesign:PackIcon Kind="Repeat" Height="12" Width="12" />
|
||||
</ToggleButton>
|
||||
|
||||
<Button Grid.Column="2" Style="{StaticResource MaterialDesignIconButton}" ToolTip="Remove" Width="20" Height="20" Margin="5 0 8 0"
|
||||
Command="{s:Action DisableSegment}" CommandParameter="Main">
|
||||
<materialDesign:PackIcon Kind="Close" Height="18" Width="18" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- End segment -->
|
||||
<TextBlock Grid.Column="2"
|
||||
TextAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Padding="0 3"
|
||||
FontSize="11"
|
||||
Background="{StaticResource MaterialDesignPaper}"
|
||||
Visibility="{Binding EndSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
ToolTip="This segment is played once a condition is no longer met">
|
||||
End
|
||||
</TextBlock>
|
||||
<Grid Grid.Column="2" VerticalAlignment="Top" Background="{StaticResource MaterialDesignPaper}"
|
||||
Visibility="{Binding EndSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock TextAlignment="Center" Padding="0 3" FontSize="12" ToolTip="This segment is played once a condition is no longer met">
|
||||
End
|
||||
</TextBlock>
|
||||
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconButton}" ToolTip="Disable end segment" Width="20" Height="20" Margin="0 0 6 0"
|
||||
Command="{s:Action DisableSegment}" CommandParameter="End">
|
||||
<materialDesign:PackIcon Kind="Close" Height="18" Width="18" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- Segment movement display -->
|
||||
<Rectangle Grid.Column="0" RadiusX="2" RadiusY="2" Fill="{DynamicResource PrimaryHueDarkBrush}" Margin="0 -1 -2 0" Width="5" Height="20" VerticalAlignment="Top"
|
||||
<Rectangle Grid.Column="0" RadiusX="2" RadiusY="2" Fill="{DynamicResource PrimaryHueDarkBrush}" Margin="0 -2 -2 0" Width="5" Height="24" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Visibility="{Binding StartSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}" />
|
||||
<Rectangle Grid.Column="1" RadiusX="2" RadiusY="2" Fill="{DynamicResource PrimaryHueDarkBrush}" Margin="0 -1 -2 0" Width="5" Height="20" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" />
|
||||
<Rectangle Grid.Column="2" RadiusX="2" RadiusY="2" Fill="{DynamicResource PrimaryHueDarkBrush}" Margin="0 -1 -2 0" Width="5" Height="20" VerticalAlignment="Top"
|
||||
<Rectangle Grid.Column="1" RadiusX="2" RadiusY="2" Fill="{DynamicResource PrimaryHueDarkBrush}" Margin="0 -2 -2 0" Width="5" Height="24" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Visibility="{Binding MainSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}" />
|
||||
<Rectangle Grid.Column="2" RadiusX="2" RadiusY="2" Fill="{DynamicResource PrimaryHueDarkBrush}" Margin="0 -2 -2 0" Width="5" Height="24" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Visibility="{Binding EndSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}" />
|
||||
|
||||
<!-- Segment movement handles -->
|
||||
<Rectangle Grid.Column="0" RadiusX="2" RadiusY="2" Fill="Transparent" Margin="0 -1 -6 0" Width="16" Height="20" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Cursor="SizeWE" Visibility="{Binding StartSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
MouseDown="{s:Action StartSegmentMouseDown}" MouseUp="{s:Action StartSegmentMouseUp}" MouseMove="{s:Action SegmentMouseMove}"/>
|
||||
<Rectangle Grid.Column="1" RadiusX="2" RadiusY="2" Fill="Transparent" Margin="0 -1 -6 0" Width="16" Height="20" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Cursor="SizeWE"
|
||||
MouseDown="{s:Action MainSegmentMouseDown}" MouseUp="{s:Action MainSegmentMouseUp}" MouseMove="{s:Action SegmentMouseMove}"/>
|
||||
<Rectangle Grid.Column="2" RadiusX="2" RadiusY="2" Fill="Transparent" Margin="0 -1 -6 0" Width="16" Height="20" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Cursor="SizeWE" Visibility="{Binding EndSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
MouseDown="{s:Action EndSegmentMouseDown}" MouseUp="{s:Action EndSegmentMouseUp}" MouseMove="{s:Action SegmentMouseMove}"/>
|
||||
<Rectangle Grid.Column="0" RadiusX="2" RadiusY="2" Fill="Transparent" Margin="0 -1 -6 0" Width="16" Height="25" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Cursor="SizeWE" Visibility="{Binding StartSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
MouseDown="{s:Action StartSegmentMouseDown}" MouseUp="{s:Action StartSegmentMouseUp}" MouseMove="{s:Action SegmentMouseMove}" />
|
||||
<Rectangle Grid.Column="1" RadiusX="2" RadiusY="2" Fill="Transparent" Margin="0 -1 -6 0" Width="16" Height="25" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Cursor="SizeWE" Visibility="{Binding MainSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
MouseDown="{s:Action MainSegmentMouseDown}" MouseUp="{s:Action MainSegmentMouseUp}" MouseMove="{s:Action SegmentMouseMove}" />
|
||||
<Rectangle Grid.Column="2" RadiusX="2" RadiusY="2" Fill="Transparent" Margin="0 -1 -6 0" Width="16" Height="25" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Cursor="SizeWE" Visibility="{Binding EndSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
MouseDown="{s:Action EndSegmentMouseDown}" MouseUp="{s:Action EndSegmentMouseUp}" MouseMove="{s:Action SegmentMouseMove}" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
@ -348,19 +371,59 @@
|
||||
Background="{DynamicResource MaterialDesignCardBackground}">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding StartSegmentEnabled}">
|
||||
Enable start segment
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" Margin="10 0" IsChecked="{Binding EndSegmentEnabled}">
|
||||
Enable end segment
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Button Grid.Column="0"
|
||||
Margin="5 -2 0 -2"
|
||||
Padding="10 0"
|
||||
Height="20"
|
||||
Width="110"
|
||||
ToolTip="Select an effect to add"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
IsEnabled="{Binding CanAddSegment}">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatMidBgButton}">
|
||||
<Style.Triggers>
|
||||
<EventTrigger RoutedEvent="Click">
|
||||
<EventTrigger.Actions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger.Actions>
|
||||
</EventTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
<Button.ContextMenu>
|
||||
<ContextMenu>
|
||||
<ContextMenu.Resources>
|
||||
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
||||
</ContextMenu.Resources>
|
||||
<MenuItem Header="Start"
|
||||
Command="{s:Action EnableSegment}"
|
||||
CommandParameter="Start"
|
||||
IsEnabled="{Binding Data.StartSegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
||||
<MenuItem Header="Main"
|
||||
Command="{s:Action EnableSegment}"
|
||||
CommandParameter="Main"
|
||||
IsEnabled="{Binding Data.MainSegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
||||
<MenuItem Header="End"
|
||||
Command="{s:Action EnableSegment}"
|
||||
CommandParameter="End"
|
||||
IsEnabled="{Binding Data.EndSegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
||||
</ContextMenu>
|
||||
</Button.ContextMenu>
|
||||
<TextBlock FontSize="11">
|
||||
ADD SEGMENT
|
||||
</TextBlock>
|
||||
</Button>
|
||||
|
||||
<!-- Zoom control -->
|
||||
<Slider Grid.Column="1"
|
||||
@ -371,7 +434,7 @@
|
||||
Maximum="350"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
AutoToolTipPlacement="TopLeft"
|
||||
AutoToolTipPlacement="TopLeft"
|
||||
Value="{Binding ProfileEditorService.PixelsPerSecond}"
|
||||
Width="319" />
|
||||
|
||||
|
||||
@ -102,27 +102,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
|
||||
public Layer SelectedLayer => SelectedProfileElement as Layer;
|
||||
public Folder SelectedFolder => SelectedProfileElement as Folder;
|
||||
|
||||
public bool StartSegmentEnabled
|
||||
{
|
||||
get => SelectedProfileElement?.StartSegmentLength != TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
SelectedProfileElement.StartSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero;
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
NotifyOfPropertyChange(nameof(StartSegmentEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public bool EndSegmentEnabled
|
||||
{
|
||||
get => SelectedProfileElement?.EndSegmentLength != TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
SelectedProfileElement.EndSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero;
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public BindableCollection<LayerPropertyGroupViewModel> LayerPropertyGroups
|
||||
{
|
||||
@ -586,10 +565,79 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
|
||||
|
||||
#region Segments
|
||||
|
||||
public bool StartSegmentEnabled
|
||||
{
|
||||
get => SelectedProfileElement?.StartSegmentLength != TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
SelectedProfileElement.StartSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero;
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
NotifyOfPropertyChange(nameof(StartSegmentEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanAddSegment));
|
||||
}
|
||||
}
|
||||
|
||||
public bool MainSegmentEnabled
|
||||
{
|
||||
get => SelectedProfileElement?.MainSegmentLength != TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
SelectedProfileElement.MainSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero;
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
NotifyOfPropertyChange(nameof(MainSegmentEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanAddSegment));
|
||||
}
|
||||
}
|
||||
|
||||
public bool EndSegmentEnabled
|
||||
{
|
||||
get => SelectedProfileElement?.EndSegmentLength != TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
SelectedProfileElement.EndSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero;
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanAddSegment));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanAddSegment => !StartSegmentEnabled || !MainSegmentEnabled || !EndSegmentEnabled;
|
||||
|
||||
public bool RepeatMainSegment
|
||||
{
|
||||
get => SelectedProfileElement?.RepeatMainSegment ?? false;
|
||||
set
|
||||
{
|
||||
SelectedProfileElement.RepeatMainSegment = value;
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
NotifyOfPropertyChange(nameof(RepeatMainSegment));
|
||||
}
|
||||
}
|
||||
|
||||
private bool _draggingStartSegment;
|
||||
private bool _draggingMainSegment;
|
||||
private bool _draggingEndSegment;
|
||||
|
||||
public void DisableSegment(string segment)
|
||||
{
|
||||
if (segment == "Start")
|
||||
StartSegmentEnabled = false;
|
||||
else if (segment == "Main")
|
||||
MainSegmentEnabled = false;
|
||||
else if (segment == "End")
|
||||
EndSegmentEnabled = false;
|
||||
}
|
||||
|
||||
public void EnableSegment(string segment)
|
||||
{
|
||||
if (segment == "Start")
|
||||
StartSegmentEnabled = true;
|
||||
else if (segment == "Main")
|
||||
MainSegmentEnabled = true;
|
||||
else if (segment == "End")
|
||||
EndSegmentEnabled = true;
|
||||
}
|
||||
|
||||
public void StartSegmentMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
((IInputElement) sender).CaptureMouse();
|
||||
|
||||
@ -54,8 +54,15 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
<TreeView Grid.Row="1" ItemsSource="{Binding MainDataModel.Children}" HorizontalContentAlignment="Stretch">
|
||||
<TreeView Grid.Row="1"
|
||||
ItemsSource="{Binding MainDataModel.Children}" HorizontalContentAlignment="Stretch"
|
||||
VirtualizingStackPanel.IsVirtualizing="True"
|
||||
VirtualizingStackPanel.VirtualizationMode="Recycling">
|
||||
<TreeView.Resources>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource MaterialDesignTreeViewItem}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}">
|
||||
</Setter>
|
||||
</Style>
|
||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{Binding Children}">
|
||||
<TextBlock Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
@ -1,29 +1,36 @@
|
||||
using Artemis.Core.Services;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Core.Services.Interfaces;
|
||||
using Artemis.Core.Utilities;
|
||||
using Artemis.UI.Events;
|
||||
using Artemis.UI.Screens.Splash;
|
||||
using Artemis.UI.Shared.Controls;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using MaterialDesignExtensions.Controls;
|
||||
using Ninject;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens
|
||||
{
|
||||
public class TrayViewModel : Screen
|
||||
public class TrayViewModel : PropertyChangedBase, IViewAware
|
||||
{
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IWindowManager _windowManager;
|
||||
private bool _setGradientPickerService;
|
||||
private SplashViewModel _splashViewModel;
|
||||
private bool _canShowRootViewModel;
|
||||
private UIElement _view;
|
||||
|
||||
public TrayViewModel(IKernel kernel, IWindowManager windowManager, IEventAggregator eventAggregator, ICoreService coreService, ISettingsService settingsService)
|
||||
{
|
||||
_kernel = kernel;
|
||||
_windowManager = windowManager;
|
||||
_eventAggregator = eventAggregator;
|
||||
_coreService = coreService;
|
||||
CanShowRootViewModel = true;
|
||||
|
||||
var autoRunning = Bootstrapper.StartupArguments.Contains("--autorun");
|
||||
@ -89,5 +96,15 @@ namespace Artemis.UI.Screens
|
||||
{
|
||||
CanShowRootViewModel = true;
|
||||
}
|
||||
|
||||
public void AttachView(UIElement view)
|
||||
{
|
||||
View = view;
|
||||
|
||||
var handle = new WindowInteropHelper((Window) view).EnsureHandle();
|
||||
_coreService.SetMainWindowHandle(handle);
|
||||
}
|
||||
|
||||
public UIElement View { get; set; }
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@ namespace Artemis.Plugins.LayerEffects.Filter
|
||||
"BlurOn"
|
||||
);
|
||||
AddLayerEffectDescriptor<DilateEffect>("Dilate", "A layer effect providing a dilation filter effect", "EyePlus");
|
||||
AddLayerEffectDescriptor<OpacityEffect>("Opacity", "A layer effect letting you change the opacity of all children", "Opacity");
|
||||
AddLayerEffectDescriptor<ErodeEffect>("Erode", "A layer effect providing an erode filter effect", "EyeMinus");
|
||||
AddLayerEffectDescriptor<GlowEffect>("Glow", "A layer effect providing a glow filter effect", "BoxShadow");
|
||||
AddLayerEffectDescriptor<GrayScaleEffect>("Gray-scale", "A layer effect providing a gray-scale filter effect", "InvertColors");
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
using Artemis.Core.Models.Profile;
|
||||
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||
using Artemis.Core.Models.Profile.LayerProperties.Types;
|
||||
using Artemis.Core.Plugins.LayerEffect.Abstract;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Plugins.LayerEffects.Filter
|
||||
{
|
||||
public class OpacityEffect : LayerEffect<OpacityEffectProperties>
|
||||
{
|
||||
public override void EnableLayerEffect()
|
||||
{
|
||||
}
|
||||
|
||||
public override void DisableLayerEffect()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
public override void PreProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint)
|
||||
{
|
||||
paint.Color = paint.Color.WithAlpha((byte) (Properties.Opacity.CurrentValue * 2.55f));
|
||||
}
|
||||
|
||||
public override void PostProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class OpacityEffectProperties : LayerPropertyGroup
|
||||
{
|
||||
[PropertyDescription(Description = "The opacity of the shape", InputAffix = "%", MinInputValue = 0f, MaxInputValue = 100f)]
|
||||
public FloatLayerProperty Opacity { get; set; }
|
||||
|
||||
protected override void PopulateDefaults()
|
||||
{
|
||||
Opacity.DefaultValue = 100f;
|
||||
}
|
||||
|
||||
protected override void OnPropertiesInitialized()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Plugins.Abstract.DataModels;
|
||||
using System.Diagnostics;
|
||||
using Artemis.Core.Plugins.Abstract.DataModels.Attributes;
|
||||
using Artemis.Plugins.Modules.General.DataModel.Windows;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Plugins.Modules.General
|
||||
namespace Artemis.Plugins.Modules.General.DataModel
|
||||
{
|
||||
public class GeneralDataModel : DataModel
|
||||
public class GeneralDataModel : Core.Plugins.Abstract.DataModels.DataModel
|
||||
{
|
||||
public TestDataModel TestDataModel { get; set; }
|
||||
public WindowsDataModel Windows { get; set; }
|
||||
|
||||
|
||||
public GeneralDataModel()
|
||||
{
|
||||
TestDataModel = new TestDataModel();
|
||||
Windows = new WindowsDataModel();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestDataModel
|
||||
{
|
||||
public TestDataModel()
|
||||
{
|
||||
PlayerInfo = new PlayerInfo();
|
||||
IntsList = new List<int>();
|
||||
@ -0,0 +1,26 @@
|
||||
using System.Diagnostics;
|
||||
using Artemis.Core.Plugins.Abstract.DataModels.Attributes;
|
||||
using Artemis.Plugins.Modules.General.Utilities;
|
||||
|
||||
namespace Artemis.Plugins.Modules.General.DataModel.Windows
|
||||
{
|
||||
public class WindowDataModel
|
||||
{
|
||||
[DataModelIgnore]
|
||||
public Process Process { get; }
|
||||
|
||||
public WindowDataModel(Process process)
|
||||
{
|
||||
Process = process;
|
||||
WindowTitle = process.MainWindowTitle;
|
||||
ProcessName = process.ProcessName;
|
||||
|
||||
// Accessing MainModule requires admin privileges, this way does not
|
||||
ProgramLocation = WindowMonitor.GetProcessFilename(process);
|
||||
}
|
||||
|
||||
public string WindowTitle { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public string ProgramLocation { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Artemis.Plugins.Modules.General.DataModel.Windows
|
||||
{
|
||||
public class WindowsDataModel
|
||||
{
|
||||
public WindowDataModel ActiveWindow { get; set; }
|
||||
public List<WindowDataModel> OpenWindows { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Interop;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Plugins.Abstract;
|
||||
using Artemis.Core.Plugins.Abstract.ViewModels;
|
||||
using Artemis.Core.Plugins.Models;
|
||||
using Artemis.Plugins.Modules.General.DataModel;
|
||||
using Artemis.Plugins.Modules.General.DataModel.Windows;
|
||||
using Artemis.Plugins.Modules.General.Utilities;
|
||||
using Artemis.Plugins.Modules.General.ViewModels;
|
||||
using SkiaSharp;
|
||||
|
||||
@ -13,6 +21,7 @@ namespace Artemis.Plugins.Modules.General
|
||||
private readonly PluginSettings _settings;
|
||||
private readonly Random _rand;
|
||||
|
||||
|
||||
public GeneralModule(PluginSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
@ -26,12 +35,19 @@ namespace Artemis.Plugins.Modules.General
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
DataModel.UpdatesDividedByFour += 0.25;
|
||||
DataModel.Updates += 1;
|
||||
DataModel.PlayerInfo.Position = new SKPoint(_rand.Next(100), _rand.Next(100));
|
||||
DataModel.TestDataModel.UpdatesDividedByFour += 0.25;
|
||||
DataModel.TestDataModel.Updates += 1;
|
||||
DataModel.TestDataModel.PlayerInfo.Position = new SKPoint(_rand.Next(100), _rand.Next(100));
|
||||
DataModel.TestDataModel.PlayerInfo.Health++;
|
||||
if (DataModel.TestDataModel.PlayerInfo.Health > 200)
|
||||
DataModel.TestDataModel.PlayerInfo.Health = 0;
|
||||
|
||||
DataModel.TestDataModel.IntsList[0] = _rand.Next();
|
||||
DataModel.TestDataModel.IntsList[2] = _rand.Next();
|
||||
|
||||
UpdateCurrentWindow();
|
||||
UpdateBackgroundWindows();
|
||||
|
||||
DataModel.IntsList[0] = _rand.Next();
|
||||
DataModel.IntsList[2] = _rand.Next();
|
||||
base.Update(deltaTime);
|
||||
}
|
||||
|
||||
@ -41,8 +57,8 @@ namespace Artemis.Plugins.Modules.General
|
||||
DisplayIcon = "AllInclusive";
|
||||
ExpandsDataModel = true;
|
||||
|
||||
DataModel.IntsList = new List<int> {_rand.Next(), _rand.Next(), _rand.Next()};
|
||||
DataModel.PlayerInfosList = new List<PlayerInfo> {new PlayerInfo()};
|
||||
DataModel.TestDataModel.IntsList = new List<int> {_rand.Next(), _rand.Next(), _rand.Next()};
|
||||
DataModel.TestDataModel.PlayerInfosList = new List<PlayerInfo> {new PlayerInfo()};
|
||||
|
||||
var testSetting = _settings.GetSetting("TestSetting", DateTime.Now);
|
||||
}
|
||||
@ -50,5 +66,36 @@ namespace Artemis.Plugins.Modules.General
|
||||
public override void DisablePlugin()
|
||||
{
|
||||
}
|
||||
|
||||
#region Open windows
|
||||
|
||||
private DateTime _lastBackgroundWindowsUpdate;
|
||||
|
||||
public void UpdateCurrentWindow()
|
||||
{
|
||||
var processId = WindowMonitor.GetActiveProcessId();
|
||||
if (DataModel.Windows.ActiveWindow == null || DataModel.Windows.ActiveWindow.Process.Id != processId)
|
||||
DataModel.Windows.ActiveWindow = new WindowDataModel(Process.GetProcessById(processId));
|
||||
}
|
||||
|
||||
public void UpdateBackgroundWindows()
|
||||
{
|
||||
// This is kinda slow so lets not do it very often and lets do it in a task
|
||||
if (DateTime.Now - _lastBackgroundWindowsUpdate < TimeSpan.FromSeconds(5))
|
||||
return;
|
||||
|
||||
_lastBackgroundWindowsUpdate = DateTime.Now;
|
||||
Task.Run(() =>
|
||||
{
|
||||
// All processes with a main window handle are considered open windows
|
||||
DataModel.Windows.OpenWindows = Process.GetProcesses()
|
||||
.Where(p => p.MainWindowHandle != IntPtr.Zero)
|
||||
.Select(p => new WindowDataModel(p))
|
||||
.Where(w => !string.IsNullOrEmpty(w.WindowTitle))
|
||||
.ToList();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Artemis.Plugins.Modules.General.Utilities
|
||||
{
|
||||
public static class WindowMonitor
|
||||
{
|
||||
public static int GetActiveProcessId()
|
||||
{
|
||||
var hWnd = GetForegroundWindow(); // Get foreground window handle
|
||||
GetWindowThreadProcessId(hWnd, out var processId);
|
||||
return (int) processId;
|
||||
}
|
||||
|
||||
public static string GetProcessFilename(Process p)
|
||||
{
|
||||
var capacity = 2000;
|
||||
var builder = new StringBuilder(capacity);
|
||||
var ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
|
||||
if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity)) return string.Empty;
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
|
||||
|
||||
|
||||
[Flags]
|
||||
private enum ProcessAccessFlags : uint
|
||||
{
|
||||
QueryLimitedInformation = 0x00001000
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user