ADD: shortcuts representation.
parent
8b0d9d9cdc
commit
83cf9af94e
|
@ -0,0 +1,4 @@
|
||||||
|
.cache
|
||||||
|
*~*
|
||||||
|
*\#*
|
||||||
|
build
|
|
@ -0,0 +1,11 @@
|
||||||
|
.PHONY: build tests
|
||||||
|
|
||||||
|
build:
|
||||||
|
meson setup build
|
||||||
|
meson compile -C build
|
||||||
|
|
||||||
|
tests: build
|
||||||
|
build/twq-tests
|
||||||
|
|
||||||
|
install: tests
|
||||||
|
meson install -C build
|
|
@ -0,0 +1,41 @@
|
||||||
|
project('tiwiq',
|
||||||
|
'cpp',
|
||||||
|
version: '0.0.0',
|
||||||
|
default_options: [
|
||||||
|
'prefix=/usr',
|
||||||
|
'warning_level=3',
|
||||||
|
'cpp_std=c++17',
|
||||||
|
])
|
||||||
|
|
||||||
|
twq_lib = static_library('tiwiq',
|
||||||
|
sources: [
|
||||||
|
'src/core/KeyMod.cpp',
|
||||||
|
'src/core/Shortcut.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
dependency('ncursesw')
|
||||||
|
])
|
||||||
|
|
||||||
|
twq_dep = declare_dependency(
|
||||||
|
link_with: twq_lib,
|
||||||
|
include_directories: ['src/']
|
||||||
|
)
|
||||||
|
|
||||||
|
executable('twq',
|
||||||
|
sources: [
|
||||||
|
'src/main.cpp'
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
twq_dep
|
||||||
|
],
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
executable('twq-tests',
|
||||||
|
sources: [
|
||||||
|
'tests/main.cpp',
|
||||||
|
'tests/Shortcut.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
twq_dep,
|
||||||
|
dependency('catch2')
|
||||||
|
])
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef kwq_COMMONS_HPP
|
||||||
|
#define kwq_COMMONS_HPP
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define GEN_ENUM(X) X
|
||||||
|
#define GEN_STRING(X) #X
|
||||||
|
|
||||||
|
#define TWQ_ENUM(PREFIX, TYPES) \
|
||||||
|
enum PREFIX { TYPES(GEN_ENUM) }; \
|
||||||
|
constexpr char const* PREFIX ## Str [] = { TYPES(GEN_STRING) }
|
||||||
|
|
||||||
|
#define TWQ_ERROR(NAME) \
|
||||||
|
struct NAME : public std::runtime_error { \
|
||||||
|
NAME (std::string const& what): std::runtime_error {what} {} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TWQ_ASSERT(COND, MSG) \
|
||||||
|
if (! COND) { \
|
||||||
|
std::cerr << MSG << std::endl; \
|
||||||
|
abort(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "KeyMod.hpp"
|
||||||
|
|
||||||
|
namespace twq
|
||||||
|
{
|
||||||
|
namespace core
|
||||||
|
{
|
||||||
|
/*static*/ KeyMod KeyMod::key(KeyType key_type,
|
||||||
|
std::vector<ModType> const& mods)
|
||||||
|
{
|
||||||
|
return KeyMod { key_type, mods, std::nullopt };
|
||||||
|
}
|
||||||
|
|
||||||
|
/*explicit*/ KeyMod::KeyMod(char text,
|
||||||
|
std::vector<ModType> const& mods)
|
||||||
|
: m_key_type { KEY_TEXT }
|
||||||
|
, m_mods { mods }
|
||||||
|
, m_text { text }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*explicit*/ KeyMod::KeyMod(KeyType key_type,
|
||||||
|
std::vector<ModType> const& mods,
|
||||||
|
std::optional<char> text)
|
||||||
|
: m_key_type { key_type }
|
||||||
|
, m_mods { mods }
|
||||||
|
, m_text { text }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ KeyMod::~KeyMod()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeyMod::has_mod(ModType mod_type) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
std::end(m_mods) !=
|
||||||
|
std::find(std::begin(m_mods), std::end(m_mods), mod_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string KeyMod::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
if (has_mod(MOD_LCTRL))
|
||||||
|
{
|
||||||
|
ss << "C-";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_mod(MOD_ALT))
|
||||||
|
{
|
||||||
|
ss << "A-";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_key_type == KEY_TEXT)
|
||||||
|
{
|
||||||
|
ss << *m_text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << (KeyTypeStr[m_key_type] + strlen("KEY_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef twq_core_KEYMOD_HPP
|
||||||
|
#define twq_core_KEYMOD_HPP
|
||||||
|
|
||||||
|
#include "../commons.hpp"
|
||||||
|
|
||||||
|
#define KEY_TYPES(G) \
|
||||||
|
G(KEY_TEXT), \
|
||||||
|
G(KEY_UP), \
|
||||||
|
G(KEY_DOWN), \
|
||||||
|
G(KEY_LEFT), \
|
||||||
|
G(KEY_RIGHT), \
|
||||||
|
G(KEY_COUNT),
|
||||||
|
|
||||||
|
#define MOD_TYPES(G)\
|
||||||
|
G(MOD_LCTRL), \
|
||||||
|
G(MOD_ALT), \
|
||||||
|
G(MOD_COUNT),
|
||||||
|
|
||||||
|
namespace twq
|
||||||
|
{
|
||||||
|
namespace core
|
||||||
|
{
|
||||||
|
TWQ_ENUM(KeyType, KEY_TYPES);
|
||||||
|
TWQ_ENUM(ModType, MOD_TYPES);
|
||||||
|
|
||||||
|
class KeyMod
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static KeyMod key(KeyType key_type,
|
||||||
|
std::vector<ModType> const& mods={});
|
||||||
|
|
||||||
|
explicit KeyMod(char text, std::vector<ModType> const& mods={});
|
||||||
|
|
||||||
|
explicit KeyMod(KeyType key_type,
|
||||||
|
std::vector<ModType> const& mods,
|
||||||
|
std::optional<char> text);
|
||||||
|
|
||||||
|
KeyType key_type() const { return m_key_type; }
|
||||||
|
std::vector<ModType> mods() const { return m_mods; }
|
||||||
|
std::optional<char> text() const { return m_text; }
|
||||||
|
|
||||||
|
bool has_mod(ModType mod_type) const;
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
virtual ~KeyMod();
|
||||||
|
|
||||||
|
private:
|
||||||
|
KeyType m_key_type;
|
||||||
|
std::vector<ModType> m_mods;
|
||||||
|
std::optional<char> m_text;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include "Shortcut.hpp"
|
||||||
|
#include "src/core/KeyMod.hpp"
|
||||||
|
|
||||||
|
namespace twq
|
||||||
|
{
|
||||||
|
namespace core
|
||||||
|
{
|
||||||
|
/*explicit*/ Shortcut::Shortcut()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*explicit*/ Shortcut::Shortcut(std::string const& repr)
|
||||||
|
{
|
||||||
|
std::string buffer;
|
||||||
|
std::vector<ModType> mods;
|
||||||
|
|
||||||
|
auto create_keymod = [&](){
|
||||||
|
if (buffer.find("C-") != std::string::npos)
|
||||||
|
{
|
||||||
|
mods.push_back(MOD_LCTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.find("A-") != std::string::npos)
|
||||||
|
{
|
||||||
|
mods.push_back(MOD_ALT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t k = buffer.size() - 1;
|
||||||
|
while (k >= 0 && std::isspace(buffer[k]))
|
||||||
|
{
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
while (k >= 0
|
||||||
|
&& !std::isspace(buffer[k])
|
||||||
|
&& buffer[k] != '-')
|
||||||
|
{
|
||||||
|
value = buffer[k] + value;
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.size() == 1)
|
||||||
|
{
|
||||||
|
return KeyMod {KEY_TEXT, mods, value.front()};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<KEY_COUNT; i++)
|
||||||
|
{
|
||||||
|
std::string val = KeyTypeStr[i] + strlen("KEY_");
|
||||||
|
|
||||||
|
if (val == value)
|
||||||
|
{
|
||||||
|
return KeyMod {(KeyType) i, mods, std::nullopt};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw invalid_shortcut_error {"cannot convert '"
|
||||||
|
+ repr
|
||||||
|
+ "' to shortcut."};
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t i=0; i<repr.size(); i++)
|
||||||
|
{
|
||||||
|
char c = repr[i];
|
||||||
|
|
||||||
|
if (std::isspace(c))
|
||||||
|
{
|
||||||
|
if (!buffer.empty())
|
||||||
|
{
|
||||||
|
push(create_keymod());
|
||||||
|
}
|
||||||
|
|
||||||
|
mods.clear();
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer.empty())
|
||||||
|
{
|
||||||
|
push(create_keymod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Shortcut::~Shortcut()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shortcut::push(KeyMod const& keymod)
|
||||||
|
{
|
||||||
|
m_keymods.push_back(keymod);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Shortcut::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
std::string sep;
|
||||||
|
|
||||||
|
for (auto const& km: m_keymods)
|
||||||
|
{
|
||||||
|
ss << sep << km.string();
|
||||||
|
sep = " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef twq_core_SHORTCUT_HPP
|
||||||
|
#define twq_core_SHORTCUT_HPP
|
||||||
|
|
||||||
|
#include "KeyMod.hpp"
|
||||||
|
|
||||||
|
namespace twq
|
||||||
|
{
|
||||||
|
namespace core
|
||||||
|
{
|
||||||
|
TWQ_ERROR(invalid_shortcut_error);
|
||||||
|
|
||||||
|
class Shortcut
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Shortcut();
|
||||||
|
explicit Shortcut(std::string const& repr);
|
||||||
|
virtual ~Shortcut();
|
||||||
|
|
||||||
|
void push(KeyMod const& keymod);
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<KeyMod> m_keymods;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int, char**)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../src/core/Shortcut.hpp"
|
||||||
|
|
||||||
|
using namespace twq::core;
|
||||||
|
|
||||||
|
class ShortcutTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ShortcutTest() {}
|
||||||
|
virtual ~ShortcutTest() {}
|
||||||
|
|
||||||
|
void test_to_string(std::string const& oracle,
|
||||||
|
std::vector<KeyMod> keymods)
|
||||||
|
{
|
||||||
|
Shortcut sc;
|
||||||
|
|
||||||
|
for (auto km: keymods)
|
||||||
|
{
|
||||||
|
sc.push(km);
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Expected '" << oracle << "' got '" << sc.string() << "'");
|
||||||
|
REQUIRE(oracle == sc.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_from_string(std::string const& oracle,
|
||||||
|
std::string const& shortcut)
|
||||||
|
{
|
||||||
|
Shortcut sc { shortcut };
|
||||||
|
INFO("Expected '" << oracle << "' got '" << sc.string() << "'");
|
||||||
|
REQUIRE(oracle == sc.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ShortcutTest, "Shortcut_string")
|
||||||
|
{
|
||||||
|
test_to_string("f", {KeyMod {'f'}});
|
||||||
|
|
||||||
|
test_to_string("a b c", {
|
||||||
|
KeyMod {'a'},
|
||||||
|
KeyMod {'b'},
|
||||||
|
KeyMod {'c'},
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("C-g", {KeyMod {'g', {MOD_LCTRL}}});
|
||||||
|
test_to_string("A-g", {KeyMod {'g', {MOD_ALT}}});
|
||||||
|
test_to_string("C-A-g", {KeyMod {'g', {MOD_LCTRL, MOD_ALT}}});
|
||||||
|
|
||||||
|
test_to_string("A-a C-A-b C-c d", {
|
||||||
|
KeyMod {'a', {MOD_ALT}},
|
||||||
|
KeyMod {'b', {MOD_LCTRL, MOD_ALT}},
|
||||||
|
KeyMod {'c', {MOD_LCTRL}},
|
||||||
|
KeyMod {'d'},
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("UP", {
|
||||||
|
KeyMod::key(KEY_UP)
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("DOWN", {
|
||||||
|
KeyMod::key(KEY_DOWN)
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("LEFT", {
|
||||||
|
KeyMod::key(KEY_LEFT)
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("RIGHT", {
|
||||||
|
KeyMod::key(KEY_RIGHT)
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("C-UP", {
|
||||||
|
KeyMod::key(KEY_UP, {MOD_LCTRL})
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("A-LEFT", {
|
||||||
|
KeyMod::key(KEY_LEFT, {MOD_ALT}),
|
||||||
|
});
|
||||||
|
|
||||||
|
test_to_string("C-A-RIGHT", {
|
||||||
|
KeyMod::key(KEY_RIGHT, {MOD_LCTRL, MOD_ALT}),
|
||||||
|
});
|
||||||
|
|
||||||
|
test_from_string("", "");
|
||||||
|
|
||||||
|
test_from_string("a", "a");
|
||||||
|
test_from_string("a", " a");
|
||||||
|
test_from_string("a", "a ");
|
||||||
|
|
||||||
|
test_from_string("C-a", "C-a");
|
||||||
|
test_from_string("C-a", " C-a ");
|
||||||
|
test_from_string("A-a", "A-a");
|
||||||
|
test_from_string("A-a", " A-a ");
|
||||||
|
|
||||||
|
test_from_string("C-A-a", " A-C-a ");
|
||||||
|
test_from_string("C-A-a", " C-A-a ");
|
||||||
|
|
||||||
|
test_from_string("C-A-a b C-c", " C-A-a b C-c");
|
||||||
|
|
||||||
|
test_from_string("C-A-LEFT RIGHT C-UP",
|
||||||
|
" C-A-LEFT RIGHT C-UP");
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(Shortcut { "C-A-DUCK" }, invalid_shortcut_error);
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
Loading…
Reference in New Issue