diff --git a/src/client.cc b/src/client.cc index 4ad21115..c2bbddbe 100644 --- a/src/client.cc +++ b/src/client.cc @@ -62,12 +62,12 @@ public: : Client::Mode(client), m_callback(callback), m_choice_count(choices.size()), m_selected(0) { - client.menu_show(choices); + client.m_ui->menu_show(choices); } ~MenuMode() { - m_client.menu_hide(); + m_client.m_ui->menu_hide(); } void on_key(const Key& key, Context& context) override @@ -79,7 +79,7 @@ public: { if (++m_selected >= m_choice_count) m_selected = 0; - m_client.menu_select(m_selected); + m_client.m_ui->menu_select(m_selected); } if (key == Key::Up or key == Key::BackTab or @@ -88,7 +88,7 @@ public: { if (--m_selected < 0) m_selected = m_choice_count-1; - m_client.menu_select(m_selected); + m_client.m_ui->menu_select(m_selected); } if (key == Key(Key::Modifiers::Control, 'm')) { @@ -105,7 +105,7 @@ public: if (key.modifiers == Key::Modifiers::None and key.key >= '0' and key.key <= '9') { - m_client.menu_hide(); + m_client.m_ui->menu_hide(); // save callback as reset_normal_mode will delete this MenuCallback callback = std::move(m_callback); m_client.reset_normal_mode(); @@ -133,7 +133,7 @@ public: ~PromptMode() { - m_client.menu_hide(); + m_client.m_ui->menu_hide(); } void on_key(const Key& key, Context& context) override @@ -226,14 +226,14 @@ public: --m_cursor_pos; } - m_client.menu_hide(); + m_client.m_ui->menu_hide(); m_current_completion = -1; } else if (key == Key(Key::Modifiers::Control, 'r')) { - Key k = m_client.get_key(); + Key k = m_client.m_ui->get_key(); String reg = RegisterManager::instance()[k.key].values(context)[0]; - m_client.menu_hide(); + m_client.m_ui->menu_hide(); m_current_completion = -1; m_result = m_result.substr(0, m_cursor_pos) + reg + m_result.substr(m_cursor_pos, String::npos); @@ -251,8 +251,8 @@ public: if (candidates.empty()) return; - m_client.menu_hide(); - m_client.menu_show(candidates); + m_client.m_ui->menu_hide(); + m_client.m_ui->menu_show(candidates); String prefix = m_result.substr(m_completions.start, m_completions.end - m_completions.start); if (not contains(candidates, prefix)) @@ -265,14 +265,14 @@ public: m_current_completion = candidates.size()-1; const String& completion = candidates[m_current_completion]; - m_client.menu_select(m_current_completion); + m_client.m_ui->menu_select(m_current_completion); m_result = m_result.substr(0, m_completions.start) + completion + m_result.substr(m_cursor_pos); m_cursor_pos = m_completions.start + completion.length(); } else { - m_client.menu_hide(); + m_client.m_ui->menu_hide(); m_current_completion = -1; m_result = m_result.substr(0, m_cursor_pos) + key.key + m_result.substr(m_cursor_pos, String::npos); ++m_cursor_pos; @@ -381,8 +381,9 @@ private: IncrementalInserter m_inserter; }; -Client::Client() +Client::Client(UserInterface* ui) : m_mode(new NormalMode(*this)), + m_ui(ui), m_last_insert(IncrementalInserter::Mode::Insert, {}) { } @@ -430,10 +431,21 @@ void Client::on_next_key(KeyCallback callback) void Client::handle_next_input(Context& context) { - m_mode->on_key(get_key(), context); + m_mode->on_key(m_ui->get_key(), context); context.draw_ifn(); } +void Client::print_status(const String& status, CharCount cursor_pos) +{ + m_ui->print_status(status, cursor_pos); +} + +void Client::draw_window(Window& window) +{ + m_ui->draw_window(window); +} + + void Client::reset_normal_mode() { m_mode.reset(new NormalMode(*this)); diff --git a/src/client.hh b/src/client.hh index 4f1c5406..d7099112 100644 --- a/src/client.hh +++ b/src/client.hh @@ -5,7 +5,8 @@ #include "completion.hh" #include "utils.hh" #include "string.hh" -#include "editor.hh" +#include "window.hh" +#include "user_interface.hh" namespace Kakoune { @@ -21,11 +22,8 @@ using KeyCallback = std::function; class Client : public SafeCountable { public: - Client(); - virtual ~Client(); - virtual void draw_window(Window& window) = 0; - virtual void print_status(const String& status, - CharCount cursor_pos = -1) = 0; + Client(UserInterface* ui); + ~Client(); void insert(Editor& editor, IncrementalInserter::Mode mode); void repeat_last_insert(Editor& editor, Context& context); @@ -36,19 +34,19 @@ public: void menu(const memoryview& choices, MenuCallback callback); + void print_status(const String& status, CharCount cursor_pos = -1); + void draw_window(Window& window); + void on_next_key(KeyCallback callback); void handle_next_input(Context& context); private: - virtual void menu_show(const memoryview& choices) = 0; - virtual void menu_select(int selected) = 0; - virtual void menu_hide() = 0; - virtual Key get_key() = 0; - void reset_normal_mode(); std::pair> m_last_insert; + std::unique_ptr m_ui; + class Mode; std::unique_ptr m_mode; diff --git a/src/commands.cc b/src/commands.cc index 1303f4fd..c8445e15 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -649,10 +649,10 @@ private: char m_name; }; -class BatchClient : public Client +class BatchUI : public UserInterface { public: - BatchClient(const KeyList& keys) + BatchUI(const KeyList& keys) : m_keys(keys), m_pos(0) { } @@ -680,7 +680,8 @@ private: void exec_keys(const KeyList& keys, Context& context) { - BatchClient batch_client(keys); + BatchUI* batch_ui = new BatchUI(keys); + Client batch_client(batch_ui); RegisterRestorer quote('"', context); RegisterRestorer slash('/', context); @@ -689,7 +690,7 @@ void exec_keys(const KeyList& keys, Context& context) Context new_context(batch_client); new_context.change_editor(context.editor()); - while (batch_client.has_key_left()) + while (batch_ui->has_key_left()) batch_client.handle_next_input(new_context); } diff --git a/src/context.hh b/src/context.hh index 1107ac14..4e2c5479 100644 --- a/src/context.hh +++ b/src/context.hh @@ -7,6 +7,8 @@ namespace Kakoune { +// A Context is provided to all commands, it permits +// to access a client, window, editor or buffer if available. struct Context { Context() {} diff --git a/src/main.cc b/src/main.cc index 4534165e..8a771b7b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -475,7 +475,7 @@ int main(int argc, char* argv[]) try { - NCursesClient client; + Client client(new NCursesUI()); Context context(client); try diff --git a/src/ncurses.cc b/src/ncurses.cc index 930a22d5..2581cf48 100644 --- a/src/ncurses.cc +++ b/src/ncurses.cc @@ -70,7 +70,7 @@ static void set_color(Color fg_color, Color bg_color) } -NCursesClient::NCursesClient() +NCursesUI::NCursesUI() : m_menu(nullptr) { // setlocale(LC_ALL, ""); @@ -89,12 +89,12 @@ NCursesClient::NCursesClient() m_menu_bg = get_color_pair(Color::Cyan, Color::Blue); } -NCursesClient::~NCursesClient() +NCursesUI::~NCursesUI() { endwin(); } -void NCursesClient::draw_window(Window& window) +void NCursesUI::draw_window(Window& window) { int max_x,max_y; getmaxyx(stdscr, max_y, max_x); @@ -164,7 +164,7 @@ void NCursesClient::draw_window(Window& window) refresh(); } -Key NCursesClient::get_key() +Key NCursesUI::get_key() { const int c = getch(); @@ -196,7 +196,7 @@ Key NCursesClient::get_key() return c; } -void NCursesClient::print_status(const String& status, CharCount cursor_pos) +void NCursesUI::print_status(const String& status, CharCount cursor_pos) { int x,y; getmaxyx(stdscr, y, x); @@ -222,7 +222,7 @@ void NCursesClient::print_status(const String& status, CharCount cursor_pos) refresh(); } -void NCursesClient::menu_show(const memoryview& choices) +void NCursesUI::menu_show(const memoryview& choices) { assert(m_menu == nullptr); m_choices = std::vector(choices.begin(), choices.end()); @@ -252,7 +252,7 @@ void NCursesClient::menu_show(const memoryview& choices) refresh(); } -void NCursesClient::menu_select(int selected) +void NCursesUI::menu_select(int selected) { // last item in m_items is the nullptr, hence the - 1 if (selected >= 0 and selected < m_items.size() - 1) @@ -265,7 +265,7 @@ void NCursesClient::menu_select(int selected) refresh(); } -void NCursesClient::menu_hide() +void NCursesUI::menu_hide() { if (not m_menu) return; diff --git a/src/ncurses.hh b/src/ncurses.hh index 86766be6..b7a7c54b 100644 --- a/src/ncurses.hh +++ b/src/ncurses.hh @@ -4,19 +4,19 @@ #include #include -#include "client.hh" +#include "user_interface.hh" namespace Kakoune { -class NCursesClient : public Client +class NCursesUI : public UserInterface { public: - NCursesClient(); - ~NCursesClient(); + NCursesUI(); + ~NCursesUI(); - NCursesClient(const NCursesClient&) = delete; - NCursesClient& operator=(const NCursesClient&) = delete; + NCursesUI(const NCursesUI&) = delete; + NCursesUI& operator=(const NCursesUI&) = delete; void draw_window(Window& window) override; void print_status(const String& status, CharCount cursor_pos) override; diff --git a/src/user_interface.hh b/src/user_interface.hh new file mode 100644 index 00000000..c8fc14d7 --- /dev/null +++ b/src/user_interface.hh @@ -0,0 +1,28 @@ +#ifndef user_interface_hh_INCLUDED +#define user_interface_hh_INCLUDED + +#include "memoryview.hh" +#include "keys.hh" +#include "units.hh" + +namespace Kakoune +{ + +class String; +class Window; + +class UserInterface +{ +public: + virtual ~UserInterface() {} + virtual void print_status(const String& status, CharCount cursor_pos) = 0; + virtual void menu_show(const memoryview& choices) = 0; + virtual void menu_select(int selected) = 0; + virtual void menu_hide() = 0; + virtual void draw_window(Window& window) = 0; + virtual Key get_key() = 0; +}; + +} + +#endif // user_interface_hh_INCLUDED