diff --git a/doc/pages/faces.asciidoc b/doc/pages/faces.asciidoc index a0eed95186..14e56fb5f9 100644 --- a/doc/pages/faces.asciidoc +++ b/doc/pages/faces.asciidoc @@ -113,3 +113,9 @@ areas of the user interface: *Whitespace*:: face used by the show_whitespaces highlighter + +*BufferList*:: + face applied on the regular (not focused) buffer names in the buffer list + +*BufferListActive*:: + face applied on the name of the currently focused buffer, in the buffer list diff --git a/doc/pages/options.asciidoc b/doc/pages/options.asciidoc index 2a68bd72be..698ebb2a51 100644 --- a/doc/pages/options.asciidoc +++ b/doc/pages/options.asciidoc @@ -265,3 +265,6 @@ are exclusively available to built-in options. *ncurses_wheel_down_button*, *ncurses_wheel_up_button*::: specify which button send for wheel down/up events + + *ncurses_buflist_separator*::: + string inserted between each buffer name in the buffer list diff --git a/src/client.cc b/src/client.cc index 6ae587b8ce..09492cbb9b 100644 --- a/src/client.cc +++ b/src/client.cc @@ -156,6 +156,28 @@ DisplayLine Client::generate_mode_line() const return modeline; } +Vector Client::generate_buflist(int* active_buffer) const +{ + Vector buflist; + int i; + + i = 0; + *active_buffer = -1; + for (auto& buffer : BufferManager::instance()) + { + buflist.push_back(buffer->display_name()); + + if (buffer.get() == &context().buffer()) + *active_buffer = i; + + i++; + } + + kak_assert(*active_buffer > -1); + + return buflist; +} + void Client::change_buffer(Buffer& buffer) { if (m_buffer_reload_dialog_opened) @@ -204,6 +226,15 @@ void Client::redraw_ifn() m_mode_line = std::move(mode_line); } + int active_buffer; + Vector buflist = generate_buflist(&active_buffer); + if (buflist != m_buflist or active_buffer != m_active_buffer) + { + m_ui_pending |= BufList; + m_active_buffer = active_buffer; + m_buflist = std::move(buflist); + } + if (m_ui_pending == 0) return; @@ -251,6 +282,9 @@ void Client::redraw_ifn() if (m_ui_pending & StatusLine) m_ui->draw_status(m_status_line, m_mode_line, get_face("StatusLine")); + if (m_ui_pending & BufList) + m_ui->draw_buflist(m_buflist, m_active_buffer, get_face("BufferListActive"), get_face("BufferList")); + auto cursor = m_input_handler.get_cursor_info(); m_ui->set_cursor(cursor.first, cursor.second); @@ -260,7 +294,7 @@ void Client::redraw_ifn() void Client::force_redraw() { - m_ui_pending |= Refresh | Draw | StatusLine | + m_ui_pending |= Refresh | Draw | StatusLine | BufList | (m_menu.items.empty() ? MenuHide : MenuShow | MenuSelect) | (m_info.content.empty() ? InfoHide : InfoShow); } diff --git a/src/client.hh b/src/client.hh index dadc03203f..89e17b4db1 100644 --- a/src/client.hh +++ b/src/client.hh @@ -85,6 +85,9 @@ private: DisplayLine generate_mode_line() const; + int m_active_buffer; + Vector generate_buflist(int* active_buffer) const; + std::unique_ptr m_ui; std::unique_ptr m_window; @@ -97,6 +100,7 @@ private: InputHandler m_input_handler; DisplayLine m_status_line; + Vector m_buflist; DisplayLine m_mode_line; enum PendingUI : int @@ -107,8 +111,9 @@ private: InfoShow = 1 << 3, InfoHide = 1 << 4, StatusLine = 1 << 5, - Draw = 1 << 6, - Refresh = 1 << 7, + BufList = 1 << 6, + Draw = 1 << 7, + Refresh = 1 << 8, }; int m_ui_pending = 0; diff --git a/src/face_registry.cc b/src/face_registry.cc index cfae6cb8cd..13068ca0f7 100644 --- a/src/face_registry.cc +++ b/src/face_registry.cc @@ -152,6 +152,8 @@ FaceRegistry::FaceRegistry() { "MatchingChar", {Face{ Color::Default, Color::Default, Attribute::Bold }} }, { "BufferPadding", {Face{ Color::Blue, Color::Default }} }, { "Whitespace", {Face{ Color::Default, Color::Default }} }, + { "BufferListActive", {Face{ Color::Default, Color::Default, Attribute::Bold }} }, + { "BufferList", {Face{ Color::Default, Color::Default }} }, } {} diff --git a/src/json_ui.cc b/src/json_ui.cc index 01d95f9c1e..55aa81f025 100644 --- a/src/json_ui.cc +++ b/src/json_ui.cc @@ -190,6 +190,13 @@ void JsonUI::draw_status(const DisplayLine& status_line, rpc_call("draw_status", status_line, mode_line, default_face); } +void JsonUI::draw_buflist(ConstArrayView buffer_names, + int idx_active_buffer, + const Face& face_active_buffer, + const Face& face_regular_buffer) +{ + rpc_call("draw_buflist", buffer_names, idx_active_buffer, face_active_buffer, face_regular_buffer); +} void JsonUI::menu_show(ConstArrayView items, DisplayCoord anchor, Face fg, Face bg, diff --git a/src/json_ui.hh b/src/json_ui.hh index ad88756131..d26d835430 100644 --- a/src/json_ui.hh +++ b/src/json_ui.hh @@ -26,6 +26,10 @@ public: void draw_status(const DisplayLine& status_line, const DisplayLine& mode_line, const Face& default_face) override; + void draw_buflist(ConstArrayView buffer_names, + int idx_active_buffer, + const Face& face_active_buffer, + const Face& face_regular_buffer) override; void menu_show(ConstArrayView items, DisplayCoord anchor, Face fg, Face bg, diff --git a/src/main.cc b/src/main.cc index b7c62aece9..d26f82d05e 100644 --- a/src/main.cc +++ b/src/main.cc @@ -406,6 +406,10 @@ std::unique_ptr make_ui(UIType ui_type) void draw(const DisplayBuffer&, const Face&, const Face&) override {} void draw_status(const DisplayLine&, const DisplayLine&, const Face&) override {} + void draw_buflist(ConstArrayView buffer_names, + int idx_active_buffer, + const Face& face_active_buffer, + const Face& face_regular_buffer) override {} DisplayCoord dimensions() override { return {24,80}; } void set_cursor(CursorMode, DisplayCoord) override {} void refresh(bool) override {} diff --git a/src/ncurses_ui.cc b/src/ncurses_ui.cc index 47e2b0309b..48712d6d04 100644 --- a/src/ncurses_ui.cc +++ b/src/ncurses_ui.cc @@ -467,6 +467,54 @@ void NCursesUI::draw_status(const DisplayLine& status_line, m_dirty = true; } +void NCursesUI::draw_buflist(ConstArrayView buffer_names, int idx_active_buffer, + const Face& face_active_buffer, const Face& face_regular_buffer) +{ + DisplayLine line; + const int buflist_pos = m_status_on_top ? 1 : (int)m_dimensions.line; + + wmove(m_window, buflist_pos, 0); + wbkgdset(m_window, COLOR_PAIR(get_color_pair(face_regular_buffer))); + wclrtoeol(m_window); + + int i = 0; + ColumnCount width_to_current = 0; + for (auto& buffer_name : buffer_names) + { + if (i > 0) + line.push_back(DisplayAtom(m_buflist_separator.str())); + + for (auto& atom : buffer_name.atoms()) + { + const Face atom_face = i == idx_active_buffer ? face_active_buffer : face_regular_buffer; + + kak_assert(atom.type() == DisplayAtom::Type::Text); + + line.push_back(DisplayAtom(atom.content().str(), atom_face)); + } + + // save the length of the whole line up to and including the current buffer + if (i == idx_active_buffer) + width_to_current = line.length(); + + i++; + } + + // if the line is wider than the terminal, bring back the current buffer into view + // also show the buffer following the current buffer for context (~1/4th of the terminal width) + while (line.length() > m_dimensions.column + and width_to_current > m_dimensions.column * 3 / 4 + and line.atoms().size() > 2) + { + width_to_current -= line.atoms()[0].length() + line.atoms()[1].length(); + line.erase(line.begin(), line.begin() + 2); + } + + draw_line(m_window, line, 0, m_dimensions.column, face_regular_buffer); + + m_dirty = true; +} + void NCursesUI::check_resize(bool force) { if (not force and not resize_pending) @@ -557,7 +605,7 @@ Optional NCursesUI::get_next_key() }; return Key{ get_modifiers(ev.bstate), - encode_coord({ ev.y - (m_status_on_top ? 1 : 0), ev.x }) }; + encode_coord({ ev.y - (m_status_on_top ? 2 : 0), ev.x }) }; } } @@ -745,7 +793,7 @@ void NCursesUI::menu_show(ConstArrayView items, div_round_up(item_count, m_menu.columns)); if (style != MenuStyle::Prompt and m_status_on_top) - anchor.line += 1; + anchor.line += 2; LineCount line = anchor.line + 1; if (is_prompt) @@ -1104,6 +1152,12 @@ void NCursesUI::set_ui_options(const Options& options) m_wheel_down_button = wheel_down_it != options.end() ? str_to_int_ifp(wheel_down_it->value).value_or(5) : 5; } + + { + auto it = options.find("ncurses_buflist_separator"_sv); + if (it != options.end()) + m_buflist_separator = it->value; + } } } diff --git a/src/ncurses_ui.hh b/src/ncurses_ui.hh index 3588919311..8d47f9e1ab 100644 --- a/src/ncurses_ui.hh +++ b/src/ncurses_ui.hh @@ -31,6 +31,10 @@ public: void draw_status(const DisplayLine& status_line, const DisplayLine& mode_line, const Face& default_face) override; + void draw_buflist(ConstArrayView buffer_names, + int idx_active_buffer, + const Face& face_active_buffer, + const Face& face_regular_buffer) override; void menu_show(ConstArrayView items, DisplayCoord anchor, Face fg, Face bg, @@ -134,6 +138,8 @@ private: bool m_status_on_top = false; ConstArrayView m_assistant; + StringView m_buflist_separator = "|"; + void enable_mouse(bool enabled); bool m_mouse_enabled = false; diff --git a/src/remote.cc b/src/remote.cc index f3dd7d063b..008204698e 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -36,6 +36,7 @@ enum class MessageType : uint8_t InfoHide, Draw, DrawStatus, + DrawBufList, SetCursor, Refresh, SetOptions, @@ -338,6 +339,10 @@ class RemoteUI : public UserInterface void draw_status(const DisplayLine& status_line, const DisplayLine& mode_line, const Face& default_face) override; + void draw_buflist(ConstArrayView buffer_names, + int idx_active_buffer, + const Face& face_active_buffer, + const Face& face_regular_buffer) override; void set_cursor(CursorMode mode, DisplayCoord coord) override; @@ -499,6 +504,19 @@ void RemoteUI::draw_status(const DisplayLine& status_line, m_socket_watcher.events() |= FdEvents::Write; } +void RemoteUI::draw_buflist(ConstArrayView buffer_names, + int idx_active_buffer, + const Face& face_active_buffer, + const Face& face_regular_buffer) +{ + MsgWriter msg{m_send_buffer, MessageType::DrawBufList}; + msg.write(buffer_names); + msg.write(idx_active_buffer); + msg.write(face_active_buffer); + msg.write(face_regular_buffer); + m_socket_watcher.events() |= FdEvents::Write; +} + void RemoteUI::set_cursor(CursorMode mode, DisplayCoord coord) { MsgWriter msg{m_send_buffer, MessageType::SetCursor}; diff --git a/src/user_interface.hh b/src/user_interface.hh index d81393362f..c428e866ca 100644 --- a/src/user_interface.hh +++ b/src/user_interface.hh @@ -66,6 +66,11 @@ public: const DisplayLine& mode_line, const Face& default_face) = 0; + virtual void draw_buflist(ConstArrayView buffer_names, + int idx_active_buffer, + const Face& face_active_buffer, + const Face& face_regular_buffer) = 0; + virtual DisplayCoord dimensions() = 0; virtual void set_cursor(CursorMode mode, DisplayCoord coord) = 0;