Skip to content

Add a Scroll Bar #5304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
6 changes: 6 additions & 0 deletions doc/pages/faces.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ the user interface:
*BufferPadding*::
Face applied on the *~* characters that follow the last line of a buffer.

*ScrollBarGutter*::
Face applied on the scroll bar's gutter.

*ScrollBarHandle*::
Face applied on the scroll bar's handle.

=== Built-in highlighter faces

The following faces are used by built-in highlighters if enabled.
Expand Down
2 changes: 2 additions & 0 deletions doc/pages/options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,8 @@ are exclusively available to built-in options.
set the maximum allowable width of an info box. set to zero for
no limit.

*terminal_status_bar*:::
if *yes* or *true* a scroll bar will be displayed.
[[startup-info]]
*startup_info_version* `int`::
_default_ 0 +
Expand Down
17 changes: 14 additions & 3 deletions src/client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,20 @@ void Client::redraw_ifn()

const auto& faces = context().faces();

if (m_ui_pending & Draw)
m_ui->draw(window.update_display_buffer(context()),
faces["Default"], faces["BufferPadding"]);
if (m_ui_pending & Draw) {
auto& db = window.update_display_buffer(context());
auto selections = context().selections();
auto sel = selections.begin();
Vector<LineCount> selection_lines;
selection_lines.reserve(selections.size());
std::generate_n(std::back_inserter(selection_lines), selections.size(), [&sel] { return (sel++)->min().line; });
m_ui->draw(db,
{db.range().begin.line, db.range().end.line},
context().buffer().line_count(),
selection_lines,
faces["Default"], faces["BufferPadding"],
faces["ScrollBarGutter"], faces["ScrollBarHandle"]);
}

const bool update_menu_anchor = (m_ui_pending & Draw) and not (m_ui_pending & MenuHide) and
not m_menu.items.empty() and m_menu.style == MenuStyle::Inline;
Expand Down
2 changes: 2 additions & 0 deletions src/face_registry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ FaceRegistry::FaceRegistry()
{ "BufferPadding", {Face{ Color::Blue, Color::Default }} },
{ "Whitespace", {Face{ Color::Default, Color::Default, Attribute::FinalFg }} },
{ "WhitespaceIndent", {Face{}, "Whitespace"} },
{ "ScrollBarGutter", {Face{ Color::Blue, Color::Default }} },
{ "ScrollBarHandle", {Face{ Color::Blue, Color::Default, Attribute::Reverse }} },
}
{}

Expand Down
10 changes: 8 additions & 2 deletions src/json_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,15 @@ JsonUI::JsonUI()
}

void JsonUI::draw(const DisplayBuffer& display_buffer,
const Face& default_face, const Face& padding_face)
const Range<LineCount> range,
const LineCount buffer_line_count,
const Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face)
{
rpc_call("draw", display_buffer.lines(), default_face, padding_face);
rpc_call("draw", display_buffer.lines(), range.begin, range.end, buffer_line_count, selection_lines, default_face, padding_face, scroll_bar_gutter_face, scroll_bar_handle_face);
}

void JsonUI::draw_status(const DisplayLine& status_line,
Expand Down
9 changes: 7 additions & 2 deletions src/json_ui.hh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ public:
bool is_ok() const override { return m_stdin_watcher.fd() != -1; }

void draw(const DisplayBuffer& display_buffer,
const Face& default_face,
const Face& buffer_padding) override;
const Range<LineCount> range,
const LineCount buffer_line_count,
const Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face) override;

void draw_status(const DisplayLine& status_line,
const DisplayLine& mode_line,
Expand Down
12 changes: 10 additions & 2 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ void register_options()
" terminal_shift_function_key int\n"
" terminal_padding_char codepoint\n"
" terminal_padding_fill bool\n"
" terminal_info_max_width int\n",
" terminal_info_max_width int\n"
" terminal_scroll_bar bool\n",
UserInterface::Options{});
reg.declare_option("modelinefmt", "format string used to generate the modeline",
"%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str);
Expand Down Expand Up @@ -654,7 +655,14 @@ std::unique_ptr<UserInterface> make_ui(UIType ui_type)
void info_show(const DisplayLine&, const DisplayLineList&, DisplayCoord, Face, InfoStyle) override {}
void info_hide() override {}

void draw(const DisplayBuffer&, const Face&, const Face&) override {}
void draw(const DisplayBuffer& display_buffer,
const Range<LineCount> range,
const LineCount buffer_line_count,
const Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face) override {}
void draw_status(const DisplayLine&, const DisplayLine&, const Face&) override {}
DisplayCoord dimensions() override { return {24,80}; }
void set_cursor(CursorMode, DisplayCoord) override {}
Expand Down
16 changes: 13 additions & 3 deletions src/remote.cc
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,13 @@ class RemoteUI : public UserInterface
void info_hide() override;

void draw(const DisplayBuffer& display_buffer,
const Range<LineCount> range,
const LineCount buffer_line_count,
const Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face) override;
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face) override;

void draw_status(const DisplayLine& status_line,
const DisplayLine& mode_line,
Expand Down Expand Up @@ -562,10 +567,15 @@ void RemoteUI::info_hide()
}

void RemoteUI::draw(const DisplayBuffer& display_buffer,
const Range<LineCount> range,
const LineCount buffer_line_count,
const Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face)
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face)
{
send_message(MessageType::Draw, display_buffer, default_face, padding_face);
send_message(MessageType::Draw, display_buffer, range, buffer_line_count, selection_lines, default_face, padding_face, scroll_bar_gutter_face, scroll_bar_handle_face);
}

void RemoteUI::draw_status(const DisplayLine& status_line,
Expand Down
62 changes: 58 additions & 4 deletions src/terminal_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -556,11 +556,26 @@ void TerminalUI::refresh(bool force)
m_dirty = false;
}

template<typename T>
T scale_to(T val, Range<T> from, Range<T> to)
{
T from_size = from.end - from.begin + 1;
T to_size = to.end - to.begin + 1;

return ((val - from.begin) * to_size + from_size / 2) / from_size + to.begin;
}

static const DisplayLine empty_line = { String(" "), {} };


void TerminalUI::draw(const DisplayBuffer& display_buffer,
const Range<LineCount> range,
const LineCount buffer_line_count,
const Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face)
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face)
{
check_resize();

Expand All @@ -574,9 +589,40 @@ void TerminalUI::draw(const DisplayBuffer& display_buffer,

DisplayAtom padding{String{m_padding_char, m_padding_fill ? dim.column : 1}};

const auto padding_lines = (dim.line + line_offset) - line_index;
while (line_index < dim.line + line_offset)
m_window.draw(line_index++, padding, face);

if (m_scroll_bar)
{
Range<LineCount> gutter_range = {0_line, dim.line - 1};
Range<LineCount> buffer_range = {0_line, buffer_line_count - 1 + dim.line - 1};

std::fill(m_scroll_bar_scratch.begin(), m_scroll_bar_scratch.end(), 0);

for (const LineCount selection_line : selection_lines)
m_scroll_bar_scratch[(int) scale_to(selection_line, buffer_range, gutter_range)]++;

const auto visible_lines = range.end - range.begin + padding_lines;
const auto mark_height = scale_to(visible_lines, buffer_range, gutter_range);

const auto mark_begin = scale_to(range.begin, buffer_range, gutter_range);
const auto mark_end = mark_begin + mark_height;

for (auto line = 0_line; line < dim.line; ++line) {
const bool is_mark = line >= mark_begin and line <= mark_end;
String selections;
switch (m_scroll_bar_scratch[(int)line]) {
case 0: selections = " "; break;
case 1: selections = "-"; break;
case 2: selections = "="; break;
default: selections = "≡"; break;
}

m_window.draw({line + line_offset, m_window.size.column - 1}, DisplayAtom(selections), is_mark ? scroll_bar_handle_face : scroll_bar_gutter_face);
}
}

m_dirty = true;
}

Expand All @@ -589,10 +635,10 @@ void TerminalUI::draw_status(const DisplayLine& status_line,

const auto mode_len = mode_line.length();
m_status_len = status_line.length();
const auto remaining = m_dimensions.column - m_status_len;
const auto remaining = m_dimensions.column - m_status_len + 1;
if (mode_len < remaining)
{
ColumnCount col = m_dimensions.column - mode_len;
ColumnCount col = m_dimensions.column - mode_len + 1;
m_window.draw({status_line_pos, col}, mode_line.atoms(), default_face);
}
else if (remaining > 2)
Expand All @@ -602,7 +648,7 @@ void TerminalUI::draw_status(const DisplayLine& status_line,
trimmed_mode_line.insert(trimmed_mode_line.begin(), { "…", {} });
kak_assert(trimmed_mode_line.length() == remaining - 1);

ColumnCount col = m_dimensions.column - remaining + 1;
ColumnCount col = m_dimensions.column - remaining + 2;
m_window.draw({status_line_pos, col}, trimmed_mode_line.atoms(), default_face);
}

Expand Down Expand Up @@ -656,6 +702,11 @@ void TerminalUI::check_resize(bool force)

m_dimensions = terminal_size - 1_line;

if (m_scroll_bar) {
m_dimensions -= {0_line, 1_col};
m_scroll_bar_scratch.resize((size_t)m_dimensions.line);
}

// if (char* csr = tigetstr((char*)"csr"))
// putp(tparm(csr, 0, ws.ws_row));

Expand Down Expand Up @@ -1576,6 +1627,9 @@ void TerminalUI::set_ui_options(const Options& options)
m_padding_fill = find("terminal_padding_fill").map(to_bool).value_or(false);

m_info_max_width = find("terminal_info_max_width").map(str_to_int_ifp).value_or(0);

m_scroll_bar = find("terminal_scroll_bar").map(to_bool).value_or(false);
m_scroll_bar_scratch.resize((size_t)m_dimensions.line);
}

}
10 changes: 9 additions & 1 deletion src/terminal_ui.hh
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ public:
bool is_ok() const override { return (bool)m_window; }

void draw(const DisplayBuffer& display_buffer,
const Range<LineCount> range,
const LineCount buffer_line_count,
const Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face) override;
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face) override;

void draw_status(const DisplayLine& status_line,
const DisplayLine& mode_line,
Expand Down Expand Up @@ -168,6 +173,9 @@ private:
Codepoint m_padding_char = '~';
bool m_padding_fill = false;

bool m_scroll_bar = false;
Vector<char> m_scroll_bar_scratch;

bool m_dirty = false;

bool m_resize_pending = false;
Expand Down
10 changes: 9 additions & 1 deletion src/user_interface.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include "array_view.hh"
#include "hash_map.hh"
#include "range.hh"
#include "selection.hh"
#include "units.hh"

#include <functional>

Expand Down Expand Up @@ -65,8 +68,13 @@ public:
virtual void info_hide() = 0;

virtual void draw(const DisplayBuffer& display_buffer,
const Range<LineCount> range,
const LineCount buffer_line_count,
Vector<LineCount> selection_lines,
const Face& default_face,
const Face& padding_face) = 0;
const Face& padding_face,
const Face& scroll_bar_gutter_face,
const Face& scroll_bar_handle_face) = 0;

virtual void draw_status(const DisplayLine& status_line,
const DisplayLine& mode_line,
Expand Down