Skip to content

Commit

Permalink
Add XInput in preparation for gamepad triggers + add xmldoc (#50)
Browse files Browse the repository at this point in the history
* Add XInput in preparation for gamepad triggers + add xmldoc

* add parent-child relation between mapped options

* add remove child mappings option + remove debug context menu

* x86 > AnyCPU (gives more sensible errors in WinForms Designer) - Fixed Designer failing on MappingForm

* Fix polling gamepad

* Add gamepad input trigger and config control + Fix mapping list not updating correctly

* remove broken and quite useless test

* update readme with warning regarding #46 + update readme special thanks

* add warning on same gamepad id trigger and action

* attempt to give github actions more time before timeout (tests sometimes fail)

* use current default mapping profile from app (so we dont have to copy it to tests everytime manually)

* Add gamepad button trigger

* Add multi-property edit mode

* Show physical gamepad connection warning

* give even more time for tests so they dont fail

* Block arming mappings if simulated gamepad index collides with physical gamepad

* Cleanup + add GamePad Trigger Trigger

* Fix combined trigger remove not working

* Separate stick action and allow custom scaling

* improve stick feeling

* fix parent picker

* update default mappings

* add trigger and try work out conflicts between physical and simulated devices (no luck yet)

* Fix simulated gamepad recognized as physical

* Show gamepad devices in UI

* fix mapping form + add more multi-edit type support

* make reverse mapping tool more useful

* easily setup/update reverse mappings while creating/updating a mapping

* update readme screenshots + prefer 'Arm' terminology for enabling profile

* support more nullable types in mapping profile

* simplify groups

* add configurable grouping

* prevent wonky sorting across groups

* Add easy switch group option

* Fix being able to create corrupt profile

* fail loading incorrect profile safely
  • Loading branch information
luttje authored Oct 22, 2023
1 parent 14b7ce9 commit 6b38fe9
Show file tree
Hide file tree
Showing 191 changed files with 18,460 additions and 9,768 deletions.
4 changes: 2 additions & 2 deletions Core/Key2Joy.Contracts/Key2Joy.Contracts.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Authors>luttje</Authors>
Expand All @@ -14,7 +14,7 @@
<TargetFramework>NET48</TargetFramework>
<LangVersion>latest</LangVersion>
<RootNamespace>Key2Joy.Contracts</RootNamespace>
<PlatformTarget>x86</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
<SignAssembly>False</SignAssembly>
<AssemblyOriginatorKeyFile></AssemblyOriginatorKeyFile>
<BaseOutputPath>bin\$(MSBuildProjectName)</BaseOutputPath>
Expand Down
52 changes: 36 additions & 16 deletions Core/Key2Joy.Contracts/Mapping/AbstractMappedOption.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
using System;
using System.Text.Json.Serialization;
using Key2Joy.Contracts.Mapping.Actions;
using Key2Joy.Contracts.Mapping.Triggers;

namespace Key2Joy.Contracts.Mapping;

public abstract class AbstractMappedOption : ICloneable
{
[JsonInclude]
public AbstractAction Action { get; set; }
[JsonInclude]
public AbstractTrigger Trigger { get; set; }

public abstract object Clone();
}
using System;
using System.Text.Json.Serialization;
using Key2Joy.Contracts.Mapping.Actions;
using Key2Joy.Contracts.Mapping.Triggers;

namespace Key2Joy.Contracts.Mapping;

public abstract class AbstractMappedOption : ICloneable
{
/// <summary>
/// The unique identifier for this mapping.
/// This way it can be referenced by other mappings.
/// </summary>
[JsonInclude]
public Guid Guid { get; protected set; }

/// <summary>
/// The action that is executed when this mapping is executed.
/// </summary>
[JsonInclude]
public AbstractAction Action { get; set; }

/// <summary>
/// The trigger that causes this mapping to be executed.
/// </summary>
[JsonInclude]
public AbstractTrigger Trigger { get; set; }

/// <summary>
/// The unique identifier of the parent mapped option.
/// </summary>
[JsonInclude]
public Guid? ParentGuid { get; set; } = null;

public abstract object Clone();
}
298 changes: 156 additions & 142 deletions Core/Key2Joy.Contracts/Mapping/AbstractMappingAspect.cs
Original file line number Diff line number Diff line change
@@ -1,142 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;

namespace Key2Joy.Contracts.Mapping;

public abstract class AbstractMappingAspect : MarshalByRefObject, ICloneable, IComparable<AbstractMappingAspect>
{
public string Name { get; set; }

public AbstractMappingAspect(string name) => this.Name = name;

private PropertyInfo[] GetProperties()
{
var type = this.GetType();
var properties = type.GetProperties();

return properties.Where(p => p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length == 0).ToArray();
}

public virtual MappingAspectOptions SaveOptions()
{
MappingAspectOptions options = new();
var properties = this.GetProperties();

foreach (var property in properties)
{
this.SaveOptionsGetProperty(options, property);
}

return options;
}

/// <summary>
/// Can be overridden by child actions or triggers to allow saving more complex types.
/// </summary>
/// <param name="property"></param>
/// <param name="value"></param>
protected virtual void SaveOptionsGetProperty(MappingAspectOptions options, PropertyInfo property)
{
var value = property.GetValue(this);

switch (value)
{
case DateTime dateTime:
value = dateTime.Ticks;
break;

default:
break;
}

options.Add(property.Name, value);
}

public virtual void LoadOptions(MappingAspectOptions options)
{
var properties = this.GetProperties();

foreach (var property in properties)
{
if (!options.ContainsKey(property.Name))
{
continue;
}

this.LoadOptionSetProperty(options, property);
}
}

/// <summary>
/// Can be overridden by child actions or triggers to allow loading more complex types.
/// </summary>
/// <param name="property"></param>
/// <param name="value"></param>
protected virtual void LoadOptionSetProperty(MappingAspectOptions options, PropertyInfo property)
{
var propertyType = property.PropertyType;
var value = options[property.Name];
var genericTypeDefinition = propertyType.IsGenericType ? propertyType.GetGenericTypeDefinition() : null;

if (propertyType.IsEnum)
{
value = Enum.Parse(propertyType, (string)value);
}
else if (propertyType == typeof(DateTime))
{
value = new DateTime(Convert.ToInt64(value));
}
else if (propertyType.IsGenericType
&& (genericTypeDefinition == typeof(List<>) || genericTypeDefinition == typeof(IList<>)))
{
var constructedListType = typeof(List<>).MakeGenericType(propertyType.GetGenericArguments());
var instance = Activator.CreateInstance(constructedListType);

if (value is List<object> list)
{
var addMethod = constructedListType.GetMethod("Add");

foreach (var item in list)
{
addMethod.Invoke(instance, new object[] { item });
}

value = instance;
}
else
{
throw new ArgumentException($"Expected value to be of type List<> to parse. But was: {value.GetType()}");
}
}
else
{
value = Convert.ChangeType(value, propertyType);
}

property.SetValue(this, value);
}

public virtual int CompareTo(AbstractMappingAspect other) => this.ToString().CompareTo(other.ToString());

public static bool operator ==(AbstractMappingAspect a, AbstractMappingAspect b)
{
if (ReferenceEquals(a, b))
{
return true;
}

if (a is null || b is null)
{
return false;
}

return a.Equals(b);
}

public static bool operator !=(AbstractMappingAspect a, AbstractMappingAspect b) => !(a == b);

public virtual object Clone() => this.MemberwiseClone();
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;

namespace Key2Joy.Contracts.Mapping;

public abstract class AbstractMappingAspect : MarshalByRefObject, ICloneable, IComparable<AbstractMappingAspect>
{
public string Name { get; set; }

public AbstractMappingAspect(string name) => this.Name = name;

public virtual string GetNameDisplay() => this.Name;

public override string ToString() => this.GetNameDisplay();

private PropertyInfo[] GetProperties()
{
var type = this.GetType();
var properties = type.GetProperties();

return properties.Where(p => p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length == 0).ToArray();
}

public virtual MappingAspectOptions SaveOptions()
{
MappingAspectOptions options = new();
var properties = this.GetProperties();

foreach (var property in properties)
{
this.SaveOptionsGetProperty(options, property);
}

return options;
}

/// <summary>
/// Can be overridden by child actions or triggers to allow saving more complex types.
/// </summary>
/// <param name="property"></param>
/// <param name="value"></param>
protected virtual void SaveOptionsGetProperty(MappingAspectOptions options, PropertyInfo property)
{
var value = property.GetValue(this);

switch (value)
{
case DateTime dateTime:
value = dateTime.Ticks;
break;

default:
break;
}

options.Add(property.Name, value);
}

public virtual void LoadOptions(MappingAspectOptions options)
{
var properties = this.GetProperties();

foreach (var property in properties)
{
if (!options.ContainsKey(property.Name))
{
continue;
}

this.LoadOptionSetProperty(options, property);
}
}

/// <summary>
/// Can be overridden by child actions or triggers to allow loading more complex types.
/// </summary>
/// <param name="property"></param>
/// <param name="value"></param>
protected virtual void LoadOptionSetProperty(MappingAspectOptions options, PropertyInfo property)
{
var propertyType = property.PropertyType;
var value = options[property.Name];
var genericTypeDefinition = propertyType.IsGenericType ? propertyType.GetGenericTypeDefinition() : null;

propertyType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;

if (propertyType.IsEnum)
{
value = Enum.Parse(propertyType, (string)value);
}
else if (propertyType == typeof(DateTime))
{
value = new DateTime(Convert.ToInt64(value));
}
else if (propertyType == typeof(TimeSpan))
{
value = TimeSpan.Parse((string)value);
}
else if (propertyType == typeof(short))
{
value = Convert.ToInt16(value);
}
else if (propertyType.IsGenericType
&& (genericTypeDefinition == typeof(List<>) || genericTypeDefinition == typeof(IList<>)))
{
var constructedListType = typeof(List<>).MakeGenericType(propertyType.GetGenericArguments());
var instance = Activator.CreateInstance(constructedListType);

if (value is List<object> list)
{
var addMethod = constructedListType.GetMethod("Add");

foreach (var item in list)
{
addMethod.Invoke(instance, new object[] { item });
}

value = instance;
}
else
{
throw new ArgumentException($"Expected value to be of type List<> to parse. But was: {value.GetType()}");
}
}
else if (value != null)
{
value = Convert.ChangeType(value, propertyType);
}

property.SetValue(this, value);
}

public virtual int CompareTo(AbstractMappingAspect other) => this.ToString().CompareTo(other.ToString());

public static bool operator ==(AbstractMappingAspect a, AbstractMappingAspect b)
{
if (ReferenceEquals(a, b))
{
return true;
}

if (a is null || b is null)
{
return false;
}

return a.Equals(b);
}

public static bool operator !=(AbstractMappingAspect a, AbstractMappingAspect b) => !(a == b);

public virtual object Clone() => this.MemberwiseClone();
}
3 changes: 0 additions & 3 deletions Core/Key2Joy.Contracts/Mapping/Actions/AbstractAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Key2Joy.Contracts.Mapping.Triggers;
using Key2Joy.Contracts.Util;

namespace Key2Joy.Contracts.Mapping.Actions;

Expand All @@ -25,8 +24,6 @@ public abstract class AbstractAction : AbstractMappingAspect
/// <exception cref="System.NotImplementedException"></exception>
public virtual Task Execute(AbstractInputBag inputBag = null) => throw new System.NotImplementedException();

public virtual string GetNameDisplay() => this.Name;

public AbstractAction(string name)
: base(name) { }

Expand Down
Loading

0 comments on commit 6b38fe9

Please sign in to comment.