Remove direct access to ui, go through client

Client can now update menu/info positions when the window move
around.
This commit is contained in:
Maxime Coste 2016-02-27 17:23:13 +00:00
parent f0edf40543
commit 3987463e75
12 changed files with 174 additions and 108 deletions

View File

@ -4,12 +4,12 @@
#include "context.hh"
#include "buffer_manager.hh"
#include "buffer_utils.hh"
#include "user_interface.hh"
#include "file.hh"
#include "remote.hh"
#include "client_manager.hh"
#include "command_manager.hh"
#include "event_manager.hh"
#include "user_interface.hh"
#include "window.hh"
#include <signal.h>
@ -31,8 +31,11 @@ Client::Client(std::unique_ptr<UserInterface>&& ui,
context().set_client(*this);
context().set_window(*m_window);
m_window->set_dimensions(m_ui->dimensions());
m_window->options().register_watcher(*this);
m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
m_ui->set_input_callback([this](EventMode mode) { handle_available_input(mode); });
}
Client::~Client()
@ -154,19 +157,43 @@ void Client::change_buffer(Buffer& buffer)
context().selections_write_only() = std::move(ws.selections);
context().set_window(*m_window);
m_window->set_dimensions(ui().dimensions());
m_window->set_dimensions(m_ui->dimensions());
m_window->hooks().run_hook("WinDisplay", buffer.name(), context());
}
static bool is_inline(InfoStyle style)
{
return style == InfoStyle::Inline or
style == InfoStyle::InlineAbove or
style == InfoStyle::InlineBelow;
}
void Client::redraw_ifn()
{
Window& window = context().window();
UserInterface& ui = context().ui();
const bool needs_redraw = window.needs_redraw(context());
if (needs_redraw)
ui.draw(window.update_display_buffer(context()), get_face("Default"));
{
auto window_pos = window.position();
m_ui->draw(window.update_display_buffer(context()), get_face("Default"));
// window moved, reanchor eventual menu and info
if (window_pos != window.position())
{
if (not m_menu.items.empty() and m_menu.style == MenuStyle::Inline)
{
m_ui->menu_show(m_menu.items, window.display_position(m_menu.anchor),
get_face("MenuForeground"), get_face("MenuBackground"), m_menu.style);
m_ui->menu_select(m_menu.selected);
}
if (not m_info.content.empty() and is_inline(m_info.style))
m_ui->info_show(m_info.title, m_info.content,
window.display_position(m_info.anchor),
get_face("Information"), m_info.style);
}
}
DisplayLine mode_line = generate_mode_line();
if (needs_redraw or
@ -176,10 +203,10 @@ void Client::redraw_ifn()
m_mode_line = std::move(mode_line);
m_status_line = m_pending_status_line;
ui.draw_status(m_status_line, m_mode_line, get_face("StatusLine"));
m_ui->draw_status(m_status_line, m_mode_line, get_face("StatusLine"));
}
ui.refresh();
m_ui->refresh();
}
void Client::force_redraw()
@ -277,4 +304,36 @@ void Client::on_option_changed(const Option& option)
m_ui->set_ui_options(option.get<UserInterface::Options>());
}
void Client::menu_show(Vector<DisplayLine> choices, ByteCoord anchor, MenuStyle style)
{
m_menu = Menu{ std::move(choices), anchor, style, -1 };
CharCoord ui_anchor = style == MenuStyle::Inline ? context().window().display_position(anchor) : CharCoord{};
m_ui->menu_show(m_menu.items, ui_anchor, get_face("MenuForeground"), get_face("MenuBackground"), style);
}
void Client::menu_select(int selected)
{
m_menu.selected = selected;
m_ui->menu_select(selected);
}
void Client::menu_hide()
{
m_menu = Menu{};
m_ui->menu_hide();
}
void Client::info_show(String title, String content, ByteCoord anchor, InfoStyle style)
{
m_info = Info{ std::move(title), std::move(content), anchor, style };
CharCoord ui_anchor = is_inline(style) ? context().window().display_position(anchor) : CharCoord{};
m_ui->info_show(m_info.title, m_info.content, ui_anchor, get_face("Information"), style);
}
void Client::info_hide()
{
m_info = Info{};
m_ui->info_hide();
}
}

View File

@ -8,11 +8,11 @@
#include "utils.hh"
#include "option_manager.hh"
#include "enum.hh"
#include "user_interface.hh"
namespace Kakoune
{
class UserInterface;
class Window;
class String;
struct Key;
@ -34,13 +34,20 @@ public:
// handle all the keys currently available in the user interface
void handle_available_input(EventMode mode);
void menu_show(Vector<DisplayLine> choices, ByteCoord anchor, MenuStyle style);
void menu_select(int selected);
void menu_hide();
void info_show(String title, String content, ByteCoord anchor, InfoStyle style);
void info_hide();
void print_status(DisplayLine status_line);
CharCoord dimensions() const { return m_ui->dimensions(); }
void force_redraw();
void redraw_ifn();
UserInterface& ui() const { return *m_ui; }
void check_if_buffer_needs_reloading();
Context& context() { return m_input_handler.context(); }
@ -78,6 +85,22 @@ private:
DisplayLine m_pending_status_line;
DisplayLine m_mode_line;
struct Menu
{
Vector<DisplayLine> items;
ByteCoord anchor;
MenuStyle style;
int selected;
} m_menu;
struct Info
{
String title;
String content;
ByteCoord anchor;
InfoStyle style;
} m_info;
Vector<Key, MemoryDomain::Client> m_pending_keys;
bool m_buffer_reload_dialog_opened = false;

View File

@ -6,7 +6,6 @@
#include "event_manager.hh"
#include "face_registry.hh"
#include "file.hh"
#include "user_interface.hh"
#include "window.hh"
namespace Kakoune
@ -59,10 +58,6 @@ Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
return nullptr;
}
client->ui().set_input_callback([client](EventMode mode) {
client->handle_available_input(mode);
});
return client;
}

View File

@ -22,7 +22,6 @@
#include "remote.hh"
#include "shell_manager.hh"
#include "string.hh"
#include "user_interface.hh"
#include "window.hh"
#include <sys/types.h>
@ -1522,7 +1521,7 @@ const CommandDesc menu_cmd = {
select_cmds.push_back(parser[i+2]);
}
context.input_handler().menu(choices,
context.input_handler().menu(std::move(choices),
[=](int choice, MenuEvent event, Context& context) {
ScopedSetBool disable_history{context.history_disabled()};
@ -1571,20 +1570,24 @@ const CommandDesc info_cmd = {
CommandCompleter{},
[](const ParametersParser& parser, Context& context, const ShellContext&)
{
context.ui().info_hide();
if (not context.has_client())
return;
context.client().info_hide();
if (parser.positional_count() > 0)
{
InfoStyle style = InfoStyle::Prompt;
CharCoord pos;
ByteCoord pos;
if (auto anchor = parser.get_switch("anchor"))
{
auto dot = find(*anchor, '.');
if (dot == anchor->end())
throw runtime_error("expected <line>.<column> for anchor");
ByteCoord coord{str_to_int({anchor->begin(), dot})-1,
pos = ByteCoord{str_to_int({anchor->begin(), dot})-1,
str_to_int({dot+1, anchor->end()})-1};
pos = context.window().display_position(coord);
style = InfoStyle::Inline;
if (auto placement = parser.get_switch("placement"))
{
if (*placement == "above")
@ -1596,7 +1599,7 @@ const CommandDesc info_cmd = {
}
}
auto title = parser.get_switch("title").value_or(StringView{});
context.ui().info_show(title, parser[0], pos, get_face("Information"), style);
context.client().info_show(title.str(), parser[0], pos, style);
}
}
};

View File

@ -2,7 +2,6 @@
#include "alias_registry.hh"
#include "client.hh"
#include "user_interface.hh"
#include "register_manager.hh"
#include "window.hh"
@ -49,13 +48,6 @@ Client& Context::client() const
return *m_client;
}
UserInterface& Context::ui() const
{
if (not has_ui())
throw runtime_error("no user interface in context");
return client().ui();
}
Scope& Context::scope() const
{
if (has_window())
@ -75,8 +67,6 @@ void Context::set_window(Window& window)
{
kak_assert(&window.buffer() == &buffer());
m_window.reset(&window);
if (has_ui())
m_window->set_dimensions(ui().dimensions());
}
void Context::print_status(DisplayLine status) const

View File

@ -107,9 +107,6 @@ public:
InputHandler& input_handler() const;
bool has_input_handler() const { return (bool)m_input_handler; }
UserInterface& ui() const;
bool has_ui() const { return has_client(); }
SelectionList& selections();
const SelectionList& selections() const;
Vector<String> selections_content() const;

View File

@ -10,7 +10,6 @@
#include "regex.hh"
#include "register_manager.hh"
#include "unordered_map.hh"
#include "user_interface.hh"
#include "utf8.hh"
#include "window.hh"
@ -244,8 +243,8 @@ public:
pop_mode();
context().print_status({});
if (context().has_ui())
context().ui().info_hide();
if (context().has_client())
context().client().info_hide();
do_restore_hooks = true;
auto it = std::lower_bound(keymap.begin(), keymap.end(), key,
@ -254,9 +253,9 @@ public:
if (it != keymap.end() and it->key == key)
{
auto autoinfo = context().options()["autoinfo"].get<AutoInfo>();
if (autoinfo & AutoInfo::Normal and context().has_ui())
context().ui().info_show(key_to_str(key), it->docstring, CharCoord{},
get_face("Information"), InfoStyle::Prompt);
if (autoinfo & AutoInfo::Normal and context().has_client())
context().client().info_show(key_to_str(key), it->docstring.str(),
{}, InfoStyle::Prompt);
// reset m_params now to be reentrant
NormalParams params = m_params;
@ -490,17 +489,16 @@ private:
class Menu : public InputMode
{
public:
Menu(InputHandler& input_handler, ConstArrayView<DisplayLine> choices,
Menu(InputHandler& input_handler, Vector<DisplayLine> choices,
MenuCallback callback)
: InputMode(input_handler),
m_callback(callback), m_choices(choices.begin(), choices.end()),
m_selected(m_choices.begin())
{
if (not context().has_ui())
if (not context().has_client())
return;
context().ui().menu_show(choices, CharCoord{}, get_face("MenuForeground"),
get_face("MenuBackground"), MenuStyle::Prompt);
context().ui().menu_select(0);
context().client().menu_show(std::move(choices), {}, MenuStyle::Prompt);
context().client().menu_select(0);
}
void on_key(Key key) override
@ -517,8 +515,8 @@ public:
if (key == ctrl('m'))
{
if (context().has_ui())
context().ui().menu_hide();
if (context().has_client())
context().client().menu_hide();
context().print_status(DisplayLine{});
pop_mode();
int selected = m_selected - m_choices.begin();
@ -536,8 +534,8 @@ public:
}
else
{
if (context().has_ui())
context().ui().menu_hide();
if (context().has_client())
context().client().menu_hide();
pop_mode();
int selected = m_selected - m_choices.begin();
m_callback(selected, MenuEvent::Abort, context());
@ -576,10 +574,10 @@ public:
select(it);
}
if (m_edit_filter and context().has_ui())
if (m_edit_filter and context().has_client())
{
auto prompt = "filter:"_str;
auto width = context().ui().dimensions().column - prompt.char_length();
auto width = context().client().dimensions().column - prompt.char_length();
auto display_line = m_filter_editor.build_display_line(width);
display_line.insert(display_line.begin(), { prompt, get_face("Prompt") });
context().print_status(display_line);
@ -604,8 +602,8 @@ private:
{
m_selected = it;
int selected = m_selected - m_choices.begin();
if (context().has_ui())
context().ui().menu_select(selected);
if (context().has_client())
context().client().menu_select(selected);
m_callback(selected, MenuEvent::Select, context());
}
@ -671,8 +669,8 @@ public:
if (not context().history_disabled())
history_push(history, line);
context().print_status(DisplayLine{});
if (context().has_ui())
context().ui().menu_hide();
if (context().has_client())
context().client().menu_hide();
pop_mode();
// call callback after pop_mode so that callback
// may change the mode
@ -684,8 +682,8 @@ public:
if (not context().history_disabled())
history_push(history, line);
context().print_status(DisplayLine{});
if (context().has_ui())
context().ui().menu_hide();
if (context().has_client())
context().client().menu_hide();
pop_mode();
m_callback(line, PromptEvent::Abort, context());
return;
@ -811,8 +809,8 @@ public:
}
const String& completion = candidates[m_current_completion];
if (context().has_ui())
context().ui().menu_select(m_current_completion);
if (context().has_client())
context().client().menu_select(m_current_completion);
m_line_editor.insert_from(line.char_count_to(m_completions.start),
completion);
@ -872,15 +870,12 @@ private:
const String& line = m_line_editor.line();
m_completions = m_completer(context(), flags, line,
line.byte_count_to(m_line_editor.cursor_pos()));
if (context().has_ui() and not m_completions.candidates.empty())
if (context().has_client() and not m_completions.candidates.empty())
{
Vector<DisplayLine> items;
for (auto& candidate : m_completions.candidates)
items.push_back({ candidate, {} });
context().ui().menu_show(items, CharCoord{},
get_face("MenuForeground"),
get_face("MenuBackground"),
MenuStyle::Prompt);
context().client().menu_show(items, {}, MenuStyle::Prompt);
}
} catch (runtime_error&) {}
}
@ -889,16 +884,16 @@ private:
{
m_current_completion = -1;
m_completions.candidates.clear();
if (context().has_ui())
context().ui().menu_hide();
if (context().has_client())
context().client().menu_hide();
}
void display()
{
if (not context().has_ui())
if (not context().has_client())
return;
auto width = context().ui().dimensions().column - m_prompt.char_length();
auto width = context().client().dimensions().column - m_prompt.char_length();
auto display_line = m_line_editor.build_display_line(width);
display_line.insert(display_line.begin(), { m_prompt, m_prompt_face });
context().print_status(display_line);
@ -1350,9 +1345,9 @@ void InputHandler::set_prompt_face(Face prompt_face)
prompt->set_prompt_face(prompt_face);
}
void InputHandler::menu(ConstArrayView<DisplayLine> choices, MenuCallback callback)
void InputHandler::menu(Vector<DisplayLine> choices, MenuCallback callback)
{
push_mode(new InputModes::Menu(*this, choices, callback));
push_mode(new InputModes::Menu(*this, std::move(choices), callback));
}
void InputHandler::on_next_key(KeymapMode keymap_mode, KeyCallback callback)
@ -1420,12 +1415,17 @@ DisplayLine InputHandler::mode_line() const
bool show_auto_info_ifn(StringView title, StringView info, AutoInfo mask, const Context& context)
{
if (not (context.options()["autoinfo"].get<AutoInfo>() & mask) or
not context.has_ui())
not context.has_client())
return false;
Face face = get_face("Information");
context.ui().info_show(title, info, CharCoord{}, face, InfoStyle::Prompt);
context.client().info_show(title.str(), info.str(), {}, InfoStyle::Prompt);
return true;
}
void hide_auto_info_ifn(const Context& context, bool hide)
{
if (hide)
context.client().info_hide();
}
}

View File

@ -8,8 +8,8 @@
#include "keys.hh"
#include "string.hh"
#include "utils.hh"
#include "user_interface.hh"
#include "safe_ptr.hh"
#include "display_buffer.hh"
namespace Kakoune
{
@ -32,7 +32,6 @@ using PromptCallback = std::function<void (StringView, PromptEvent, Context&)>;
using KeyCallback = std::function<void (Key, Context&)>;
class InputMode;
class DisplayLine;
enum class InsertMode : unsigned;
enum class KeymapMode : char;
@ -62,7 +61,7 @@ public:
// abort or validation with corresponding MenuEvent value
// returns to normal mode after validation if callback does
// not change the mode itself
void menu(ConstArrayView<DisplayLine> choices, MenuCallback callback);
void menu(Vector<DisplayLine> choices, MenuCallback callback);
// execute callback on next keypress and returns to normal mode
// if callback does not change the mode itself
@ -124,6 +123,7 @@ constexpr Array<EnumDesc<AutoInfo>, 3> enum_desc(AutoInfo)
}
bool show_auto_info_ifn(StringView title, StringView info, AutoInfo mask, const Context& context);
void hide_auto_info_ifn(const Context& context, bool hide);
template<typename Cmd>
void on_next_key_with_autoinfo(const Context& context, KeymapMode keymap_mode, Cmd cmd,
@ -132,11 +132,11 @@ void on_next_key_with_autoinfo(const Context& context, KeymapMode keymap_mode, C
const bool hide = show_auto_info_ifn(title, info, AutoInfo::OnKey, context);
context.input_handler().on_next_key(
keymap_mode, [hide,cmd](Key key, Context& context) mutable {
if (hide)
context.ui().info_hide();
hide_auto_info_ifn(context, hide);
cmd(key, context);
});
}
}
#endif // input_handler_hh_INCLUDED

View File

@ -2,12 +2,12 @@
#include "buffer_manager.hh"
#include "buffer_utils.hh"
#include "client.hh"
#include "context.hh"
#include "display_buffer.hh"
#include "face_registry.hh"
#include "file.hh"
#include "regex.hh"
#include "user_interface.hh"
#include "window.hh"
#include "word_db.hh"
#include "utf8_iterator.hh"
@ -364,14 +364,14 @@ void InsertCompleter::select(int offset, Vector<Key>& keystrokes)
m_completions.end = cursor_pos;
m_completions.begin = buffer.advance(cursor_pos, -candidate.completion.length());
m_completions.timestamp = buffer.timestamp();
if (m_context.has_ui())
if (m_context.has_client())
{
m_context.ui().menu_select(m_current_candidate);
m_context.client().menu_select(m_current_candidate);
if (not candidate.docstring.empty())
m_context.ui().info_show(candidate.completion, candidate.docstring, CharCoord{},
get_face("Information"), InfoStyle::MenuDoc);
m_context.client().info_show(candidate.completion, candidate.docstring,
{}, InfoStyle::MenuDoc);
else
m_context.ui().info_hide();
m_context.client().info_hide();
}
for (auto i = 0_byte; i < prefix_len; ++i)
@ -395,10 +395,10 @@ void InsertCompleter::reset()
{
m_completions = InsertCompletion{};
m_explicit_completer = nullptr;
if (m_context.has_ui())
if (m_context.has_client())
{
m_context.ui().menu_hide();
m_context.ui().info_hide();
m_context.client().menu_hide();
m_context.client().info_hide();
}
}
@ -435,19 +435,16 @@ bool InsertCompleter::setup_ifn()
void InsertCompleter::menu_show()
{
if (not m_context.has_ui())
if (not m_context.has_client())
return;
CharCoord menu_pos = m_context.window().display_position(m_completions.begin);
Vector<DisplayLine> menu_entries;
for (auto& candidate : m_completions.candidates)
menu_entries.push_back(candidate.menu_entry);
m_context.ui().menu_show(menu_entries, menu_pos,
get_face("MenuForeground"),
get_face("MenuBackground"),
MenuStyle::Inline);
m_context.ui().menu_select(m_current_candidate);
m_context.client().menu_show(std::move(menu_entries), m_completions.begin,
MenuStyle::Inline);
m_context.client().menu_select(m_current_candidate);
}
void InsertCompleter::on_option_changed(const Option& opt)

View File

@ -265,6 +265,7 @@ struct convert_to_client_mode
};
static Client* local_client = nullptr;
static UserInterface* local_ui = nullptr;
static bool convert_to_client_pending = false;
pid_t fork_server_to_background()
@ -309,6 +310,8 @@ std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
{
LocalUI()
{
kak_assert(not local_ui);
local_ui = this;
m_old_sighup = set_signal_handler(SIGHUP, [](int) {
ClientManager::instance().remove_client(*local_client, false);
});
@ -318,7 +321,7 @@ std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
*ClientManager::instance().begin() == local_client)
{
// Suspend normally if we are the only client
auto current = set_signal_handler(SIGTSTP, static_cast<LocalUI&>(local_client->ui()).m_old_sigtstp);
auto current = set_signal_handler(SIGTSTP, static_cast<LocalUI*>(local_ui)->m_old_sigtstp);
sigset_t unblock_sigtstp, old_mask;
sigemptyset(&unblock_sigtstp);
@ -341,6 +344,7 @@ std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
set_signal_handler(SIGHUP, m_old_sighup);
set_signal_handler(SIGTSTP, m_old_sigtstp);
local_client = nullptr;
local_ui = nullptr;
if (not convert_to_client_pending and
not ClientManager::instance().empty())
{

View File

@ -16,7 +16,6 @@
#include "selectors.hh"
#include "shell_manager.hh"
#include "string.hh"
#include "user_interface.hh"
#include "window.hh"
namespace Kakoune
@ -346,20 +345,19 @@ void command(Context& context, NormalParams params)
return CommandManager::instance().complete(context, flags, cmd_line, pos);
},
[params](StringView cmdline, PromptEvent event, Context& context) {
if (context.has_ui())
if (context.has_client())
{
context.ui().info_hide();
context.client().info_hide();
auto autoinfo = context.options()["autoinfo"].get<AutoInfo>();
if (event == PromptEvent::Change and autoinfo & AutoInfo::Command)
{
Face face = get_face("Information");
if (cmdline.length() == 1 and is_horizontal_blank(cmdline[0_byte]))
context.ui().info_show("prompt", "commands preceded by a blank wont be saved to history",
CharCoord{}, face, InfoStyle::Prompt);
context.client().info_show("prompt", "commands preceded by a blank wont be saved to history",
{}, InfoStyle::Prompt);
auto info = CommandManager::instance().command_info(context, cmdline);
if (not info.first.empty() and not info.second.empty())
context.ui().info_show(info.first, info.second, CharCoord{}, face, InfoStyle::Prompt);
context.client().info_show(info.first, info.second, {}, InfoStyle::Prompt);
}
}
if (event == PromptEvent::Validate)
@ -586,8 +584,8 @@ void regex_prompt(Context& context, const String prompt, T func)
[=](StringView str, PromptEvent event, Context& context) mutable {
try
{
if (event != PromptEvent::Change and context.has_ui())
context.ui().info_hide();
if (event != PromptEvent::Change and context.has_client())
context.client().info_hide();
const bool incsearch = context.options()["incsearch"].get<bool>();
if (incsearch)

View File

@ -5,7 +5,7 @@
#include "highlighter.hh"
#include "hook_manager.hh"
#include "input_handler.hh"
#include "user_interface.hh"
#include "client.hh"
#include <algorithm>
#include <sstream>
@ -79,7 +79,7 @@ Window::Setup Window::build_setup(const Context& context) const
selections.push_back({sel.cursor(), sel.anchor()});
return { m_position,
context.ui().dimensions(),
context.client().dimensions(),
context.buffer().timestamp(),
context.selections().main_index(),
std::move(selections) };
@ -90,7 +90,7 @@ bool Window::needs_redraw(const Context& context) const
auto& selections = context.selections();
if (m_position != m_last_setup.position or
context.ui().dimensions() != m_last_setup.dimensions or
context.client().dimensions() != m_last_setup.dimensions or
context.buffer().timestamp() != m_last_setup.timestamp or
selections.main_index() != m_last_setup.main_selection or
selections.size() != m_last_setup.selections.size())
@ -111,7 +111,7 @@ const DisplayBuffer& Window::update_display_buffer(const Context& context)
DisplayBuffer::LineList& lines = m_display_buffer.lines();
lines.clear();
m_dimensions = context.ui().dimensions();
m_dimensions = context.client().dimensions();
if (m_dimensions == CharCoord{0,0})
return m_display_buffer;