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

Added back ImGui MKB Input functions, and added new "Overlay Hotkey" category (Issue #957) #958

66 changes: 50 additions & 16 deletions src/VKBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,44 @@
#include "CET.h"
#include "Utils.h"

std::function<void()> VKBind::DelayedCall(const bool acIsDown) const
namespace
{
if (!acIsDown) // hotkeys only on key up
std::function<void()> VKBindDelayedCallVisitor(const TVKBindInputCallback& acpCallback, const bool acIsKeyDown)
{
if (const auto* fn = std::get_if<std::function<TVKBindHotkeyCallback>>(&Handler))
return *fn;
return [acpCallback, acIsKeyDown]
{
acpCallback(acIsKeyDown);
};
}

if (const auto* fn = std::get_if<std::function<TVKBindInputCallback>>(&Handler))
return [cb = *fn, acIsDown]
std::function<void()> VKBindDelayedCallVisitor(const TVKBindHotkeyCallback& acpCallback, const bool acIsKeyDown)
{
if (acIsKeyDown)
return {};

return [acpCallback]
{
cb(acIsDown);
acpCallback();
};
}
}

assert(acIsDown); // nullptr should ever return only for key down events, in case binding is a hotkey
return nullptr;
std::function<void()> VKBind::DelayedCall(const bool acIsKeyDown) const
{
const auto bindVisitor = [acIsKeyDown](const auto& acpCallback) -> std::function<void()>
{
if constexpr (std::same_as<std::decay_t<decltype(acpCallback)>, std::monostate>)
{
assert(false);
return {};
}
else
{
return VKBindDelayedCallVisitor(acpCallback, acIsKeyDown);
}
};

return std::visit(bindVisitor, Handler);
}

void VKBind::Call(const bool acIsDown) const
Expand All @@ -29,12 +51,22 @@ void VKBind::Call(const bool acIsDown) const

bool VKBind::IsHotkey() const
{
return std::holds_alternative<std::function<TVKBindHotkeyCallback>>(Handler);
return std::holds_alternative<TVKBindHotkeyCallback>(Handler);
}

bool VKBind::IsOverlayHotkey() const
{
return std::holds_alternative<TVKBindOverlayHotkeyCallback>(Handler);
}

bool VKBind::IsInput() const
{
return std::holds_alternative<std::function<TVKBindInputCallback>>(Handler);
return std::holds_alternative<TVKBindInputCallback>(Handler);
}

bool VKBind::IsValid() const
{
return !std::holds_alternative<std::monostate>(Handler);
}

bool VKBind::HasSimpleDescription() const
Expand Down Expand Up @@ -122,7 +154,7 @@ void VKBindings::InitializeMods(const TiltedPhoques::Map<std::string, std::refer
{
// test if bind is hotkey and if not, check if bind is valid for input (which means it has
// simple input key, not combo)
found = vkBind.IsHotkey() || (idToBind.second & 0xFFFF000000000000) == idToBind.second;
found = vkBind.IsHotkey() || vkBind.IsOverlayHotkey() || (idToBind.second & 0xFFFF000000000000) == idToBind.second;
break; // we just reset found flag accordingly and exit here, we found valid entry, no need to
// continue regardless of result
}
Expand Down Expand Up @@ -253,7 +285,7 @@ bool VKBindings::Bind(const uint64_t acVKCodeBind, const VKModBind& acVKModBind)
}

const auto vkBind = vm->GetBind(vkModBind);
return vkBind && vkBind->IsHotkey();
return vkBind && (vkBind->IsHotkey() || vkBind->IsOverlayHotkey());
};

if (!isHotkey(bind->second) || !isHotkey(acVKModBind))
Expand Down Expand Up @@ -641,11 +673,11 @@ void VKBindings::ExecuteSingleInput(const USHORT acVKCode, const bool acKeyDown)
// binding state! only exception allowed is any CET bind
const auto& overlayToggleModBind = Bindings::GetOverlayToggleModBind();
const auto cetModBind = modBind.ModName == overlayToggleModBind.ModName;
if (!cetModBind && (!m_cpVm->IsInitialized() || CET::Get().GetOverlay().IsEnabled()))
if (!cetModBind && (!m_cpVm->IsInitialized() || CET::Get().GetOverlay().IsEnabled()) || !vkBind->IsValid())
return;

// this handler is not for hotkeys!
if (vkBind->IsHotkey())
if (vkBind->IsHotkey() || vkBind->IsOverlayHotkey())
return;

// execute CET binds immediately, otherwise cursor will not show on overlay toggle
Expand Down Expand Up @@ -677,7 +709,9 @@ void VKBindings::ExecuteRecording()
// binding state! only exception allowed is any CET bind
const auto& overlayToggleModBind = Bindings::GetOverlayToggleModBind();
const auto cetModBind = modBind.ModName == overlayToggleModBind.ModName;
if (!cetModBind && (!m_cpVm->IsInitialized() || CET::Get().GetOverlay().IsEnabled()))

// TODO: Crash when using a OverlayHotkey while the overlay is not active
if (!cetModBind && (!m_cpVm->IsInitialized() || CET::Get().GetOverlay().IsEnabled() != vkBind->IsOverlayHotkey()) || !vkBind->IsValid())
return;

// this handler is not for inputs! it should be used only on key up event for hotkeys!
Expand Down
58 changes: 55 additions & 3 deletions src/VKBindings.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,57 @@
#pragma once

using TVKBindHotkeyCallback = void();
using TVKBindInputCallback = void(bool);
enum class TVKBindType : int8_t
{
Invalid = -1,
Input = 0,
Hotkey = 1,
OverlayHotkey = 2
};

class TVKBindInputCallback
{
public:
TVKBindInputCallback(std::function<void(bool)> functor)
: m_functor(std::move(functor))
{
}

void operator()(bool isKeyDown) const
{
if (m_functor)
m_functor(isKeyDown);
}

private:
std::function<void(bool)> m_functor;
};

class TVKBindHotkeyCallback
{
public:
TVKBindHotkeyCallback(std::function<void()> functor)
: m_functor(std::move(functor))
{
}
virtual ~TVKBindHotkeyCallback() = default;

void operator()() const
{
if (m_functor)
m_functor();
}

private:
std::function<void()> m_functor;
};

class TVKBindOverlayHotkeyCallback final : public TVKBindHotkeyCallback
{
public:
using TVKBindHotkeyCallback::TVKBindHotkeyCallback;
};

using TVKBindHandler = std::variant<std::monostate, TVKBindInputCallback, TVKBindHotkeyCallback, TVKBindOverlayHotkeyCallback>;

using VKCodeBindDecoded = std::array<uint16_t, 4>;

Expand All @@ -18,13 +68,15 @@ struct VKBind
std::string ID{};
std::string DisplayName{};
std::variant<std::string, std::function<void()>> Description{};
std::variant<std::function<TVKBindHotkeyCallback>, std::function<TVKBindInputCallback>> Handler{};
TVKBindHandler Handler{};

[[nodiscard]] std::function<void()> DelayedCall(const bool acIsDown) const;
void Call(const bool acIsDown) const;

[[nodiscard]] bool IsHotkey() const;
[[nodiscard]] bool IsOverlayHotkey() const;
[[nodiscard]] bool IsInput() const;
[[nodiscard]] bool IsValid() const;

[[nodiscard]] bool HasSimpleDescription() const;
[[nodiscard]] bool HasComplexDescription() const;
Expand Down
78 changes: 52 additions & 26 deletions src/overlay/widgets/Bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ namespace
{
VKBind s_overlayToggleBind{
"overlay_key", "Overlay Key", "Use this hotkey to toggle overlay on and off.",
[]
TVKBindHotkeyCallback([]
{
if (!CET::Get().GetBindings().IsRecordingBind())
CET::Get().GetOverlay().Toggle();
}};
})};
VKBindInfo s_overlayToggleBindInfo{s_overlayToggleBind, 0, 0, false};
VKModBind s_overlayToggleModBind{"cet", s_overlayToggleBind.ID};
} // namespace
Expand Down Expand Up @@ -234,24 +234,27 @@ void Bindings::Initialize()

// emplace CET internal settings
{
auto& [vkBindInfos, hotkeyCount] = m_vkBindInfos[s_overlayToggleModBind.ModName];
auto& [vkBindInfos, modBindCounts] = m_vkBindInfos[s_overlayToggleModBind.ModName];
vkBindInfos.emplace_back(s_overlayToggleBindInfo);
hotkeyCount = 1;
modBindCounts.hotkeyCount = 1;
}

// emplace mod bindings
for (const auto& modBindsIt : allModsBinds)
{
auto& [vkBindInfos, hotkeyCount] = m_vkBindInfos[modBindsIt.first];
hotkeyCount = 0;
auto& [vkBindInfos, modBindCounts] = m_vkBindInfos[modBindsIt.first];
for (const auto& vkBind : modBindsIt.second.get())
{
auto& vkBindInfo = vkBindInfos.emplace_back(vkBind);
vkBindInfo.SavedCodeBind = m_bindings.GetBindCodeForModBind({modBindsIt.first, vkBind.ID});
vkBindInfo.CodeBind = vkBindInfo.SavedCodeBind;

if (vkBind.IsHotkey())
++hotkeyCount;
++modBindCounts.hotkeyCount;
if (vkBind.IsOverlayHotkey())
++modBindCounts.overlayHotkeyCount;
if (vkBind.IsInput())
++modBindCounts.inputCount;
}
}

Expand Down Expand Up @@ -284,7 +287,7 @@ void Bindings::UpdateAndDrawBinding(const VKModBind& acModBind, VKBindInfo& aVKB
const auto checkModBind = [this, &aVKBindInfo, codeBind](const VKModBind modBind)
{
const auto cetBind = modBind == s_overlayToggleModBind;
if (!cetBind || aVKBindInfo.Bind.IsHotkey())
if (!cetBind || aVKBindInfo.Bind.IsHotkey() || aVKBindInfo.Bind.IsOverlayHotkey())
{
const auto& modName = modBind.ModName;
auto& [bindInfos, _] = m_vkBindInfos[modName];
Expand All @@ -301,7 +304,7 @@ void Bindings::UpdateAndDrawBinding(const VKModBind& acModBind, VKBindInfo& aVKB
bindIt->CodeBind = 0;
}

if (aVKBindInfo.Bind.IsInput() && bindIt->Bind.IsHotkey() && m_bindings.IsFirstKeyUsed(codeBind))
if (aVKBindInfo.Bind.IsInput() && (bindIt->Bind.IsHotkey() || bindIt->Bind.IsOverlayHotkey()) && m_bindings.IsFirstKeyUsed(codeBind))
{
bindIt->CodeBind = bindItCodeBind;
m_bindings.Bind(bindIt->CodeBind, modBind);
Expand Down Expand Up @@ -433,7 +436,7 @@ void Bindings::UpdateAndDrawBinding(const VKModBind& acModBind, VKBindInfo& aVKB
m_madeChanges |= aVKBindInfo.IsBinding || aVKBindInfo.CodeBind != aVKBindInfo.SavedCodeBind;
}

void Bindings::UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoques::Vector<VKBindInfo>& aVKBindInfos, size_t aHotkeyCount, bool aSimplified)
void Bindings::UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoques::Vector<VKBindInfo>& aVKBindInfos, const ModBindCount acBindCount, bool aSimplified)
{
if (aVKBindInfos.empty())
return;
Expand Down Expand Up @@ -467,31 +470,54 @@ void Bindings::UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoq

ImGui::TreePush();

if (aHotkeyCount > 0)
if (acBindCount.hotkeyCount > 0 || acBindCount.overlayHotkeyCount > 0)
{
if (!aSimplified)
{
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + GetCenteredOffsetForText("Hotkeys"));
ImGui::TextUnformatted("Hotkeys");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
ImGui::SetTooltip("Hotkeys react after assigned key combination has been pressed and subsequently "
"released. You can bind up to 4 key combination to them.");
ImGui::Separator();
}

if (ImGui::BeginTable(("##HOTKEYS_" + activeModName).c_str(), 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchSame, ImVec2(-ImGui::GetStyle().IndentSpacing, 0)))
{
for (auto& binding : aVKBindInfos)
if (acBindCount.hotkeyCount > 0)
{
if (binding.Bind.IsHotkey())
UpdateAndDrawBinding({acModName, binding.Bind.ID}, binding);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + GetCenteredOffsetForText("Hotkeys"));
ImGui::TextUnformatted("Hotkeys");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
ImGui::SetTooltip("Hotkeys react after assigned key combination has been pressed and subsequently "
"released. You can bind up to 4 key combination to them.");
ImGui::Separator();

if (ImGui::BeginTable(("##HOTKEYS_" + activeModName).c_str(), 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchSame, ImVec2(-ImGui::GetStyle().IndentSpacing, 0)))
{
for (auto& binding : aVKBindInfos)
{
if (binding.Bind.IsHotkey())
UpdateAndDrawBinding({acModName, binding.Bind.ID}, binding);
}

ImGui::EndTable();
}
}
if (acBindCount.overlayHotkeyCount > 0)
{
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + GetCenteredOffsetForText("Overlay Hotkeys"));
ImGui::TextUnformatted("Overlay Hotkeys");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
ImGui::SetTooltip("Overlay Hotkeys react after assigned key combination has been pressed and subsequently "
"released. You can bind up to 4 key combination to them.\n Unlike regular Hotkeys, these only work while the CET Overlay is open");
ImGui::Separator();

if (ImGui::BeginTable(("##OVERLAY_HOTKEYS_" + activeModName).c_str(), 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchSame, ImVec2(-ImGui::GetStyle().IndentSpacing, 0)))
{
for (auto& binding : aVKBindInfos)
{
if (binding.Bind.IsOverlayHotkey())
UpdateAndDrawBinding({acModName, binding.Bind.ID}, binding);
}

ImGui::EndTable();
ImGui::EndTable();
}
}
}
}

if (aHotkeyCount < aVKBindInfos.size())
if (acBindCount.inputCount > 0)
{
if (!aSimplified)
{
Expand Down
11 changes: 9 additions & 2 deletions src/overlay/widgets/Bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ struct VKBindInfo
bool operator==(const std::string& id) const;
};

struct ModBindCount
{
size_t hotkeyCount{0};
size_t overlayHotkeyCount{0};
size_t inputCount{0};
};

struct LuaVM;
struct Bindings : Widget
{
Expand All @@ -37,9 +44,9 @@ struct Bindings : Widget
private:
void Initialize();
void UpdateAndDrawBinding(const VKModBind& acModBind, VKBindInfo& aVKBindInfo);
void UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoques::Vector<VKBindInfo>& aVKBindInfos, size_t aHotkeyCount, bool aSimplified = false);
void UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoques::Vector<VKBindInfo>& aVKBindInfos, ModBindCount acBindCount, bool aSimplified = false);

TiltedPhoques::Map<std::string, std::pair<TiltedPhoques::Vector<VKBindInfo>, size_t>> m_vkBindInfos{};
TiltedPhoques::Map<std::string, std::pair<TiltedPhoques::Vector<VKBindInfo>, ModBindCount>> m_vkBindInfos{};
VKBindings& m_bindings;
LuaVM& m_vm;

Expand Down
Loading