✨ can now quit Pywiq with 'C-c C-c'.
parent
041ef9b9ed
commit
7b6f0b45f4
|
@ -1,4 +1,5 @@
|
|||
*~*
|
||||
*\#*
|
||||
build
|
||||
doc
|
||||
.cache
|
5
Makefile
5
Makefile
|
@ -1,4 +1,4 @@
|
|||
.PHONY: build tests
|
||||
.PHONY: build tests doc
|
||||
|
||||
build:
|
||||
meson setup build
|
||||
|
@ -9,3 +9,6 @@ tests: build
|
|||
|
||||
install: tests
|
||||
meson install -C build
|
||||
|
||||
doc:
|
||||
doxygen
|
||||
|
|
30
meson.build
30
meson.build
|
@ -9,8 +9,36 @@ project('pywiq',
|
|||
|
||||
pwq_lib = static_library('pywiq',
|
||||
sources: [
|
||||
# Terminal
|
||||
# --------
|
||||
'src/Term.cpp',
|
||||
|
||||
# Control
|
||||
# -------
|
||||
'src/KeyMod.cpp',
|
||||
'src/Mode.cpp',
|
||||
'src/Action.cpp',
|
||||
'src/Binding.cpp',
|
||||
'src/Executor.cpp',
|
||||
'src/Pywiq.cpp',
|
||||
'src/Controller.cpp',
|
||||
|
||||
# View
|
||||
# ----
|
||||
|
||||
# Command
|
||||
# -------
|
||||
|
||||
# Scripting
|
||||
# ---------
|
||||
|
||||
# Actions
|
||||
# -------
|
||||
'src/actions/Quit.cpp',
|
||||
|
||||
],
|
||||
dependencies: [
|
||||
dependency('ncursesw')
|
||||
])
|
||||
|
||||
pwq_dep = declare_dependency(
|
||||
|
@ -30,6 +58,8 @@ executable('pwq',
|
|||
executable('pwq-tests',
|
||||
sources: [
|
||||
'tests/main.cpp',
|
||||
'tests/KeyMod.cpp',
|
||||
'tests/Executor.cpp',
|
||||
],
|
||||
dependencies: [
|
||||
pwq_dep,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#include "Action.hpp"
|
||||
#include "Mode.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ Action::Action(Mode& mode,
|
||||
std::string const& name,
|
||||
std::string const& help)
|
||||
: m_mode { mode }
|
||||
, m_name { name }
|
||||
, m_help { help }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Action::~Action()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef pwq_ACTION_HPP
|
||||
#define pwq_ACTION_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
class Mode;
|
||||
|
||||
/**
|
||||
* A process that can be executed from a command or a keystroke.
|
||||
**/
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
explicit Action(Mode& mode,
|
||||
std::string const& name,
|
||||
std::string const& help);
|
||||
|
||||
virtual ~Action();
|
||||
|
||||
std::string name() const { return m_name; }
|
||||
std::string help() const { return m_help; }
|
||||
|
||||
virtual void execute() {}
|
||||
virtual void undo() {}
|
||||
|
||||
|
||||
protected:
|
||||
Mode& m_mode;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_help;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#include "Binding.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ Binding::Binding(std::vector<KeyMod> const& keymods,
|
||||
std::weak_ptr<Action> action)
|
||||
: m_keymods { keymods}
|
||||
, m_action { action }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Binding::~Binding()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef pwq_BINDING_HPP
|
||||
#define pwq_BINDING_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
#include "KeyMod.hpp"
|
||||
#include "Action.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/**
|
||||
* Represente the association of a sequence of keys and
|
||||
* an action.
|
||||
* @see KeyMod
|
||||
* @see Action
|
||||
**/
|
||||
class Binding
|
||||
{
|
||||
public:
|
||||
explicit Binding(std::vector<KeyMod> const& keymods,
|
||||
std::weak_ptr<Action> action);
|
||||
virtual ~Binding();
|
||||
|
||||
size_t size() const { return m_keymods.size(); }
|
||||
std::vector<KeyMod> const& keymods() const { return m_keymods; }
|
||||
std::weak_ptr<Action> action() const { return m_action; }
|
||||
|
||||
private:
|
||||
std::vector<KeyMod> m_keymods;
|
||||
std::weak_ptr<Action> m_action;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
#include "Controller.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ Controller::Controller()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Controller::~Controller()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef pwq_CONTROLLER_HPP
|
||||
#define pwq_CONTROLLER_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
#include "KeyMod.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/**
|
||||
* Read keys from input.
|
||||
* @see KeyMod
|
||||
**/
|
||||
class Controller
|
||||
{
|
||||
public:
|
||||
explicit Controller();
|
||||
virtual ~Controller();
|
||||
|
||||
virtual KeyMod wait_keymod() = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
#include "Executor.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ Executor::Executor()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Executor::~Executor()
|
||||
{
|
||||
}
|
||||
|
||||
void Executor::bind(Binding const& binding)
|
||||
{
|
||||
m_entries.push_back({binding, 0});
|
||||
}
|
||||
|
||||
void Executor::update(KeyMod const& keymod)
|
||||
{
|
||||
for (auto& entry: m_entries)
|
||||
{
|
||||
size_t progress = entry.progression;
|
||||
|
||||
if (entry.binding.keymods()[progress].equals(keymod))
|
||||
{
|
||||
entry.progression++;
|
||||
|
||||
if (entry.progression >= entry.binding.size())
|
||||
{
|
||||
entry.progression = 0;
|
||||
|
||||
if (auto action = entry.binding.action().lock();
|
||||
action)
|
||||
{
|
||||
action->execute();
|
||||
}
|
||||
|
||||
if (entry.binding.keymods()[0].equals(keymod))
|
||||
{
|
||||
entry.progression++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.progression = 0;
|
||||
|
||||
if (entry.binding.keymods()[0].equals(keymod))
|
||||
{
|
||||
entry.progression++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef pwq_EXECUTOR_HPP
|
||||
#define pwq_EXECUTOR_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
#include "Binding.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
struct ExecEntry {
|
||||
Binding binding;
|
||||
size_t progression = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update key bindings and execute corresponding actions.
|
||||
* @see Action
|
||||
* @see Binding
|
||||
* @see KeyMod
|
||||
**/
|
||||
class Executor
|
||||
{
|
||||
public:
|
||||
explicit Executor();
|
||||
virtual ~Executor();
|
||||
|
||||
void bind(Binding const& binding);
|
||||
void update(KeyMod const& keymod);
|
||||
|
||||
private:
|
||||
std::vector<ExecEntry> m_entries;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,125 @@
|
|||
#include "KeyMod.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ KeyMod::KeyMod(std::wstring const& repr)
|
||||
{
|
||||
std::wstring buffer;
|
||||
std::unordered_map<std::wstring, ModType> mod_types = {
|
||||
{L"C", PWQ_MOD_CTRL},
|
||||
{L"A", PWQ_MOD_ALT}
|
||||
};
|
||||
|
||||
for (char c: repr)
|
||||
{
|
||||
if (c == '-')
|
||||
{
|
||||
m_mods.push_back(mod_types.at(buffer));
|
||||
buffer.clear();
|
||||
}
|
||||
else if (!std::isspace(c))
|
||||
{
|
||||
buffer.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.size() > 1)
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
for (auto name: KeyTypeStr)
|
||||
{
|
||||
std::string name_str = name;
|
||||
std::wstring wname (std::begin(name_str), std::end(name_str));
|
||||
|
||||
if (wname == L"PWQ_KEY_" + buffer)
|
||||
{
|
||||
m_key = (KeyType) value;
|
||||
break;
|
||||
}
|
||||
|
||||
value++;
|
||||
}
|
||||
|
||||
m_text = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_key = PWQ_KEY_TEXT;
|
||||
m_text = buffer[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*explicit*/ KeyMod::KeyMod(KeyType key)
|
||||
: KeyMod(key, std::nullopt)
|
||||
{
|
||||
}
|
||||
|
||||
/*explicit*/ KeyMod::KeyMod(KeyType key, std::optional<wchar_t> text)
|
||||
: m_key { key }
|
||||
, m_text { text }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ KeyMod::~KeyMod()
|
||||
{
|
||||
}
|
||||
|
||||
KeyMod& KeyMod::mod(ModType type)
|
||||
{
|
||||
m_mods.push_back(type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool KeyMod::has_mod(ModType type) const
|
||||
{
|
||||
return std::find(std::begin(m_mods),
|
||||
std::end(m_mods),
|
||||
type)
|
||||
!= std::end(m_mods);
|
||||
}
|
||||
|
||||
bool KeyMod::equals(KeyMod const& rhs) const
|
||||
{
|
||||
for (auto mod: m_mods)
|
||||
{
|
||||
if (!rhs.has_mod(mod))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return m_key == rhs.m_key
|
||||
&& m_text == rhs.m_text
|
||||
&& m_mods.size() == rhs.m_mods.size();
|
||||
}
|
||||
|
||||
std::wstring KeyMod::string() const
|
||||
{
|
||||
std::wstringstream ss;
|
||||
|
||||
if (has_mod(PWQ_MOD_CTRL))
|
||||
{
|
||||
ss << L"C-";
|
||||
}
|
||||
|
||||
if (has_mod(PWQ_MOD_ALT))
|
||||
{
|
||||
ss << L"A-";
|
||||
}
|
||||
|
||||
if (m_key == PWQ_KEY_TEXT)
|
||||
{
|
||||
ss << std::wstring(1, *m_text);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string name_str = KeyTypeStr[m_key] + strlen("PWQ_KEY_");
|
||||
std::wstring name (std::begin(name_str), std::end(name_str));
|
||||
ss << name;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef pwq_KEYMOD_HPP
|
||||
#define pwq_KEYMOD_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
|
||||
#define PWQ_KEY_TYPES(G) \
|
||||
G(PWQ_KEY_TEXT), \
|
||||
G(PWQ_KEY_UP), \
|
||||
G(PWQ_KEY_DOWN), \
|
||||
G(PWQ_KEY_LEFT), \
|
||||
G(PWQ_KEY_RIGHT),
|
||||
|
||||
#define PWQ_MOD_TYPES(G) \
|
||||
G(PWQ_MOD_CTRL), \
|
||||
G(PWQ_MOD_ALT)
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
PWQ_ENUM(KeyType, PWQ_KEY_TYPES);
|
||||
PWQ_ENUM(ModType, PWQ_MOD_TYPES);
|
||||
|
||||
/**
|
||||
* Represents the combination of a keystroke with a modifier key
|
||||
* like control or alt.
|
||||
*
|
||||
**/
|
||||
class KeyMod
|
||||
{
|
||||
public:
|
||||
explicit KeyMod(std::wstring const& repr);
|
||||
explicit KeyMod(KeyType key);
|
||||
explicit KeyMod(KeyType key, std::optional<wchar_t> text);
|
||||
|
||||
virtual ~KeyMod();
|
||||
|
||||
KeyType key() const { return m_key; }
|
||||
std::vector<ModType> const& mods() const { return m_mods; }
|
||||
std::optional<wchar_t> text() const { return m_text; }
|
||||
|
||||
KeyMod& mod(ModType type);
|
||||
bool has_mod(ModType type) const;
|
||||
|
||||
bool equals(KeyMod const& rhs) const;
|
||||
|
||||
std::wstring string() const;
|
||||
|
||||
private:
|
||||
KeyType m_key;
|
||||
std::optional<wchar_t> m_text;
|
||||
std::vector<ModType> m_mods;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#include "Mode.hpp"
|
||||
#include "Action.hpp"
|
||||
#include "Binding.hpp"
|
||||
#include "Executor.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ Mode::Mode(std::string const& name,
|
||||
Pywiq& pywiq,
|
||||
std::shared_ptr<Executor> executor)
|
||||
: m_name { name }
|
||||
, m_pywiq { pywiq }
|
||||
, m_executor { executor }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Mode::~Mode()
|
||||
{
|
||||
}
|
||||
|
||||
Pywiq& Mode::pywiq()
|
||||
{
|
||||
return m_pywiq;
|
||||
}
|
||||
|
||||
std::weak_ptr<Executor> Mode::executor()
|
||||
{
|
||||
return m_executor;
|
||||
}
|
||||
|
||||
void Mode::bind_action(std::vector<KeyMod> keys,
|
||||
std::shared_ptr<Action> action)
|
||||
{
|
||||
m_actions.push_back(action);
|
||||
|
||||
Binding binding {keys, action};
|
||||
m_executor->bind(binding);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef pwq_MODE_HPP
|
||||
#define pwq_MODE_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
#include "src/KeyMod.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
class Executor;
|
||||
class Pywiq;
|
||||
class Action;
|
||||
|
||||
/**
|
||||
* A configuration with a specific view and controls.
|
||||
* eg. Like Vi mode.
|
||||
**/
|
||||
class Mode
|
||||
{
|
||||
public:
|
||||
explicit Mode(std::string const& name,
|
||||
Pywiq& pywiq,
|
||||
std::shared_ptr<Executor> executor);
|
||||
virtual ~Mode();
|
||||
|
||||
Pywiq& pywiq();
|
||||
|
||||
std::weak_ptr<Executor> executor();
|
||||
|
||||
void bind_action(std::vector<KeyMod> keys,
|
||||
std::shared_ptr<Action> action);
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> make_action();
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
Pywiq& m_pywiq;
|
||||
std::shared_ptr<Executor> m_executor;
|
||||
std::vector<std::shared_ptr<Action>> m_actions;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> Mode::make_action()
|
||||
{
|
||||
auto action = std::make_shared<T>(*this);
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
#include "Pywiq.hpp"
|
||||
#include "Controller.hpp"
|
||||
#include "KeyMod.hpp"
|
||||
#include "Mode.hpp"
|
||||
#include "Executor.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ Pywiq::Pywiq(Controller& controller)
|
||||
: m_controller { controller }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Pywiq::~Pywiq()
|
||||
{
|
||||
}
|
||||
|
||||
void Pywiq::add_mode(std::shared_ptr<Mode> mode)
|
||||
{
|
||||
m_modes.push_back(mode);
|
||||
}
|
||||
|
||||
bool Pywiq::exec()
|
||||
{
|
||||
KeyMod keymod = m_controller.wait_keymod();
|
||||
|
||||
for (auto mode: m_modes)
|
||||
{
|
||||
if (auto executor=mode->executor().lock();
|
||||
executor)
|
||||
{
|
||||
executor->update(keymod);
|
||||
}
|
||||
}
|
||||
|
||||
return m_running;
|
||||
}
|
||||
|
||||
void Pywiq::quit()
|
||||
{
|
||||
m_running = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef pwq_PYWIQ_HPP
|
||||
#define pwq_PYWIQ_HPP
|
||||
|
||||
#include "commons.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
class Mode;
|
||||
class Controller;
|
||||
|
||||
/**
|
||||
* Manage the editor modes.
|
||||
**/
|
||||
class Pywiq
|
||||
{
|
||||
public:
|
||||
explicit Pywiq(Controller& controller);
|
||||
virtual ~Pywiq();
|
||||
|
||||
void add_mode(std::shared_ptr<Mode> mode);
|
||||
|
||||
bool exec();
|
||||
void quit();
|
||||
|
||||
private:
|
||||
Controller& m_controller;
|
||||
std::vector<std::shared_ptr<Mode>> m_modes;
|
||||
bool m_running = true;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,84 @@
|
|||
#include "Term.hpp"
|
||||
#include <curses.h>
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/*explicit*/ Term::Term()
|
||||
{
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
initscr();
|
||||
raw();
|
||||
noecho();
|
||||
keypad(stdscr, TRUE);
|
||||
start_color();
|
||||
curs_set(0);
|
||||
|
||||
m_mapping = {
|
||||
{"KEY_UP", PWQ_KEY_UP},
|
||||
{"KEY_DOWN", PWQ_KEY_DOWN},
|
||||
{"KEY_LEFT", PWQ_KEY_LEFT},
|
||||
{"KEY_RIGHT", PWQ_KEY_RIGHT},
|
||||
};
|
||||
}
|
||||
|
||||
/*virtual*/ Term::~Term()
|
||||
{
|
||||
endwin();
|
||||
}
|
||||
|
||||
KeyMod Term::wait_keymod() /*override*/
|
||||
{
|
||||
wint_t c;
|
||||
get_wch(&c);
|
||||
bool alt = false;
|
||||
bool ctrl = false;
|
||||
|
||||
// Detect ALT
|
||||
if (c == 27)
|
||||
{
|
||||
get_wch(&c);
|
||||
alt = true;
|
||||
}
|
||||
|
||||
// Detect CTRL
|
||||
{
|
||||
auto kname = keyname(c);
|
||||
ctrl = kname[0] == '^';
|
||||
|
||||
if (ctrl)
|
||||
{
|
||||
c = std::tolower(kname[1]);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<KeyMod> km;
|
||||
|
||||
// Detect Key Type
|
||||
{
|
||||
auto kname = keyname(c);
|
||||
|
||||
if (auto itr=m_mapping.find(kname);
|
||||
itr != std::end(m_mapping))
|
||||
{
|
||||
km = KeyMod(itr->second);
|
||||
}
|
||||
else if(strlen(kname) == 1)
|
||||
{
|
||||
km = KeyMod(PWQ_KEY_TEXT, c);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctrl)
|
||||
{
|
||||
km->mod(PWQ_MOD_CTRL);
|
||||
}
|
||||
|
||||
if (alt)
|
||||
{
|
||||
km->mod(PWQ_MOD_ALT);
|
||||
}
|
||||
|
||||
return *km;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef pwq_TERM_HPP
|
||||
#define pwq_TERM_HPP
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "commons.hpp"
|
||||
#include "Controller.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
/**
|
||||
* Abstraction over NCurses.
|
||||
* @see Controller
|
||||
**/
|
||||
class Term: public Controller
|
||||
{
|
||||
public:
|
||||
explicit Term();
|
||||
virtual ~Term();
|
||||
|
||||
KeyMod wait_keymod() override;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, KeyType> m_mapping;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#include "Quit.hpp"
|
||||
#include "../Pywiq.hpp"
|
||||
#include "../Mode.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
namespace actions
|
||||
{
|
||||
/*explicit*/ Quit::Quit(Mode& mode)
|
||||
: Action(mode, "quit", "quit Pywiq")
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Quit::~Quit()
|
||||
{
|
||||
}
|
||||
|
||||
void Quit::execute() /*override*/
|
||||
{
|
||||
m_mode.pywiq().quit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef pwq_actions_QUIT_HPP
|
||||
#define pwq_actions_QUIT_HPP
|
||||
|
||||
#include "../commons.hpp"
|
||||
#include "../Action.hpp"
|
||||
|
||||
namespace pwq
|
||||
{
|
||||
namespace actions
|
||||
{
|
||||
class Quit: public Action
|
||||
{
|
||||
public:
|
||||
explicit Quit(Mode& mode);
|
||||
virtual ~Quit();
|
||||
|
||||
void execute() override;
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef pwq_COMMONS_HPP
|
||||
#define pwq_COMMONS_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
|
||||
#define PWQ_GEN_ENUM(X) X
|
||||
#define PWQ_GEN_STRING(X) #X
|
||||
|
||||
#define PWQ_ENUM(PREFIX, TYPES) \
|
||||
enum PREFIX { TYPES(PWQ_GEN_ENUM) }; \
|
||||
constexpr char const* PREFIX ## Str [] { TYPES(PWQ_GEN_STRING) }
|
||||
|
||||
#define PWQ_ERROR(NAME) \
|
||||
struct NAME : public std::runtime_error { \
|
||||
NAME (std::string const& what): std::runtime_error(what) {} \
|
||||
}
|
||||
|
||||
#endif
|
25
src/main.cpp
25
src/main.cpp
|
@ -1,6 +1,31 @@
|
|||
#include <iostream>
|
||||
#include "KeyMod.hpp"
|
||||
#include "Pywiq.hpp"
|
||||
#include "Mode.hpp"
|
||||
#include "Executor.hpp"
|
||||
#include "Term.hpp"
|
||||
#include "actions/Quit.hpp"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
pwq::Term term;
|
||||
pwq::Pywiq pywiq {term};
|
||||
|
||||
auto executor = std::make_shared<pwq::Executor>();
|
||||
auto default_mode = std::make_shared<pwq::Mode>("default",
|
||||
pywiq,
|
||||
executor);
|
||||
|
||||
auto quit = default_mode->make_action<pwq::actions::Quit>();
|
||||
|
||||
default_mode->bind_action({pwq::KeyMod(L"C-c"),
|
||||
pwq::KeyMod(L"C-c")}, quit);
|
||||
|
||||
pywiq.add_mode(default_mode);
|
||||
|
||||
while (pywiq.exec())
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
#include <catch2/catch.hpp>
|
||||
#include "../src/Mode.hpp"
|
||||
#include "../src/Action.hpp"
|
||||
#include "../src/Binding.hpp"
|
||||
#include "../src/Executor.hpp"
|
||||
#include "../src/Controller.hpp"
|
||||
#include "../src/Pywiq.hpp"
|
||||
|
||||
using namespace pwq;
|
||||
|
||||
struct MockController : public Controller {
|
||||
virtual KeyMod wait_keymod() override { return KeyMod(L"a"); }
|
||||
};
|
||||
|
||||
class ExecutorTest
|
||||
{
|
||||
public:
|
||||
explicit ExecutorTest() {}
|
||||
virtual ~ExecutorTest() {}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Executor> exec;
|
||||
MockController m_ctrl;
|
||||
Pywiq m_pywiq = Pywiq(m_ctrl);
|
||||
Mode m_mode = Mode("executor_test", m_pywiq, exec);
|
||||
};
|
||||
|
||||
struct ActionMock: public Action
|
||||
{
|
||||
int execute_counter = 0;
|
||||
|
||||
ActionMock(Mode& mode)
|
||||
: Action(mode, "mock_action", "mock_doc")
|
||||
{
|
||||
}
|
||||
|
||||
void execute() override
|
||||
{
|
||||
execute_counter++;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_one_action")
|
||||
{
|
||||
auto action = std::make_shared<ActionMock>(m_mode);
|
||||
Executor exec;
|
||||
|
||||
exec.bind(Binding(std::vector<KeyMod>{KeyMod(L"C-a"),
|
||||
KeyMod(L"C-c")},
|
||||
action));
|
||||
|
||||
REQUIRE(0 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"C-a"));
|
||||
REQUIRE(0 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"C-c"));
|
||||
REQUIRE(1 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"C-c"));
|
||||
REQUIRE(1 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"C-a"));
|
||||
REQUIRE(1 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"C-c"));
|
||||
REQUIRE(2 == action->execute_counter);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_two_actions")
|
||||
{
|
||||
auto first_action = std::make_shared<ActionMock>(m_mode);
|
||||
auto second_action = std::make_shared<ActionMock>(m_mode);
|
||||
|
||||
Executor exec;
|
||||
|
||||
exec.bind(Binding(std::vector<KeyMod>{KeyMod(L"A-g"),
|
||||
KeyMod(L"p")},
|
||||
first_action));
|
||||
|
||||
exec.bind(Binding(std::vector<KeyMod>{KeyMod(L"z"),
|
||||
KeyMod(L"z")},
|
||||
second_action));
|
||||
|
||||
REQUIRE(0 == first_action->execute_counter);
|
||||
REQUIRE(0 == second_action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"A-g"));
|
||||
REQUIRE(0 == first_action->execute_counter);
|
||||
REQUIRE(0 == second_action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"z"));
|
||||
REQUIRE(0 == first_action->execute_counter);
|
||||
REQUIRE(0 == second_action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"z"));
|
||||
REQUIRE(0 == first_action->execute_counter);
|
||||
REQUIRE(1 == second_action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"p"));
|
||||
REQUIRE(0 == first_action->execute_counter);
|
||||
REQUIRE(1 == second_action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"A-g"));
|
||||
REQUIRE(0 == first_action->execute_counter);
|
||||
REQUIRE(1 == second_action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"p"));
|
||||
REQUIRE(1 == first_action->execute_counter);
|
||||
REQUIRE(1 == second_action->execute_counter);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_failed_but_start_another")
|
||||
{
|
||||
auto action = std::make_shared<ActionMock>(m_mode);
|
||||
Executor exec;
|
||||
|
||||
exec.bind(Binding(std::vector<KeyMod>{KeyMod(L"a"),
|
||||
KeyMod(L"b"),
|
||||
KeyMod(L"c")},
|
||||
action));
|
||||
|
||||
REQUIRE(0 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"a"));
|
||||
REQUIRE(0 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"a"));
|
||||
REQUIRE(0 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"b"));
|
||||
REQUIRE(0 == action->execute_counter);
|
||||
|
||||
exec.update(KeyMod(L"c"));
|
||||
REQUIRE(1 == action->execute_counter);
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
#include <catch2/catch.hpp>
|
||||
#include "../src/KeyMod.hpp"
|
||||
|
||||
class KeyModTest
|
||||
{
|
||||
public:
|
||||
explicit KeyModTest() {}
|
||||
virtual ~KeyModTest() {}
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(KeyModTest, "KeyMod_to_string")
|
||||
{
|
||||
SECTION("text, no mod")
|
||||
{
|
||||
pwq::KeyMod km {pwq::PWQ_KEY_TEXT, 'h'};
|
||||
REQUIRE(L"h" == km.string());
|
||||
}
|
||||
|
||||
SECTION("text, ctrl mod")
|
||||
{
|
||||
auto km = pwq::KeyMod(pwq::PWQ_KEY_TEXT, 'h')
|
||||
.mod(pwq::PWQ_MOD_CTRL);
|
||||
|
||||
REQUIRE(L"C-h" == km.string());
|
||||
}
|
||||
|
||||
SECTION("text, alt mod")
|
||||
{
|
||||
auto km = pwq::KeyMod(pwq::PWQ_KEY_TEXT, 'h')
|
||||
.mod(pwq::PWQ_MOD_ALT);
|
||||
|
||||
REQUIRE(L"A-h" == km.string());
|
||||
}
|
||||
|
||||
SECTION("text, ctrl + alt mod")
|
||||
{
|
||||
auto km = pwq::KeyMod(pwq::PWQ_KEY_TEXT, 'h')
|
||||
.mod(pwq::PWQ_MOD_ALT)
|
||||
.mod(pwq::PWQ_MOD_CTRL);
|
||||
|
||||
|
||||
REQUIRE(L"C-A-h" == km.string());
|
||||
}
|
||||
|
||||
SECTION("no text, no mod")
|
||||
{
|
||||
pwq::KeyMod km {pwq::PWQ_KEY_UP};
|
||||
REQUIRE(L"UP" == km.string());
|
||||
}
|
||||
|
||||
SECTION("no text, ctrl mod")
|
||||
{
|
||||
auto km = pwq::KeyMod(pwq::PWQ_KEY_DOWN)
|
||||
.mod(pwq::PWQ_MOD_CTRL);
|
||||
|
||||
REQUIRE(L"C-DOWN" == km.string());
|
||||
}
|
||||
|
||||
SECTION("no text, alt mod")
|
||||
{
|
||||
auto km = pwq::KeyMod(pwq::PWQ_KEY_LEFT)
|
||||
.mod(pwq::PWQ_MOD_ALT);
|
||||
|
||||
REQUIRE(L"A-LEFT" == km.string());
|
||||
}
|
||||
|
||||
SECTION("no text, alt mod")
|
||||
{
|
||||
auto km = pwq::KeyMod(pwq::PWQ_KEY_RIGHT)
|
||||
.mod(pwq::PWQ_MOD_CTRL)
|
||||
.mod(pwq::PWQ_MOD_ALT);
|
||||
|
||||
REQUIRE(L"C-A-RIGHT" == km.string());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(KeyModTest, "KeyMod_from_string")
|
||||
{
|
||||
REQUIRE(L"w" == pwq::KeyMod(L"w").string());
|
||||
REQUIRE(L"t" == pwq::KeyMod(L" t ").string());
|
||||
REQUIRE(L"C-w" == pwq::KeyMod(L"C-w ").string());
|
||||
REQUIRE(L"A-w" == pwq::KeyMod(L" A-w").string());
|
||||
|
||||
REQUIRE(L"C-A-w" == pwq::KeyMod(L"C-A-w").string());
|
||||
REQUIRE(L"C-A-w" == pwq::KeyMod(L" A-C-w ").string());
|
||||
|
||||
REQUIRE(L"UP" == pwq::KeyMod(L" UP ").string());
|
||||
|
||||
REQUIRE(L"C-DOWN" == pwq::KeyMod(L"C-DOWN ").string());
|
||||
REQUIRE(L"A-LEFT" == pwq::KeyMod(L" A-LEFT ").string());
|
||||
|
||||
REQUIRE(L"C-A-RIGHT" == pwq::KeyMod(L"A-C-RIGHT").string());
|
||||
|
||||
REQUIRE(pwq::PWQ_KEY_TEXT == pwq::KeyMod(L" U ").key());
|
||||
REQUIRE(pwq::PWQ_KEY_UP == pwq::KeyMod(L" UP ").key());
|
||||
REQUIRE(pwq::PWQ_KEY_DOWN == pwq::KeyMod(L" DOWN ").key());
|
||||
REQUIRE(pwq::PWQ_KEY_LEFT == pwq::KeyMod(L" LEFT ").key());
|
||||
REQUIRE(pwq::PWQ_KEY_RIGHT == pwq::KeyMod(L" RIGHT ").key());
|
||||
}
|
Loading…
Reference in New Issue