10 changed files with 323 additions and 303 deletions
@ -1,133 +0,0 @@
|
||||
#include "keymapper.hpp" |
||||
|
||||
#include <cassert> |
||||
#include <fmt/format.h> |
||||
#include <utility> |
||||
|
||||
#include <SDL.h> |
||||
|
||||
#ifdef USE_SDL1 |
||||
#include "utils/sdl2_to_1_2_backports.h" |
||||
#endif |
||||
|
||||
#include "control.h" |
||||
#include "options.h" |
||||
#include "utils/log.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
Keymapper::Keymapper() |
||||
{ |
||||
// Insert all supported keys: a-z, 0-9 and F1-F12.
|
||||
keyIDToKeyName.reserve(('Z' - 'A' + 1) + ('9' - '0' + 1) + 12); |
||||
for (char c = 'A'; c <= 'Z'; ++c) { |
||||
keyIDToKeyName.emplace(c, std::string(1, c)); |
||||
} |
||||
for (char c = '0'; c <= '9'; ++c) { |
||||
keyIDToKeyName.emplace(c, std::string(1, c)); |
||||
} |
||||
for (int i = 0; i < 12; ++i) { |
||||
keyIDToKeyName.emplace(DVL_VK_F1 + i, fmt::format("F{}", i + 1)); |
||||
} |
||||
|
||||
keyNameToKeyID.reserve(keyIDToKeyName.size()); |
||||
for (const auto &kv : keyIDToKeyName) { |
||||
keyNameToKeyID.emplace(kv.second, kv.first); |
||||
} |
||||
} |
||||
|
||||
Keymapper::ActionIndex Keymapper::AddAction(const Action &action) |
||||
{ |
||||
actions.emplace_back(action); |
||||
|
||||
return actions.size() - 1; |
||||
} |
||||
|
||||
void Keymapper::KeyPressed(int key) const |
||||
{ |
||||
auto it = keyIDToAction.find(key); |
||||
if (it == keyIDToAction.end()) |
||||
return; // Ignore unmapped keys.
|
||||
|
||||
const auto &action = it->second; |
||||
|
||||
// Check that the action can be triggered and that the chat textbox is not
|
||||
// open.
|
||||
if (!action.get().enable() || talkflag) |
||||
return; |
||||
|
||||
action(); |
||||
} |
||||
|
||||
std::string Keymapper::KeyNameForAction(ActionIndex actionIndex) const |
||||
{ |
||||
assert(actionIndex < actions.size()); |
||||
auto key = actions[actionIndex].key; |
||||
auto it = keyIDToKeyName.find(key); |
||||
assert(it != keyIDToKeyName.end()); |
||||
return it->second; |
||||
} |
||||
|
||||
void Keymapper::Save() const |
||||
{ |
||||
// Use the action vector to go though the actions to keep the same order.
|
||||
for (const auto &action : actions) { |
||||
if (action.key == DVL_VK_INVALID) { |
||||
// Just add an empty config entry if the action is unbound.
|
||||
SetIniValue("Keymapping", action.name.c_str(), ""); |
||||
continue; |
||||
} |
||||
|
||||
auto keyNameIt = keyIDToKeyName.find(action.key); |
||||
if (keyNameIt == keyIDToKeyName.end()) { |
||||
Log("Keymapper: no name found for key '{}'", action.key); |
||||
continue; |
||||
} |
||||
SetIniValue("Keymapping", action.name.c_str(), keyNameIt->second.c_str()); |
||||
} |
||||
} |
||||
|
||||
void Keymapper::Load() |
||||
{ |
||||
keyIDToAction.clear(); |
||||
|
||||
for (auto &action : actions) { |
||||
auto key = GetActionKey(action); |
||||
action.key = key; |
||||
if (key == DVL_VK_INVALID) { |
||||
// Skip if the action has no key bound to it.
|
||||
continue; |
||||
} |
||||
// Store the key in action.key and in the map so we can save() the
|
||||
// actions while keeping the same order as they have been added.
|
||||
keyIDToAction.emplace(key, action); |
||||
} |
||||
} |
||||
|
||||
int Keymapper::GetActionKey(const Keymapper::Action &action) |
||||
{ |
||||
std::array<char, 64> result; |
||||
if (!GetIniValue("Keymapping", action.name.c_str(), result.data(), result.size())) |
||||
return action.defaultKey; // Return the default key if no key has been set.
|
||||
|
||||
std::string key = result.data(); |
||||
if (key.empty()) |
||||
return DVL_VK_INVALID; |
||||
|
||||
auto keyIt = keyNameToKeyID.find(key); |
||||
if (keyIt == keyNameToKeyID.end()) { |
||||
// Return the default key if the key is unknown.
|
||||
Log("Keymapper: unknown key '{}'", key); |
||||
return action.defaultKey; |
||||
} |
||||
|
||||
auto it = keyIDToAction.find(keyIt->second); |
||||
if (it != keyIDToAction.end()) { |
||||
// Warn about overwriting keys.
|
||||
Log("Keymapper: key '{}' is already bound to action '{}', overwriting", key, it->second.get().name); |
||||
} |
||||
|
||||
return keyIt->second; |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -1,69 +0,0 @@
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <functional> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <vector> |
||||
|
||||
namespace devilution { |
||||
|
||||
/** The Keymapper maps keys to actions. */ |
||||
class Keymapper final { |
||||
public: |
||||
using ActionIndex = std::size_t; |
||||
|
||||
/**
|
||||
* Action represents an action that can be triggered using a keyboard |
||||
* shortcut. |
||||
*/ |
||||
class Action final { |
||||
public: |
||||
Action(const std::string &name, int defaultKey, std::function<void()> action) |
||||
: name(name) |
||||
, defaultKey(defaultKey) |
||||
, action(action) |
||||
, enable([] { return true; }) |
||||
{ |
||||
} |
||||
Action(const std::string &name, int defaultKey, std::function<void()> action, std::function<bool()> enable) |
||||
: name(name) |
||||
, defaultKey(defaultKey) |
||||
, action(action) |
||||
, enable(enable) |
||||
{ |
||||
} |
||||
|
||||
void operator()() const |
||||
{ |
||||
action(); |
||||
} |
||||
|
||||
private: |
||||
std::string name; |
||||
int defaultKey; |
||||
std::function<void()> action; |
||||
std::function<bool()> enable; |
||||
int key {}; |
||||
|
||||
friend class Keymapper; |
||||
}; |
||||
|
||||
Keymapper(); |
||||
|
||||
ActionIndex AddAction(const Action &action); |
||||
void KeyPressed(int key) const; |
||||
std::string KeyNameForAction(ActionIndex actionIndex) const; |
||||
void Save() const; |
||||
void Load(); |
||||
|
||||
private: |
||||
int GetActionKey(const Action &action); |
||||
|
||||
std::vector<Action> actions; |
||||
std::unordered_map<int, std::reference_wrapper<Action>> keyIDToAction; |
||||
std::unordered_map<int, std::string> keyIDToKeyName; |
||||
std::unordered_map<std::string, int> keyNameToKeyID; |
||||
}; |
||||
|
||||
} // namespace devilution
|
||||
Loading…
Reference in new issue