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',
sources: [
'src/core/KeyMod.cpp',
'src/core/Shortcut.cpp',
'src/core/Binding.cpp',
'src/core/Command.cpp',
'src/core/Context.cpp',
'src/core/Binding.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: [
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
{
/*explicit*/ Context::Context()
/*explicit*/ Context::Context(Tiwiq& tiwiq)
: m_tiwiq { tiwiq }
{
}

View File

@ -1,6 +1,8 @@
#ifndef twq_core_CONTEXT_HPP
#define twq_core_CONTEXT_HPP
#include "Tiwiq.hpp"
namespace twq
{
namespace core
@ -8,10 +10,13 @@ namespace twq
class Context
{
public:
explicit Context();
explicit Context(Tiwiq& tiwiq);
virtual ~Context();
Tiwiq& tiwiq() const { return m_tiwiq; }
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 };
}
/*explicit*/ KeyMod::KeyMod(std::string const& repr)
/*explicit*/ KeyMod::KeyMod(std::wstring const& repr)
{
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;
@ -30,7 +30,7 @@ namespace twq
k--;
}
std::string value;
std::wstring value;
while (k >= 0
&& !std::isspace(repr[k])
@ -42,16 +42,17 @@ namespace twq
if (value.size() == 1)
{
m_key_type = KEY_TEXT;
m_key_type = KM_KEY_TEXT;
m_mods = mods;
m_text = value.front();
return;
}
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)
{
@ -62,15 +63,16 @@ namespace twq
}
throw invalid_keymod_error {"cannot convert '"
+ repr
+ std::string(std::begin(repr),
std::end(repr))
+ "' to keymod."};
}
}
/*explicit*/ KeyMod::KeyMod(char text,
/*explicit*/ KeyMod::KeyMod(wchar_t text,
std::vector<ModType> const& mods)
: m_key_type { KEY_TEXT }
: m_key_type { KM_KEY_TEXT }
, m_mods { mods }
, m_text { text }
{
@ -78,7 +80,7 @@ namespace twq
/*explicit*/ KeyMod::KeyMod(KeyType key_type,
std::vector<ModType> const& mods,
std::optional<char> text)
std::optional<wchar_t> text)
: m_key_type { key_type }
, m_mods { mods }
, m_text { text }
@ -103,7 +105,7 @@ namespace twq
return false;
}
if (m_key_type == KEY_TEXT
if (m_key_type == KM_KEY_TEXT
&& *m_text != *rhs.m_text)
{
return false;
@ -126,27 +128,27 @@ namespace twq
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-";
}
if (has_mod(MOD_ALT))
if (has_mod(KM_MOD_ALT))
{
ss << "A-";
}
if (m_key_type == KEY_TEXT)
if (m_key_type == KM_KEY_TEXT)
{
ss << *m_text;
}
else
{
ss << (KeyTypeStr[m_key_type] + strlen("KEY_"));
ss << (KeyTypeStr[m_key_type] + strlen("KM_KEY_"));
}
return ss.str();

View File

@ -4,17 +4,17 @@
#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),
G(KM_KEY_TEXT), \
G(KM_KEY_UP), \
G(KM_KEY_DOWN), \
G(KM_KEY_LEFT), \
G(KM_KEY_RIGHT), \
G(KM_KEY_COUNT),
#define MOD_TYPES(G)\
G(MOD_LCTRL), \
G(MOD_ALT), \
G(MOD_COUNT),
G(KM_MOD_LCTRL), \
G(KM_MOD_ALT), \
G(KM_MOD_COUNT),
namespace twq
{
@ -31,31 +31,31 @@ namespace twq
static KeyMod key(KeyType key_type,
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,
std::vector<ModType> const& mods,
std::optional<char> text);
std::optional<wchar_t> 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; }
std::optional<wchar_t> text() const { return m_text; }
bool has_mod(ModType mod_type) const;
bool equals(KeyMod const& rhs) const;
std::string string() const;
std::wstring wstring() const;
virtual ~KeyMod();
private:
KeyType m_key_type;
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;
for (size_t i=0; i<repr.size(); i++)
{
char c = repr[i];
wchar_t c = repr[i];
if (std::isspace(c))
{
@ -29,9 +29,11 @@ namespace twq
}
catch (invalid_keymod_error const& err)
{
throw invalid_shortcut_error {"cannot convert '"
+ repr
+ "' to shortcut"};
throw invalid_shortcut_error {
"cannot convert '"
+ std::string(std::begin(repr),
std::end(repr))
+ "' to shortcut"};
}
}
@ -53,7 +55,8 @@ namespace twq
catch (invalid_keymod_error const& err)
{
throw invalid_shortcut_error {"cannot convert '"
+ repr
+ std::string(std::begin(repr),
std::end(repr))
+ "' to shortcut"};
}
}
@ -77,15 +80,15 @@ namespace twq
m_keymods.push_back(keymod);
}
std::string Shortcut::string() const
std::wstring Shortcut::wstring() const
{
std::stringstream ss;
std::string sep;
std::wstringstream ss;
std::wstring sep;
for (auto const& km: m_keymods)
{
ss << sep << km.string();
sep = " ";
ss << sep << km.wstring();
sep = L" ";
}
return ss.str();

View File

@ -13,14 +13,14 @@ namespace twq
{
public:
explicit Shortcut();
explicit Shortcut(std::string const& repr);
explicit Shortcut(std::wstring const& repr);
virtual ~Shortcut();
size_t count() const { return m_keymods.size(); }
KeyMod get(size_t index) const;
void push(KeyMod const& keymod);
std::string string() const;
std::wstring wstring() const;
private:
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 "core/Tiwiq.hpp"
#include "term/Term.hpp"
#include "plugins/CorePlugin.hpp"
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;
}

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

View File

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