Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Mouse Button Support #974

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Blish HUD.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="Logge" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="Log" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PublicFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="Log" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="Logge" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
Expand Down Expand Up @@ -325,5 +331,6 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Blish/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Menomonia/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
7 changes: 4 additions & 3 deletions Blish HUD/Controls/KeybindingAssigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ public override void RecalculateLayout() {
}

private void SetupNewAssignmentWindow() {
var newHkAssign = new KeybindingAssignmentWindow(_text, _keyBinding.ModifierKeys, _keyBinding.PrimaryKey) {
var newHkAssign = new KeybindingAssignmentWindow(_text, _keyBinding.ModifierKeys, _keyBinding.PrimaryKey, _keyBinding.PrimaryMouseButton) {
Parent = Graphics.SpriteScreen
};

newHkAssign.AssignmentAccepted += delegate {
_keyBinding.ModifierKeys = newHkAssign.ModifierKeys;
_keyBinding.PrimaryKey = newHkAssign.PrimaryKey;
_keyBinding.ModifierKeys = newHkAssign.ModifierKeys;
_keyBinding.PrimaryKey = newHkAssign.PrimaryKey;
_keyBinding.PrimaryMouseButton = newHkAssign.PrimaryMouseButton;

OnBindingChanged(EventArgs.Empty);
};
Expand Down
76 changes: 65 additions & 11 deletions Blish HUD/Controls/KeybindingAssignmentWindow.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using Blish_HUD.Input;
using Blish_HUD.Input.Mouse;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
Expand Down Expand Up @@ -48,6 +49,7 @@ private void OnAssignmentCanceled(EventArgs e) {

private ModifierKeys _modifierKeys;
private Keys _primaryKey;
private MouseButtons _primaryMouseButton;

/// <summary>
/// The current modifier key(s) assignment.
Expand All @@ -62,17 +64,38 @@ public ModifierKeys ModifierKeys {
/// </summary>
public Keys PrimaryKey {
get => _primaryKey;
private set => SetProperty(ref _primaryKey, value, true);
private set {
if (SetProperty(ref _primaryKey, value, true)) {
_primaryMouseButton = MouseButtons.None;
}
}
}

/// <summary>
/// The current primary mouse button assignment.
/// </summary>
public MouseButtons PrimaryMouseButton {
get => _primaryMouseButton;
private set {
if (SetProperty(ref _primaryMouseButton, value, true)) {
_primaryKey = Keys.None;
}
}
}

private readonly string _assignmentName;

private string _assignmentDisplayString;

public KeybindingAssignmentWindow(string assignmentName, ModifierKeys modifierKeys = ModifierKeys.None, Keys primaryKey = Keys.None) {
_assignmentName = assignmentName;
_modifierKeys = modifierKeys;
_primaryKey = primaryKey;
public KeybindingAssignmentWindow(string assignmentName, ModifierKeys modifierKeys = ModifierKeys.None, Keys primaryKey = Keys.None) : this(assignmentName, modifierKeys, primaryKey, MouseButtons.None) {
/* NOOP */
}

public KeybindingAssignmentWindow(string assignmentName, ModifierKeys modifierKeys = ModifierKeys.None, Keys primaryKey = Keys.None, MouseButtons primaryMouseButton = MouseButtons.None) {
_assignmentName = assignmentName;
_modifierKeys = modifierKeys;
_primaryKey = primaryKey;
_primaryMouseButton = primaryMouseButton;

this.BackgroundColor = Color.Black * 0.3f;
this.Size = new Point(_normalizedWindowRegion.Width, _normalizedWindowRegion.Height);
Expand All @@ -81,7 +104,10 @@ public KeybindingAssignmentWindow(string assignmentName, ModifierKeys modifierKe

BuildChildElements();

Input.Keyboard.KeyStateChanged += KeyboardOnKeyStateChanged;
Input.Keyboard.KeyStateChanged += KeyboardOnKeyStateChanged;
Input.Mouse.XButton1Pressed += MouseButtonPressed;
Input.Mouse.XButton2Pressed += MouseButtonPressed;
Input.Mouse.MiddleMouseButtonPressed += MouseButtonPressed;
}

protected override void OnShown(EventArgs e) {
Expand All @@ -95,6 +121,10 @@ protected override void OnShown(EventArgs e) {
private void BlockGameInput(string input) { /* NOOP */ }

private void KeyboardOnKeyStateChanged(object sender, KeyboardEventArgs e) {
if (!this.Visible) {
return;
}

if (e.Key == Keys.Escape) {
if (e.EventType == KeyboardEventType.KeyUp) {
OnAssignmentCanceled(EventArgs.Empty);
Expand All @@ -108,6 +138,26 @@ private void KeyboardOnKeyStateChanged(object sender, KeyboardEventArgs e) {
}
}

private void MouseButtonPressed(object sender, MouseEventArgs e) {
if (!this.Visible) {
return;
}

if (e.EventType == MouseEventType.XButton1Pressed ||
e.EventType == MouseEventType.XButton2Pressed ||
e.EventType == MouseEventType.MiddleMouseButtonPressed) {
this.PrimaryKey = Keys.None;
this.ModifierKeys = KeysUtil.ModifiersFromKeys(Input.Keyboard.KeysDown);

this.PrimaryMouseButton = e.EventType switch {
MouseEventType.XButton1Pressed => MouseButtons.XButton1,
MouseEventType.XButton2Pressed => MouseButtons.XButton2,
MouseEventType.MiddleMouseButtonPressed => MouseButtons.MiddleButton,
_ => this.PrimaryMouseButton
};
}
}

private void FinishAssignment() {
GameService.Input.Keyboard.UnsetTextInputListner(BlockGameInput);

Expand Down Expand Up @@ -153,8 +203,9 @@ private void BuildChildElements() {
_acceptBttn.Location = new Point(_cancelBttn.Left - 8 - _acceptBttn.Width, _cancelBttn.Top);

_unbindBttn.Click += delegate {
this.ModifierKeys = ModifierKeys.None;
this.PrimaryKey = Keys.None;
this.ModifierKeys = ModifierKeys.None;
this.PrimaryKey = Keys.None;
this.PrimaryMouseButton = MouseButtons.None;
};

_cancelBttn.Click += delegate {
Expand Down Expand Up @@ -186,10 +237,10 @@ public override void RecalculateLayout() {
this.ContentRegion = _windowRegion;
}

_assignmentDisplayString = KeysUtil.GetFriendlyName(_modifierKeys, _primaryKey);
_assignmentDisplayString = KeysUtil.GetFriendlyName(_modifierKeys, _primaryKey, _primaryMouseButton);

if (_unbindBttn != null) {
_unbindBttn.Enabled = this.PrimaryKey != Keys.None;
_unbindBttn.Enabled = this.PrimaryKey != Keys.None || this.PrimaryMouseButton != MouseButtons.None;
}
}

Expand All @@ -205,7 +256,10 @@ protected override void DisposeControl() {

GameService.Input.Keyboard.UnsetTextInputListner(BlockGameInput);

Input.Keyboard.KeyStateChanged -= KeyboardOnKeyStateChanged;
Input.Keyboard.KeyStateChanged -= KeyboardOnKeyStateChanged;
Input.Mouse.XButton1Pressed -= MouseButtonPressed;
Input.Mouse.XButton2Pressed -= MouseButtonPressed;
Input.Mouse.MiddleMouseButtonPressed -= MouseButtonPressed;
}

// We implement IWindow to avoid other windows from reacting to our ESC input
Expand Down
75 changes: 70 additions & 5 deletions Blish HUD/GameServices/Input/Keyboard/KeyBinding.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Microsoft.Xna.Framework.Input;
using Blish_HUD.Input.Mouse;

namespace Blish_HUD.Input {

Expand Down Expand Up @@ -30,6 +31,7 @@ protected void OnActivated(EventArgs e) {
private Keys _primaryKey;
/// <summary>
/// The primary key in the binding.
/// Mutually exclusive with <see cref="PrimaryMouseButton"/>.
/// </summary>
[JsonProperty]
public Keys PrimaryKey {
Expand All @@ -38,11 +40,38 @@ public Keys PrimaryKey {
if (_primaryKey == value) {
return;
}

if (value != Keys.None) {
_primaryMouseButton = MouseButtons.None;
}

_primaryKey = value;
BindingChanged?.Invoke(this, EventArgs.Empty);
}
}

private MouseButtons _primaryMouseButton;
/// <summary>
/// The primary mouse button in the binding.
/// Mutually exclusive with <see cref="PrimaryKey"/>.
/// </summary>
[JsonProperty]
public MouseButtons PrimaryMouseButton {
get => _primaryMouseButton;
set {
if (_primaryMouseButton == value) {
return;
}

if (value != MouseButtons.None) {
_primaryKey = Keys.None;
}

_primaryMouseButton = value;
this.BindingChanged?.Invoke(this, EventArgs.Empty);
}
}

private ModifierKeys _modifierKeys;
/// <summary>
/// Any combination of <see cref="ModifierKeys"/> required to be pressed
Expand All @@ -55,6 +84,7 @@ public ModifierKeys ModifierKeys {
if (_modifierKeys == value) {
return;
}

_modifierKeys = value;
BindingChanged?.Invoke(this, EventArgs.Empty);
}
Expand All @@ -74,8 +104,23 @@ public bool Enabled {
if (value) {
KeyboardOnKeyStateChanged(null, null);
GameService.Input.Keyboard.KeyStateChanged += KeyboardOnKeyStateChanged;

GameService.Input.Mouse.MiddleMouseButtonPressed += MouseButtonStateChanged;
GameService.Input.Mouse.XButton1Pressed += MouseButtonStateChanged;
GameService.Input.Mouse.XButton2Pressed += MouseButtonStateChanged;
GameService.Input.Mouse.MiddleMouseButtonReleased += MouseButtonStateChanged;
GameService.Input.Mouse.XButton1Released += MouseButtonStateChanged;
GameService.Input.Mouse.XButton2Released += MouseButtonStateChanged;
} else {
GameService.Input.Keyboard.KeyStateChanged -= KeyboardOnKeyStateChanged;

GameService.Input.Mouse.MiddleMouseButtonPressed -= MouseButtonStateChanged;
GameService.Input.Mouse.XButton1Pressed -= MouseButtonStateChanged;
GameService.Input.Mouse.XButton2Pressed -= MouseButtonStateChanged;
GameService.Input.Mouse.MiddleMouseButtonReleased -= MouseButtonStateChanged;
GameService.Input.Mouse.XButton1Released -= MouseButtonStateChanged;
GameService.Input.Mouse.XButton2Released -= MouseButtonStateChanged;

GameService.Input.Keyboard.UnstageKeyBinding(this);
}

Expand All @@ -86,6 +131,10 @@ public bool Enabled {
}
}

private void MouseButtonStateChanged(object sender, MouseEventArgs e) {
KeyboardOnKeyStateChanged(null, null);
}

/// <summary>
/// If <c>true</c>, the <see cref="PrimaryKey"/> is not sent to the game when it is
/// the final key pressed in the keybinding sequence.
Expand All @@ -110,14 +159,21 @@ public KeyBinding() { /* NOOP */ }

public KeyBinding(Keys primaryKey) : this(ModifierKeys.None, primaryKey) { /* NOOP */ }

public KeyBinding(MouseButtons primaryMouseButon) : this(ModifierKeys.None, primaryMouseButon) { /* NOOP */ }

public KeyBinding(ModifierKeys modifierKeys, Keys primaryKey) {
_modifierKeys = modifierKeys;
_primaryKey = primaryKey;
}

public KeyBinding(ModifierKeys modifierKeys, MouseButtons primaryMouseButton) {
_modifierKeys = modifierKeys;
_primaryMouseButton = primaryMouseButton;
}

private void KeyboardOnKeyStateChanged(object sender, KeyboardEventArgs e) {
if (this.PrimaryKey == Keys.None
|| (this.IgnoreWhenInTextField && GameService.Input.Keyboard.TextFieldIsActive())) return;
if ((this.PrimaryKey == Keys.None && this.PrimaryMouseButton == MouseButtons.None)
|| (this.IgnoreWhenInTextField && GameService.Input.Keyboard.TextFieldIsActive())) return;

CheckTrigger(GameService.Input.Keyboard.ActiveModifiers, GameService.Input.Keyboard.KeysDown);
}
Expand All @@ -138,13 +194,22 @@ private void StopFiring() {
this.IsTriggering = false;
}

private bool MouseButtonActive() {
return _primaryMouseButton switch {
MouseButtons.MiddleButton => GameService.Input.Mouse.State.MiddleButton == ButtonState.Pressed,
MouseButtons.XButton1 => GameService.Input.Mouse.State.XButton1 == ButtonState.Pressed,
MouseButtons.XButton2 => GameService.Input.Mouse.State.XButton2 == ButtonState.Pressed,
_ => false
};
}

private void CheckTrigger(ModifierKeys activeModifiers, IEnumerable<Keys> pressedKeys) {
if (activeModifiers == this.ModifierKeys || _acceptedPrimaryModifierKeys.Contains(_primaryKey)) {
if (this.BlockSequenceFromGw2) {
GameService.Input.Keyboard.StageKeyBinding(this);
}

if (pressedKeys.Contains(this.PrimaryKey)) {
if (pressedKeys.Contains(this.PrimaryKey) || MouseButtonActive()) {
Fire();
return;
}
Expand All @@ -159,7 +224,7 @@ private void CheckTrigger(ModifierKeys activeModifiers, IEnumerable<Keys> presse
/// Gets a display string representing the <see cref="KeyBinding"/> suitable
/// for display in the UI.
/// </summary>
public string GetBindingDisplayText() => KeysUtil.GetFriendlyName(this.ModifierKeys, this.PrimaryKey);
public string GetBindingDisplayText() => KeysUtil.GetFriendlyName(this.ModifierKeys, this.PrimaryKey, this.PrimaryMouseButton);

/// <summary>
/// Manually triggers the actions bound to this <see cref="KeyBinding"/>.
Expand Down
Loading
Loading