can now quit tiwiq using C-C C-C keys.

main
bog 2023-10-04 16:56:06 +02:00
parent 1d2394068d
commit 5188860936
30 changed files with 719 additions and 144 deletions

View File

@ -9,12 +9,25 @@ project('tiwiq',
twq_lib = static_library('tiwiq', twq_lib = static_library('tiwiq',
sources: [ sources: [
'src/core/KeyMod.cpp', 'src/core/Binding.cpp',
'src/core/Shortcut.cpp',
'src/core/Command.cpp', 'src/core/Command.cpp',
'src/core/Context.cpp', 'src/core/Context.cpp',
'src/core/Binding.cpp',
'src/core/Executor.cpp', 'src/core/Executor.cpp',
'src/core/KeyMod.cpp',
'src/core/Plugin.cpp',
'src/core/Shortcut.cpp',
'src/core/Tiwiq.cpp',
'src/core/UI.cpp',
'src/core/Observer.cpp',
'src/core/Observable.cpp',
'src/core/Event.cpp',
'src/cmd/QuitCmd.cpp',
'src/plugins/CorePlugin.cpp',
'src/term/Controller.cpp',
'src/term/Term.cpp',
], ],
dependencies: [ dependencies: [
dependency('ncursesw') dependency('ncursesw')

25
src/cmd/QuitCmd.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "QuitCmd.hpp"
namespace twq
{
namespace cmd
{
/*explicit*/ QuitCmd::QuitCmd()
{
}
/*virtual*/ QuitCmd::~QuitCmd()
{
}
/*virtual*/ void QuitCmd::execute(core::Context& context) /*override*/
{
context.tiwiq().stop();
}
/*virtual*/ void QuitCmd::undo(core::Context&) /*override*/
{
}
}
}

24
src/cmd/QuitCmd.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef twq_cmd_QUITCMD_HPP
#define twq_cmd_QUITCMD_HPP
#include "../core/Command.hpp"
namespace twq
{
namespace cmd
{
class QuitCmd: public core::Command
{
public:
explicit QuitCmd();
virtual ~QuitCmd();
virtual void execute(core::Context& context) override;
virtual void undo(core::Context& context) override;
private:
};
}
}
#endif

View File

@ -4,7 +4,8 @@ namespace twq
{ {
namespace core namespace core
{ {
/*explicit*/ Context::Context() /*explicit*/ Context::Context(Tiwiq& tiwiq)
: m_tiwiq { tiwiq }
{ {
} }

View File

@ -1,6 +1,8 @@
#ifndef twq_core_CONTEXT_HPP #ifndef twq_core_CONTEXT_HPP
#define twq_core_CONTEXT_HPP #define twq_core_CONTEXT_HPP
#include "Tiwiq.hpp"
namespace twq namespace twq
{ {
namespace core namespace core
@ -8,10 +10,13 @@ namespace twq
class Context class Context
{ {
public: public:
explicit Context(); explicit Context(Tiwiq& tiwiq);
virtual ~Context(); virtual ~Context();
Tiwiq& tiwiq() const { return m_tiwiq; }
private: private:
Tiwiq& m_tiwiq;
}; };
} }
} }

8
src/core/Event.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "Event.hpp"
namespace twq
{
namespace core
{
}
}

18
src/core/Event.hpp Normal file
View File

@ -0,0 +1,18 @@
#ifndef twq_core_EVENT_HPP
#define twq_core_EVENT_HPP
#include "../commons.hpp"
#include "KeyMod.hpp"
namespace twq
{
namespace core
{
struct Event
{
std::optional<KeyMod> key;
};
}
}
#endif

View File

@ -10,18 +10,18 @@ namespace twq
return KeyMod { key_type, mods, std::nullopt }; return KeyMod { key_type, mods, std::nullopt };
} }
/*explicit*/ KeyMod::KeyMod(std::string const& repr) /*explicit*/ KeyMod::KeyMod(std::wstring const& repr)
{ {
std::vector<ModType> mods; std::vector<ModType> mods;
if (repr.find("C-") != std::string::npos) if (repr.find(L"C-") != std::string::npos)
{ {
mods.push_back(MOD_LCTRL); mods.push_back(KM_MOD_LCTRL);
} }
if (repr.find("A-") != std::string::npos) if (repr.find(L"A-") != std::string::npos)
{ {
mods.push_back(MOD_ALT); mods.push_back(KM_MOD_ALT);
} }
ssize_t k = repr.size() - 1; ssize_t k = repr.size() - 1;
@ -30,7 +30,7 @@ namespace twq
k--; k--;
} }
std::string value; std::wstring value;
while (k >= 0 while (k >= 0
&& !std::isspace(repr[k]) && !std::isspace(repr[k])
@ -42,16 +42,17 @@ namespace twq
if (value.size() == 1) if (value.size() == 1)
{ {
m_key_type = KEY_TEXT; m_key_type = KM_KEY_TEXT;
m_mods = mods; m_mods = mods;
m_text = value.front(); m_text = value.front();
return; return;
} }
else else
{ {
for (size_t i=0; i<KEY_COUNT; i++) for (size_t i=0; i<KM_KEY_COUNT; i++)
{ {
std::string val = KeyTypeStr[i] + strlen("KEY_"); std::string v = KeyTypeStr[i] + strlen("KM_KEY_");
std::wstring val(std::begin(v), std::end(v));
if (val == value) if (val == value)
{ {
@ -62,15 +63,16 @@ namespace twq
} }
throw invalid_keymod_error {"cannot convert '" throw invalid_keymod_error {"cannot convert '"
+ repr + std::string(std::begin(repr),
std::end(repr))
+ "' to keymod."}; + "' to keymod."};
} }
} }
/*explicit*/ KeyMod::KeyMod(char text, /*explicit*/ KeyMod::KeyMod(wchar_t text,
std::vector<ModType> const& mods) std::vector<ModType> const& mods)
: m_key_type { KEY_TEXT } : m_key_type { KM_KEY_TEXT }
, m_mods { mods } , m_mods { mods }
, m_text { text } , m_text { text }
{ {
@ -78,7 +80,7 @@ namespace twq
/*explicit*/ KeyMod::KeyMod(KeyType key_type, /*explicit*/ KeyMod::KeyMod(KeyType key_type,
std::vector<ModType> const& mods, std::vector<ModType> const& mods,
std::optional<char> text) std::optional<wchar_t> text)
: m_key_type { key_type } : m_key_type { key_type }
, m_mods { mods } , m_mods { mods }
, m_text { text } , m_text { text }
@ -103,7 +105,7 @@ namespace twq
return false; return false;
} }
if (m_key_type == KEY_TEXT if (m_key_type == KM_KEY_TEXT
&& *m_text != *rhs.m_text) && *m_text != *rhs.m_text)
{ {
return false; return false;
@ -126,27 +128,27 @@ namespace twq
return true; return true;
} }
std::string KeyMod::string() const std::wstring KeyMod::wstring() const
{ {
std::stringstream ss; std::wstringstream ss;
if (has_mod(MOD_LCTRL)) if (has_mod(KM_MOD_LCTRL))
{ {
ss << "C-"; ss << "C-";
} }
if (has_mod(MOD_ALT)) if (has_mod(KM_MOD_ALT))
{ {
ss << "A-"; ss << "A-";
} }
if (m_key_type == KEY_TEXT) if (m_key_type == KM_KEY_TEXT)
{ {
ss << *m_text; ss << *m_text;
} }
else else
{ {
ss << (KeyTypeStr[m_key_type] + strlen("KEY_")); ss << (KeyTypeStr[m_key_type] + strlen("KM_KEY_"));
} }
return ss.str(); return ss.str();

View File

@ -4,17 +4,17 @@
#include "../commons.hpp" #include "../commons.hpp"
#define KEY_TYPES(G) \ #define KEY_TYPES(G) \
G(KEY_TEXT), \ G(KM_KEY_TEXT), \
G(KEY_UP), \ G(KM_KEY_UP), \
G(KEY_DOWN), \ G(KM_KEY_DOWN), \
G(KEY_LEFT), \ G(KM_KEY_LEFT), \
G(KEY_RIGHT), \ G(KM_KEY_RIGHT), \
G(KEY_COUNT), G(KM_KEY_COUNT),
#define MOD_TYPES(G)\ #define MOD_TYPES(G)\
G(MOD_LCTRL), \ G(KM_MOD_LCTRL), \
G(MOD_ALT), \ G(KM_MOD_ALT), \
G(MOD_COUNT), G(KM_MOD_COUNT),
namespace twq namespace twq
{ {
@ -31,31 +31,31 @@ namespace twq
static KeyMod key(KeyType key_type, static KeyMod key(KeyType key_type,
std::vector<ModType> const& mods={}); std::vector<ModType> const& mods={});
explicit KeyMod(std::string const& repr); explicit KeyMod(std::wstring const& repr);
explicit KeyMod(char text, std::vector<ModType> const& mods={}); explicit KeyMod(wchar_t text, std::vector<ModType> const& mods={});
explicit KeyMod(KeyType key_type, explicit KeyMod(KeyType key_type,
std::vector<ModType> const& mods, std::vector<ModType> const& mods,
std::optional<char> text); std::optional<wchar_t> text);
KeyType key_type() const { return m_key_type; } KeyType key_type() const { return m_key_type; }
std::vector<ModType> mods() const { return m_mods; } std::vector<ModType> mods() const { return m_mods; }
std::optional<char> text() const { return m_text; } std::optional<wchar_t> text() const { return m_text; }
bool has_mod(ModType mod_type) const; bool has_mod(ModType mod_type) const;
bool equals(KeyMod const& rhs) const; bool equals(KeyMod const& rhs) const;
std::string string() const; std::wstring wstring() const;
virtual ~KeyMod(); virtual ~KeyMod();
private: private:
KeyType m_key_type; KeyType m_key_type;
std::vector<ModType> m_mods; std::vector<ModType> m_mods;
std::optional<char> m_text; std::optional<wchar_t> m_text;
}; };
} }
} }

33
src/core/Observable.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "Observable.hpp"
namespace twq
{
namespace core
{
/*explicit*/ Observable::Observable()
{
}
/*virtual*/ Observable::~Observable()
{
}
void Observable::add_observer(std::weak_ptr<Observer> observer)
{
m_observers.push_back(observer);
}
void Observable::notify(Event& event)
{
for (auto observer: m_observers)
{
if (auto obs = observer.lock();
obs)
{
obs->update(event);
}
}
}
}
}

27
src/core/Observable.hpp Normal file
View File

@ -0,0 +1,27 @@
#ifndef twq_core_OBSERVABLE_HPP
#define twq_core_OBSERVABLE_HPP
#include "../commons.hpp"
#include "Observer.hpp"
#include "Event.hpp"
namespace twq
{
namespace core
{
class Observable
{
public:
explicit Observable();
virtual ~Observable();
void add_observer(std::weak_ptr<Observer> observer);
void notify(Event& event);
private:
std::vector<std::weak_ptr<Observer>> m_observers;
};
}
}
#endif

15
src/core/Observer.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "Observer.hpp"
namespace twq
{
namespace core
{
/*explicit*/ Observer::Observer()
{
}
/*virtual*/ Observer::~Observer()
{
}
}
}

24
src/core/Observer.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef twq_core_OBSERVER_HPP
#define twq_core_OBSERVER_HPP
#include "../commons.hpp"
#include "Event.hpp"
namespace twq
{
namespace core
{
class Observer
{
public:
explicit Observer();
virtual ~Observer();
virtual void update(Event&) {}
private:
};
}
}
#endif

23
src/core/Plugin.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "Plugin.hpp"
#include "Executor.hpp"
namespace twq
{
namespace core
{
/*explicit*/ Plugin::Plugin(std::string const& name)
: m_name { name }
, m_executor { std::make_shared<Executor>() }
{
}
/*virtual*/ Plugin::~Plugin()
{
}
std::weak_ptr<Executor> Plugin::executor()
{
return m_executor;
}
}
}

33
src/core/Plugin.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef twq_core_PLUGIN_HPP
#define twq_core_PLUGIN_HPP
#include "../commons.hpp"
#include "UI.hpp"
namespace twq
{
namespace core
{
class Executor;
class Plugin
{
public:
explicit Plugin(std::string const& name);
virtual ~Plugin();
std::string name() const { return m_name; }
std::weak_ptr<UI> ui() { return m_ui; }
std::weak_ptr<Executor> executor();
private:
std::string m_name;
std::shared_ptr<Executor> m_executor;
std::shared_ptr<UI> m_ui =
std::make_shared<UI>();
};
}
}
#endif

View File

@ -10,14 +10,14 @@ namespace twq
{ {
} }
/*explicit*/ Shortcut::Shortcut(std::string const& repr) /*explicit*/ Shortcut::Shortcut(std::wstring const& repr)
{ {
std::string buffer; std::wstring buffer;
std::vector<ModType> mods; std::vector<ModType> mods;
for (size_t i=0; i<repr.size(); i++) for (size_t i=0; i<repr.size(); i++)
{ {
char c = repr[i]; wchar_t c = repr[i];
if (std::isspace(c)) if (std::isspace(c))
{ {
@ -29,8 +29,10 @@ namespace twq
} }
catch (invalid_keymod_error const& err) catch (invalid_keymod_error const& err)
{ {
throw invalid_shortcut_error {"cannot convert '" throw invalid_shortcut_error {
+ repr "cannot convert '"
+ std::string(std::begin(repr),
std::end(repr))
+ "' to shortcut"}; + "' to shortcut"};
} }
} }
@ -53,7 +55,8 @@ namespace twq
catch (invalid_keymod_error const& err) catch (invalid_keymod_error const& err)
{ {
throw invalid_shortcut_error {"cannot convert '" throw invalid_shortcut_error {"cannot convert '"
+ repr + std::string(std::begin(repr),
std::end(repr))
+ "' to shortcut"}; + "' to shortcut"};
} }
} }
@ -77,15 +80,15 @@ namespace twq
m_keymods.push_back(keymod); m_keymods.push_back(keymod);
} }
std::string Shortcut::string() const std::wstring Shortcut::wstring() const
{ {
std::stringstream ss; std::wstringstream ss;
std::string sep; std::wstring sep;
for (auto const& km: m_keymods) for (auto const& km: m_keymods)
{ {
ss << sep << km.string(); ss << sep << km.wstring();
sep = " "; sep = L" ";
} }
return ss.str(); return ss.str();

View File

@ -13,14 +13,14 @@ namespace twq
{ {
public: public:
explicit Shortcut(); explicit Shortcut();
explicit Shortcut(std::string const& repr); explicit Shortcut(std::wstring const& repr);
virtual ~Shortcut(); virtual ~Shortcut();
size_t count() const { return m_keymods.size(); } size_t count() const { return m_keymods.size(); }
KeyMod get(size_t index) const; KeyMod get(size_t index) const;
void push(KeyMod const& keymod); void push(KeyMod const& keymod);
std::string string() const; std::wstring wstring() const;
private: private:
std::vector<KeyMod> m_keymods; std::vector<KeyMod> m_keymods;

44
src/core/Tiwiq.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "Tiwiq.hpp"
#include "Event.hpp"
#include "Executor.hpp"
namespace twq
{
namespace core
{
/*explicit*/ Tiwiq::Tiwiq()
{
}
/*virtual*/ Tiwiq::~Tiwiq()
{
}
void Tiwiq::start()
{
m_is_running = true;
}
void Tiwiq::stop()
{
m_is_running = false;
}
void Tiwiq::add_plugin(std::shared_ptr<Plugin> plugin)
{
m_plugins.push_back(plugin);
}
void Tiwiq::update(Event& event) /*override*/
{
if (event.key)
{
if (!m_plugins.empty())
{
Context ctx { *this };
m_plugins.back()->executor().lock()->update(ctx, *event.key);
}
}
}
}
}

34
src/core/Tiwiq.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef twq_core_TIWIQ_HPP
#define twq_core_TIWIQ_HPP
#include "../commons.hpp"
#include "Plugin.hpp"
#include "Observer.hpp"
namespace twq
{
namespace core
{
class Tiwiq: public Observer
{
public:
explicit Tiwiq();
virtual ~Tiwiq();
bool is_running() const { return m_is_running; }
void start();
void stop();
void add_plugin(std::shared_ptr<Plugin> plugin);
void update(Event& event) override;
private:
bool m_is_running = false;
std::vector<std::shared_ptr<Plugin>> m_plugins;
};
}
}
#endif

15
src/core/UI.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "UI.hpp"
namespace twq
{
namespace core
{
/*explicit*/ UI::UI()
{
}
/*virtual*/ UI::~UI()
{
}
}
}

19
src/core/UI.hpp Normal file
View File

@ -0,0 +1,19 @@
#ifndef twq_core_UI_HPP
#define twq_core_UI_HPP
namespace twq
{
namespace core
{
class UI
{
public:
explicit UI();
virtual ~UI();
private:
};
}
}
#endif

View File

@ -1,6 +1,27 @@
#include <iostream> #include <iostream>
#include "core/Tiwiq.hpp"
#include "term/Term.hpp"
#include "plugins/CorePlugin.hpp"
int main(int, char**) int main(int, char**)
{ {
auto tiwiq = std::make_shared<twq::core::Tiwiq>();
auto term = std::make_shared<twq::term::Term>();
term->controller().lock()->add_observer(tiwiq);
// Build and add plugins
// ---------------------
auto core_plugin = std::make_shared<twq::plugins::CorePlugin>();
tiwiq->add_plugin(core_plugin);
// Start TiwiQ
// -----------
tiwiq->start();
while (tiwiq->is_running())
{
term->controller().lock()->update();
}
return 0; return 0;
} }

View File

@ -0,0 +1,23 @@
#include "CorePlugin.hpp"
#include "../core/Executor.hpp"
namespace twq
{
namespace plugins
{
/*explicit*/ CorePlugin::CorePlugin()
: core::Plugin {"core"}
{
auto exec = executor().lock();
auto sc = std::make_shared<core::Shortcut>(L"C-C C-C");
auto cmd = std::make_shared<cmd::QuitCmd>();
auto binding = core::Binding{sc, cmd};
exec->register_binding(binding);
}
/*virtual*/ CorePlugin::~CorePlugin()
{
}
}
}

View File

@ -0,0 +1,22 @@
#ifndef twq_plugins_COREPLUGIN_HPP
#define twq_plugins_COREPLUGIN_HPP
#include "../core/Plugin.hpp"
#include "../cmd/QuitCmd.hpp"
namespace twq
{
namespace plugins
{
class CorePlugin: public core::Plugin
{
public:
explicit CorePlugin();
virtual ~CorePlugin();
private:
};
}
}
#endif

61
src/term/Controller.cpp Normal file
View File

@ -0,0 +1,61 @@
#include <curses.h>
#include "Controller.hpp"
#include "../core/KeyMod.hpp"
namespace twq
{
namespace term
{
/*explicit*/ Controller::Controller()
{
}
/*virtual*/ Controller::~Controller()
{
}
void Controller::update()
{
core::Event event;
event.key = get();
notify(event);
}
core::KeyMod Controller::get() const
{
wint_t c;
get_wch(&c);
core::KeyType key_type = core::KM_KEY_TEXT;
std::optional<wchar_t> text;
std::vector<core::ModType> mods;
if (c == 27)
{
get_wch(&c);
mods.push_back(core::KM_MOD_ALT);
}
std::string name = keyname(c);
std::wstring wname {std::begin(name), std::end(name)};
if (wname.size() > 1 && wname[0] == L'^')
{
text = wname[1];
mods.push_back(core::KM_MOD_LCTRL);
}
else if (std::iswprint((wchar_t) c))
{
text = c;
}
else
{
TWQ_ASSERT(0, "keymod not found");
}
auto keymod = core::KeyMod {key_type, mods, text};
return keymod;
}
}
}

26
src/term/Controller.hpp Normal file
View File

@ -0,0 +1,26 @@
#ifndef twq_term_CONTROLLER_HPP
#define twq_term_CONTROLLER_HPP
#include "../commons.hpp"
#include "../core/KeyMod.hpp"
#include "../core/Observable.hpp"
namespace twq
{
namespace term
{
class Controller: public core::Observable
{
public:
explicit Controller();
virtual ~Controller();
void update();
core::KeyMod get() const;
private:
};
}
}
#endif

22
src/term/Term.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "Term.hpp"
namespace twq
{
namespace term
{
/*explicit*/ Term::Term()
{
setlocale(LC_ALL, "");
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
}
/*virtual*/ Term::~Term()
{
endwin();
}
}
}

28
src/term/Term.hpp Normal file
View File

@ -0,0 +1,28 @@
#ifndef twq_term_TERM_HPP
#define twq_term_TERM_HPP
#include <ncurses.h>
#include "../commons.hpp"
#include "Controller.hpp"
namespace twq
{
namespace term
{
class Term
{
public:
explicit Term();
virtual ~Term();
std::weak_ptr<Controller> controller() { return m_controller; }
private:
std::shared_ptr<Controller> m_controller =
std::make_shared<Controller>();
};
}
}
#endif

View File

@ -1,4 +1,5 @@
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include "../src/core/Tiwiq.hpp"
#include "../src/core/Executor.hpp" #include "../src/core/Executor.hpp"
using namespace twq::core; using namespace twq::core;
@ -9,7 +10,13 @@ public:
explicit ExecutorTest() {} explicit ExecutorTest() {}
virtual ~ExecutorTest() {} virtual ~ExecutorTest() {}
Context get_context()
{
return Context {m_tiwiq};
}
protected: protected:
twq::core::Tiwiq m_tiwiq;
}; };
struct CommandMock: public Command struct CommandMock: public Command
@ -28,95 +35,95 @@ struct CommandMock: public Command
TEST_CASE_METHOD(ExecutorTest, "Executor_one_key") TEST_CASE_METHOD(ExecutorTest, "Executor_one_key")
{ {
Context ctx; Context ctx = get_context();
auto cmd = std::make_shared<CommandMock>(); auto cmd = std::make_shared<CommandMock>();
Binding b {std::make_shared<Shortcut>("a"), cmd}; Binding b {std::make_shared<Shortcut>(L"a"), cmd};
Executor exec; Executor exec;
exec.register_binding(b); exec.register_binding(b);
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(1 == cmd->execute_count); REQUIRE(1 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(2 == cmd->execute_count); REQUIRE(2 == cmd->execute_count);
} }
TEST_CASE_METHOD(ExecutorTest, "Executor_two_same_keys") TEST_CASE_METHOD(ExecutorTest, "Executor_two_same_keys")
{ {
Context ctx; Context ctx = get_context();
auto cmd = std::make_shared<CommandMock>(); auto cmd = std::make_shared<CommandMock>();
Binding b {std::make_shared<Shortcut>("a a"), cmd}; Binding b {std::make_shared<Shortcut>(L"a a"), cmd};
Executor exec; Executor exec;
exec.register_binding(b); exec.register_binding(b);
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(1 == cmd->execute_count); REQUIRE(1 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(1 == cmd->execute_count); REQUIRE(1 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(2 == cmd->execute_count); REQUIRE(2 == cmd->execute_count);
} }
TEST_CASE_METHOD(ExecutorTest, "Executor_two_keys") TEST_CASE_METHOD(ExecutorTest, "Executor_two_keys")
{ {
Context ctx; Context ctx = get_context();
auto cmd = std::make_shared<CommandMock>(); auto cmd = std::make_shared<CommandMock>();
Binding b {std::make_shared<Shortcut>("a b"), cmd}; Binding b {std::make_shared<Shortcut>(L"a b"), cmd};
Executor exec; Executor exec;
exec.register_binding(b); exec.register_binding(b);
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(1 == cmd->execute_count); REQUIRE(1 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(1 == cmd->execute_count); REQUIRE(1 == cmd->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(2 == cmd->execute_count); REQUIRE(2 == cmd->execute_count);
} }
TEST_CASE_METHOD(ExecutorTest, "Executor_wrong_key") TEST_CASE_METHOD(ExecutorTest, "Executor_wrong_key")
{ {
Context ctx; Context ctx = get_context();
auto cmd = std::make_shared<CommandMock>(); auto cmd = std::make_shared<CommandMock>();
Binding b {std::make_shared<Shortcut>("a b"), cmd}; Binding b {std::make_shared<Shortcut>(L"a b"), cmd};
Executor exec; Executor exec;
exec.register_binding(b); exec.register_binding(b);
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"c"}); exec.update(ctx, KeyMod {L"c"});
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"a"}); exec.update(ctx, KeyMod {L"a"});
REQUIRE(0 == cmd->execute_count); REQUIRE(0 == cmd->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(1 == cmd->execute_count); REQUIRE(1 == cmd->execute_count);
} }
TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands") TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
{ {
Context ctx; Context ctx = get_context();
auto cmd0 = std::make_shared<CommandMock>(); auto cmd0 = std::make_shared<CommandMock>();
Binding b0 {std::make_shared<Shortcut>("C-a b"), cmd0}; Binding b0 {std::make_shared<Shortcut>(L"C-a b"), cmd0};
auto cmd1 = std::make_shared<CommandMock>(); auto cmd1 = std::make_shared<CommandMock>();
Binding b1 {std::make_shared<Shortcut>("C-a c"), cmd1}; Binding b1 {std::make_shared<Shortcut>(L"C-a c"), cmd1};
Executor exec; Executor exec;
exec.register_binding(b0); exec.register_binding(b0);
@ -126,11 +133,11 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
{ {
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"C-a"}); exec.update(ctx, KeyMod {L"C-a"});
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(1 == cmd0->execute_count); REQUIRE(1 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
@ -140,11 +147,11 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
{ {
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"C-a"}); exec.update(ctx, KeyMod {L"C-a"});
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"c"}); exec.update(ctx, KeyMod {L"c"});
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(1 == cmd1->execute_count); REQUIRE(1 == cmd1->execute_count);
@ -154,15 +161,15 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
{ {
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"C-a"}); exec.update(ctx, KeyMod {L"C-a"});
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"d"}); exec.update(ctx, KeyMod {L"d"});
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
@ -171,13 +178,13 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
TEST_CASE_METHOD(ExecutorTest, "Executor_two_sequentials_commands") TEST_CASE_METHOD(ExecutorTest, "Executor_two_sequentials_commands")
{ {
Context ctx; Context ctx = get_context();
auto cmd0 = std::make_shared<CommandMock>(); auto cmd0 = std::make_shared<CommandMock>();
Binding b0 {std::make_shared<Shortcut>("C-a b"), cmd0}; Binding b0 {std::make_shared<Shortcut>(L"C-a b"), cmd0};
auto cmd1 = std::make_shared<CommandMock>(); auto cmd1 = std::make_shared<CommandMock>();
Binding b1 {std::make_shared<Shortcut>("b c"), cmd1}; Binding b1 {std::make_shared<Shortcut>(L"b c"), cmd1};
Executor exec; Executor exec;
exec.register_binding(b0); exec.register_binding(b0);
@ -187,23 +194,23 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_sequentials_commands")
{ {
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"C-a"}); exec.update(ctx, KeyMod {L"C-a"});
REQUIRE(0 == cmd0->execute_count); REQUIRE(0 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(1 == cmd0->execute_count); REQUIRE(1 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"c"}); exec.update(ctx, KeyMod {L"c"});
REQUIRE(1 == cmd0->execute_count); REQUIRE(1 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"b"}); exec.update(ctx, KeyMod {L"b"});
REQUIRE(1 == cmd0->execute_count); REQUIRE(1 == cmd0->execute_count);
REQUIRE(0 == cmd1->execute_count); REQUIRE(0 == cmd1->execute_count);
exec.update(ctx, KeyMod {"c"}); exec.update(ctx, KeyMod {L"c"});
REQUIRE(1 == cmd0->execute_count); REQUIRE(1 == cmd0->execute_count);
REQUIRE(1 == cmd1->execute_count); REQUIRE(1 == cmd1->execute_count);

View File

@ -9,7 +9,7 @@ public:
explicit ShortcutTest() {} explicit ShortcutTest() {}
virtual ~ShortcutTest() {} virtual ~ShortcutTest() {}
void test_to_string(std::string const& oracle, void test_to_string(std::wstring const& oracle,
std::vector<KeyMod> keymods) std::vector<KeyMod> keymods)
{ {
Shortcut sc; Shortcut sc;
@ -19,16 +19,15 @@ public:
sc.push(km); sc.push(km);
} }
INFO("Expected '" << oracle << "' got '" << sc.string() << "'"); REQUIRE(oracle == sc.wstring());
REQUIRE(oracle == sc.string());
} }
void test_from_string(std::string const& oracle, void test_from_string(std::wstring const& oracle,
std::string const& shortcut) std::wstring const& shortcut)
{ {
Shortcut sc { shortcut }; Shortcut sc { shortcut };
INFO("Expected '" << oracle << "' got '" << sc.string() << "'");
REQUIRE(oracle == sc.string()); REQUIRE(oracle == sc.wstring());
} }
protected: protected:
@ -36,71 +35,71 @@ protected:
TEST_CASE_METHOD(ShortcutTest, "Shortcut_string") TEST_CASE_METHOD(ShortcutTest, "Shortcut_string")
{ {
test_to_string("f", {KeyMod {'f'}}); test_to_string(L"f", {KeyMod {'f'}});
test_to_string("a b c", { test_to_string(L"a b c", {
KeyMod {'a'}, KeyMod {L'a'},
KeyMod {'b'}, KeyMod {L'b'},
KeyMod {'c'}, KeyMod {L'c'},
}); });
test_to_string("C-g", {KeyMod {'g', {MOD_LCTRL}}}); test_to_string(L"C-g", {KeyMod {L'g', {KM_MOD_LCTRL}}});
test_to_string("A-g", {KeyMod {'g', {MOD_ALT}}}); test_to_string(L"A-g", {KeyMod {L'g', {KM_MOD_ALT}}});
test_to_string("C-A-g", {KeyMod {'g', {MOD_LCTRL, MOD_ALT}}}); test_to_string(L"C-A-g", {KeyMod {L'g', {KM_MOD_LCTRL, KM_MOD_ALT}}});
test_to_string("A-a C-A-b C-c d", { test_to_string(L"A-a C-A-b C-c d", {
KeyMod {'a', {MOD_ALT}}, KeyMod {L'a', {KM_MOD_ALT}},
KeyMod {'b', {MOD_LCTRL, MOD_ALT}}, KeyMod {L'b', {KM_MOD_LCTRL, KM_MOD_ALT}},
KeyMod {'c', {MOD_LCTRL}}, KeyMod {L'c', {KM_MOD_LCTRL}},
KeyMod {'d'}, KeyMod {L'd'},
}); });
test_to_string("UP", { test_to_string(L"UP", {
KeyMod::key(KEY_UP) KeyMod::key(KM_KEY_UP)
}); });
test_to_string("DOWN", { test_to_string(L"DOWN", {
KeyMod::key(KEY_DOWN) KeyMod::key(KM_KEY_DOWN)
}); });
test_to_string("LEFT", { test_to_string(L"LEFT", {
KeyMod::key(KEY_LEFT) KeyMod::key(KM_KEY_LEFT)
}); });
test_to_string("RIGHT", { test_to_string(L"RIGHT", {
KeyMod::key(KEY_RIGHT) KeyMod::key(KM_KEY_RIGHT)
}); });
test_to_string("C-UP", { test_to_string(L"C-UP", {
KeyMod::key(KEY_UP, {MOD_LCTRL}) KeyMod::key(KM_KEY_UP, {KM_MOD_LCTRL})
}); });
test_to_string("A-LEFT", { test_to_string(L"A-LEFT", {
KeyMod::key(KEY_LEFT, {MOD_ALT}), KeyMod::key(KM_KEY_LEFT, {KM_MOD_ALT}),
}); });
test_to_string("C-A-RIGHT", { test_to_string(L"C-A-RIGHT", {
KeyMod::key(KEY_RIGHT, {MOD_LCTRL, MOD_ALT}), KeyMod::key(KM_KEY_RIGHT, {KM_MOD_LCTRL, KM_MOD_ALT}),
}); });
test_from_string("", ""); test_from_string(L"", L"");
test_from_string("a", "a"); test_from_string(L"a", L"a");
test_from_string("a", " a"); test_from_string(L"a", L" a");
test_from_string("a", "a "); test_from_string(L"a", L"a ");
test_from_string("C-a", "C-a"); test_from_string(L"C-a", L"C-a");
test_from_string("C-a", " C-a "); test_from_string(L"C-a", L" C-a ");
test_from_string("A-a", "A-a"); test_from_string(L"A-a", L"A-a");
test_from_string("A-a", " A-a "); test_from_string(L"A-a", L" A-a ");
test_from_string("C-A-a", " A-C-a "); test_from_string(L"C-A-a", L" A-C-a ");
test_from_string("C-A-a", " C-A-a "); test_from_string(L"C-A-a", L" C-A-a ");
test_from_string("C-A-a b C-c", " C-A-a b C-c"); test_from_string(L"C-A-a b C-c", L" C-A-a b C-c");
test_from_string("C-A-LEFT RIGHT C-UP", test_from_string(L"C-A-LEFT RIGHT C-UP",
" C-A-LEFT RIGHT C-UP"); L" C-A-LEFT RIGHT C-UP");
REQUIRE_THROWS_AS(Shortcut { "C-A-DUCK" }, invalid_shortcut_error); REQUIRE_THROWS_AS(Shortcut { L"C-A-DUCK" }, invalid_shortcut_error);
} }