Extract most ncurses calls into NCursesUI::Window methods
Try to isolate ncurses as much as possible to explore alternate terminal UI implementation.
This commit is contained in:
parent
4c4eb61c18
commit
afd45c6755
|
@ -29,6 +29,131 @@ using std::max;
|
||||||
|
|
||||||
struct NCursesWin : WINDOW {};
|
struct NCursesWin : WINDOW {};
|
||||||
|
|
||||||
|
void NCursesUI::Window::create(const DisplayCoord& p, const DisplayCoord& s)
|
||||||
|
{
|
||||||
|
pos = p;
|
||||||
|
size = s;
|
||||||
|
win = (NCursesWin*)newpad((int)size.line, (int)size.column);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::destroy()
|
||||||
|
{
|
||||||
|
delwin(win);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::invalidate()
|
||||||
|
{
|
||||||
|
win = nullptr;
|
||||||
|
pos = DisplayCoord{};
|
||||||
|
size = DisplayCoord{};
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::refresh(bool force)
|
||||||
|
{
|
||||||
|
if (not win)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (force)
|
||||||
|
redrawwin(win);
|
||||||
|
|
||||||
|
DisplayCoord max_pos = pos + size - DisplayCoord{1,1};
|
||||||
|
pnoutrefresh(win, 0, 0, (int)pos.line, (int)pos.column,
|
||||||
|
(int)max_pos.line, (int)max_pos.column);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::move_cursor(DisplayCoord coord)
|
||||||
|
{
|
||||||
|
wmove(win, (int)coord.line, (int)coord.column);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::add_str(StringView str)
|
||||||
|
{
|
||||||
|
waddnstr(win, str.begin(), (int)str.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::clear_to_end_of_line()
|
||||||
|
{
|
||||||
|
wclrtoeol(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::draw_line(Palette& palette,
|
||||||
|
const DisplayLine& line,
|
||||||
|
ColumnCount col_index,
|
||||||
|
ColumnCount max_column,
|
||||||
|
const Face& default_face)
|
||||||
|
{
|
||||||
|
for (const DisplayAtom& atom : line)
|
||||||
|
{
|
||||||
|
set_face(palette, atom.face, default_face);
|
||||||
|
|
||||||
|
StringView content = atom.content();
|
||||||
|
if (content.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto remaining_columns = max_column - col_index;
|
||||||
|
if (content.back() == '\n' and
|
||||||
|
content.column_length() - 1 < remaining_columns)
|
||||||
|
{
|
||||||
|
add_str(content.substr(0, content.length()-1));
|
||||||
|
waddch(win, ' ');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
content = content.substr(0_col, remaining_columns);
|
||||||
|
add_str(content);
|
||||||
|
col_index += content.column_length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::set_face(Palette& palette, Face face, const Face& default_face)
|
||||||
|
{
|
||||||
|
if (m_active_pair != -1)
|
||||||
|
wattroff(win, COLOR_PAIR(m_active_pair));
|
||||||
|
|
||||||
|
face = merge_faces(default_face, face);
|
||||||
|
|
||||||
|
if (face.fg != Color::Default or face.bg != Color::Default)
|
||||||
|
{
|
||||||
|
m_active_pair = palette.get_color_pair(face);
|
||||||
|
wattron(win, COLOR_PAIR(m_active_pair));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto set_attribute = [&](Attribute attr, int nc_attr) {
|
||||||
|
(face.attributes & attr) ? wattron(win, nc_attr) : wattroff(win, nc_attr);
|
||||||
|
};
|
||||||
|
|
||||||
|
set_attribute(Attribute::Underline, A_UNDERLINE);
|
||||||
|
set_attribute(Attribute::Reverse, A_REVERSE);
|
||||||
|
set_attribute(Attribute::Blink, A_BLINK);
|
||||||
|
set_attribute(Attribute::Bold, A_BOLD);
|
||||||
|
set_attribute(Attribute::Dim, A_DIM);
|
||||||
|
#if defined(A_ITALIC)
|
||||||
|
set_attribute(Attribute::Italic, A_ITALIC);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::mark_dirty(LineCount pos, LineCount count)
|
||||||
|
{
|
||||||
|
wredrawln(win, (int)pos, (int)count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::set_background_color(Palette& palette, Face face)
|
||||||
|
{
|
||||||
|
wbkgdset(win, COLOR_PAIR(palette.get_color_pair(face)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int NCursesUI::Window::get_char()
|
||||||
|
{
|
||||||
|
return wgetch(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NCursesUI::Window::set_blocking(bool blocking)
|
||||||
|
{
|
||||||
|
wtimeout(win, blocking ? -1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr int NCursesUI::default_shift_function_key;
|
constexpr int NCursesUI::default_shift_function_key;
|
||||||
|
|
||||||
static constexpr StringView assistant_cat[] =
|
static constexpr StringView assistant_cat[] =
|
||||||
|
@ -66,14 +191,6 @@ static constexpr StringView assistant_dilbert[] =
|
||||||
R"( @ )",
|
R"( @ )",
|
||||||
R"( )"};
|
R"( )"};
|
||||||
|
|
||||||
static void set_attribute(WINDOW* window, int attribute, bool on)
|
|
||||||
{
|
|
||||||
if (on)
|
|
||||||
wattron(window, attribute);
|
|
||||||
else
|
|
||||||
wattroff(window, attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> T sq(T x) { return x * x; }
|
template<typename T> T sq(T x) { return x * x; }
|
||||||
|
|
||||||
constexpr struct { unsigned char r, g, b; } builtin_colors[] = {
|
constexpr struct { unsigned char r, g, b; } builtin_colors[] = {
|
||||||
|
@ -143,7 +260,28 @@ constexpr struct { unsigned char r, g, b; } builtin_colors[] = {
|
||||||
{0xd0,0xd0,0xd0}, {0xda,0xda,0xda}, {0xe4,0xe4,0xe4}, {0xee,0xee,0xee},
|
{0xd0,0xd0,0xd0}, {0xda,0xda,0xda}, {0xe4,0xe4,0xe4}, {0xee,0xee,0xee},
|
||||||
};
|
};
|
||||||
|
|
||||||
int NCursesUI::get_color(Color color)
|
const std::initializer_list<HashMap<Kakoune::Color, int>::Item>
|
||||||
|
NCursesUI::Palette::default_colors = {
|
||||||
|
{ Color::Default, -1 },
|
||||||
|
{ Color::Black, 0 },
|
||||||
|
{ Color::Red, 1 },
|
||||||
|
{ Color::Green, 2 },
|
||||||
|
{ Color::Yellow, 3 },
|
||||||
|
{ Color::Blue, 4 },
|
||||||
|
{ Color::Magenta, 5 },
|
||||||
|
{ Color::Cyan, 6 },
|
||||||
|
{ Color::White, 7 },
|
||||||
|
{ Color::BrightBlack, 8 },
|
||||||
|
{ Color::BrightRed, 9 },
|
||||||
|
{ Color::BrightGreen, 10 },
|
||||||
|
{ Color::BrightYellow, 11 },
|
||||||
|
{ Color::BrightBlue, 12 },
|
||||||
|
{ Color::BrightMagenta, 13 },
|
||||||
|
{ Color::BrightCyan, 14 },
|
||||||
|
{ Color::BrightWhite, 15 },
|
||||||
|
};
|
||||||
|
|
||||||
|
int NCursesUI::Palette::get_color(Color color)
|
||||||
{
|
{
|
||||||
auto it = m_colors.find(color);
|
auto it = m_colors.find(color);
|
||||||
if (it != m_colors.end())
|
if (it != m_colors.end())
|
||||||
|
@ -181,7 +319,7 @@ int NCursesUI::get_color(Color color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int NCursesUI::get_color_pair(const Face& face)
|
int NCursesUI::Palette::get_color_pair(const Face& face)
|
||||||
{
|
{
|
||||||
ColorPair colors{face.fg, face.bg};
|
ColorPair colors{face.fg, face.bg};
|
||||||
auto it = m_colorpairs.find(colors);
|
auto it = m_colorpairs.find(colors);
|
||||||
|
@ -195,27 +333,21 @@ int NCursesUI::get_color_pair(const Face& face)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCursesUI::set_face(NCursesWin* window, Face face, const Face& default_face)
|
bool NCursesUI::Palette::set_change_colors(bool change_colors)
|
||||||
{
|
{
|
||||||
if (m_active_pair != -1)
|
bool reset = false;
|
||||||
wattroff(window, COLOR_PAIR(m_active_pair));
|
if (can_change_color() and m_change_colors != change_colors)
|
||||||
|
|
||||||
face = merge_faces(default_face, face);
|
|
||||||
|
|
||||||
if (face.fg != Color::Default or face.bg != Color::Default)
|
|
||||||
{
|
{
|
||||||
m_active_pair = get_color_pair(face);
|
fputs("\033]104\007", stdout); // try to reset palette
|
||||||
wattron(window, COLOR_PAIR(m_active_pair));
|
fflush(stdout);
|
||||||
|
m_colorpairs.clear();
|
||||||
|
m_colors = default_colors;
|
||||||
|
m_next_color = 16;
|
||||||
|
m_next_pair = 1;
|
||||||
|
reset = true;
|
||||||
}
|
}
|
||||||
|
m_change_colors = change_colors;
|
||||||
set_attribute(window, A_UNDERLINE, face.attributes & Attribute::Underline);
|
return reset;
|
||||||
set_attribute(window, A_REVERSE, face.attributes & Attribute::Reverse);
|
|
||||||
set_attribute(window, A_BLINK, face.attributes & Attribute::Blink);
|
|
||||||
set_attribute(window, A_BOLD, face.attributes & Attribute::Bold);
|
|
||||||
set_attribute(window, A_DIM, face.attributes & Attribute::Dim);
|
|
||||||
#if defined(A_ITALIC)
|
|
||||||
set_attribute(window, A_ITALIC, face.attributes & Attribute::Italic);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static sig_atomic_t resize_pending = 0;
|
static sig_atomic_t resize_pending = 0;
|
||||||
|
@ -228,30 +360,8 @@ static void signal_handler(int)
|
||||||
EventManager::instance().force_signal(0);
|
EventManager::instance().force_signal(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::initializer_list<HashMap<Kakoune::Color, int>::Item>
|
|
||||||
default_colors = {
|
|
||||||
{ Color::Default, -1 },
|
|
||||||
{ Color::Black, 0 },
|
|
||||||
{ Color::Red, 1 },
|
|
||||||
{ Color::Green, 2 },
|
|
||||||
{ Color::Yellow, 3 },
|
|
||||||
{ Color::Blue, 4 },
|
|
||||||
{ Color::Magenta, 5 },
|
|
||||||
{ Color::Cyan, 6 },
|
|
||||||
{ Color::White, 7 },
|
|
||||||
{ Color::BrightBlack, 8 },
|
|
||||||
{ Color::BrightRed, 9 },
|
|
||||||
{ Color::BrightGreen, 10 },
|
|
||||||
{ Color::BrightYellow, 11 },
|
|
||||||
{ Color::BrightBlue, 12 },
|
|
||||||
{ Color::BrightMagenta, 13 },
|
|
||||||
{ Color::BrightCyan, 14 },
|
|
||||||
{ Color::BrightWhite, 15 },
|
|
||||||
};
|
|
||||||
|
|
||||||
NCursesUI::NCursesUI()
|
NCursesUI::NCursesUI()
|
||||||
: m_colors{default_colors},
|
: m_cursor{CursorMode::Buffer, {}},
|
||||||
m_cursor{CursorMode::Buffer, {}},
|
|
||||||
m_stdin_watcher{0, FdEvents::Read,
|
m_stdin_watcher{0, FdEvents::Read,
|
||||||
[this](FDWatcher&, FdEvents, EventMode) {
|
[this](FDWatcher&, FdEvents, EventMode) {
|
||||||
if (not m_on_key)
|
if (not m_on_key)
|
||||||
|
@ -278,7 +388,7 @@ NCursesUI::NCursesUI()
|
||||||
|
|
||||||
check_resize(true);
|
check_resize(true);
|
||||||
|
|
||||||
redraw();
|
redraw(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
NCursesUI::~NCursesUI()
|
NCursesUI::~NCursesUI()
|
||||||
|
@ -294,47 +404,20 @@ NCursesUI::~NCursesUI()
|
||||||
set_signal_handler(SIGCONT, SIG_DFL);
|
set_signal_handler(SIGCONT, SIG_DFL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCursesUI::Window::create(const DisplayCoord& p, const DisplayCoord& s)
|
void NCursesUI::redraw(bool force)
|
||||||
{
|
{
|
||||||
pos = p;
|
m_window.refresh(force);
|
||||||
size = s;
|
|
||||||
win = (NCursesWin*)newpad((int)size.line, (int)size.column);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCursesUI::Window::destroy()
|
|
||||||
{
|
|
||||||
delwin(win);
|
|
||||||
win = nullptr;
|
|
||||||
pos = DisplayCoord{};
|
|
||||||
size = DisplayCoord{};
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCursesUI::Window::refresh()
|
|
||||||
{
|
|
||||||
if (not win)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DisplayCoord max_pos = pos + size - DisplayCoord{1,1};
|
|
||||||
pnoutrefresh(win, 0, 0, (int)pos.line, (int)pos.column,
|
|
||||||
(int)max_pos.line, (int)max_pos.column);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCursesUI::redraw()
|
|
||||||
{
|
|
||||||
pnoutrefresh(m_window, 0, 0, 0, 0,
|
|
||||||
(int)m_dimensions.line + 1, (int)m_dimensions.column);
|
|
||||||
|
|
||||||
if (m_menu.columns != 0 or m_menu.pos.column > m_status_len)
|
if (m_menu.columns != 0 or m_menu.pos.column > m_status_len)
|
||||||
m_menu.refresh();
|
m_menu.refresh(false);
|
||||||
|
|
||||||
m_info.refresh();
|
m_info.refresh(false);
|
||||||
|
|
||||||
|
Window screen{{}, static_cast<NCursesWin*>(newscr)};
|
||||||
if (m_cursor.mode == CursorMode::Prompt)
|
if (m_cursor.mode == CursorMode::Prompt)
|
||||||
wmove(newscr, m_status_on_top ? 0 : (int)m_dimensions.line,
|
screen.move_cursor({m_status_on_top ? 0 : m_dimensions.line, m_cursor.coord.column});
|
||||||
(int)m_cursor.coord.column);
|
|
||||||
else
|
else
|
||||||
wmove(newscr, (int)(m_cursor.coord.line + content_line_offset()),
|
screen.move_cursor(m_cursor.coord + content_line_offset());
|
||||||
(int)m_cursor.coord.column);
|
|
||||||
|
|
||||||
doupdate();
|
doupdate();
|
||||||
}
|
}
|
||||||
|
@ -346,54 +429,18 @@ void NCursesUI::set_cursor(CursorMode mode, DisplayCoord coord)
|
||||||
|
|
||||||
void NCursesUI::refresh(bool force)
|
void NCursesUI::refresh(bool force)
|
||||||
{
|
{
|
||||||
if (force)
|
|
||||||
redrawwin(m_window);
|
|
||||||
|
|
||||||
if (m_dirty or force)
|
if (m_dirty or force)
|
||||||
redraw();
|
redraw(force);
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_str(WINDOW* win, StringView str)
|
|
||||||
{
|
|
||||||
waddnstr(win, str.begin(), (int)str.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCursesUI::draw_line(NCursesWin* window, const DisplayLine& line,
|
|
||||||
ColumnCount col_index, ColumnCount max_column,
|
|
||||||
const Face& default_face)
|
|
||||||
{
|
|
||||||
for (const DisplayAtom& atom : line)
|
|
||||||
{
|
|
||||||
set_face(window, atom.face, default_face);
|
|
||||||
|
|
||||||
StringView content = atom.content();
|
|
||||||
if (content.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto remaining_columns = max_column - col_index;
|
|
||||||
if (content.back() == '\n' and
|
|
||||||
content.column_length() - 1 < remaining_columns)
|
|
||||||
{
|
|
||||||
add_str(window, content.substr(0, content.length()-1));
|
|
||||||
waddch(window, ' ');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
content = content.substr(0_col, remaining_columns);
|
|
||||||
add_str(window, content);
|
|
||||||
col_index += content.column_length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const DisplayLine empty_line = { String(" "), {} };
|
static const DisplayLine empty_line = { String(" "), {} };
|
||||||
|
|
||||||
void NCursesUI::draw(const DisplayBuffer& display_buffer,
|
void NCursesUI::draw(const DisplayBuffer& display_buffer,
|
||||||
const Face& default_face,
|
const Face& default_face,
|
||||||
const Face& padding_face)
|
const Face& padding_face)
|
||||||
{
|
{
|
||||||
wbkgdset(m_window, COLOR_PAIR(get_color_pair(default_face)));
|
m_window.set_background_color(m_palette, default_face);
|
||||||
|
|
||||||
check_resize();
|
check_resize();
|
||||||
|
|
||||||
|
@ -402,20 +449,20 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer,
|
||||||
LineCount line_index = line_offset;
|
LineCount line_index = line_offset;
|
||||||
for (const DisplayLine& line : display_buffer.lines())
|
for (const DisplayLine& line : display_buffer.lines())
|
||||||
{
|
{
|
||||||
wmove(m_window, (int)line_index, 0);
|
m_window.move_cursor(line_index);
|
||||||
wclrtoeol(m_window);
|
m_window.clear_to_end_of_line();
|
||||||
draw_line(m_window, line, 0, dim.column, default_face);
|
m_window.draw_line(m_palette, line, 0, dim.column, default_face);
|
||||||
++line_index;
|
++line_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
wbkgdset(m_window, COLOR_PAIR(get_color_pair(padding_face)));
|
m_window.set_background_color(m_palette, padding_face);
|
||||||
set_face(m_window, padding_face, default_face);
|
m_window.set_face(m_palette, padding_face, default_face);
|
||||||
|
|
||||||
while (line_index < dim.line + line_offset)
|
while (line_index < dim.line + line_offset)
|
||||||
{
|
{
|
||||||
wmove(m_window, (int)line_index++, 0);
|
m_window.move_cursor(line_index++);
|
||||||
wclrtoeol(m_window);
|
m_window.clear_to_end_of_line();
|
||||||
waddch(m_window, '~');
|
m_window.add_str('~');
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
@ -425,13 +472,13 @@ void NCursesUI::draw_status(const DisplayLine& status_line,
|
||||||
const DisplayLine& mode_line,
|
const DisplayLine& mode_line,
|
||||||
const Face& default_face)
|
const Face& default_face)
|
||||||
{
|
{
|
||||||
const int status_line_pos = m_status_on_top ? 0 : (int)m_dimensions.line;
|
const LineCount status_line_pos = m_status_on_top ? 0 : m_dimensions.line;
|
||||||
wmove(m_window, status_line_pos, 0);
|
m_window.move_cursor(status_line_pos);
|
||||||
|
|
||||||
wbkgdset(m_window, COLOR_PAIR(get_color_pair(default_face)));
|
m_window.set_background_color(m_palette, default_face);
|
||||||
wclrtoeol(m_window);
|
m_window.clear_to_end_of_line();
|
||||||
|
|
||||||
draw_line(m_window, status_line, 0, m_dimensions.column, default_face);
|
m_window.draw_line(m_palette, status_line, 0, m_dimensions.column, default_face);
|
||||||
|
|
||||||
const auto mode_len = mode_line.length();
|
const auto mode_len = mode_line.length();
|
||||||
m_status_len = status_line.length();
|
m_status_len = status_line.length();
|
||||||
|
@ -439,8 +486,8 @@ void NCursesUI::draw_status(const DisplayLine& status_line,
|
||||||
if (mode_len < remaining)
|
if (mode_len < remaining)
|
||||||
{
|
{
|
||||||
ColumnCount col = m_dimensions.column - mode_len;
|
ColumnCount col = m_dimensions.column - mode_len;
|
||||||
wmove(m_window, status_line_pos, (int)col);
|
m_window.move_cursor({status_line_pos, col});
|
||||||
draw_line(m_window, mode_line, col, m_dimensions.column, default_face);
|
m_window.draw_line(m_palette, mode_line, col, m_dimensions.column, default_face);
|
||||||
}
|
}
|
||||||
else if (remaining > 2)
|
else if (remaining > 2)
|
||||||
{
|
{
|
||||||
|
@ -450,8 +497,8 @@ void NCursesUI::draw_status(const DisplayLine& status_line,
|
||||||
kak_assert(trimmed_mode_line.length() == remaining - 1);
|
kak_assert(trimmed_mode_line.length() == remaining - 1);
|
||||||
|
|
||||||
ColumnCount col = m_dimensions.column - remaining + 1;
|
ColumnCount col = m_dimensions.column - remaining + 1;
|
||||||
wmove(m_window, status_line_pos, (int)col);
|
m_window.move_cursor({status_line_pos, col});
|
||||||
draw_line(m_window, trimmed_mode_line, col, m_dimensions.column, default_face);
|
m_window.draw_line(m_palette, trimmed_mode_line, col, m_dimensions.column, default_face);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_set_title)
|
if (m_set_title)
|
||||||
|
@ -495,17 +542,17 @@ void NCursesUI::check_resize(bool force)
|
||||||
|
|
||||||
const bool info = (bool)m_info;
|
const bool info = (bool)m_info;
|
||||||
const bool menu = (bool)m_menu;
|
const bool menu = (bool)m_menu;
|
||||||
if (m_window) delwin(m_window);
|
if (m_window) m_window.destroy();
|
||||||
if (info) m_info.destroy();
|
if (info) m_info.destroy();
|
||||||
if (menu) m_menu.destroy();
|
if (menu) m_menu.destroy();
|
||||||
|
|
||||||
resize_term(ws.ws_row, ws.ws_col);
|
resize_term(ws.ws_row, ws.ws_col);
|
||||||
|
|
||||||
m_window = (NCursesWin*)newpad(ws.ws_row, ws.ws_col);
|
m_window.create({0, 0}, {ws.ws_row, ws.ws_col});
|
||||||
kak_assert(m_window);
|
kak_assert(m_window);
|
||||||
intrflush(m_window, false);
|
intrflush(m_window.win, false);
|
||||||
keypad(m_window, not m_builtin_key_parser);
|
keypad(m_window.win, not m_builtin_key_parser);
|
||||||
meta(m_window, true);
|
meta(m_window.win, true);
|
||||||
|
|
||||||
m_dimensions = DisplayCoord{ws.ws_row-1, ws.ws_col};
|
m_dimensions = DisplayCoord{ws.ws_row-1, ws.ws_col};
|
||||||
|
|
||||||
|
@ -531,7 +578,7 @@ Optional<Key> NCursesUI::get_next_key()
|
||||||
{
|
{
|
||||||
set_signal_handler(SIGWINCH, SIG_DFL);
|
set_signal_handler(SIGWINCH, SIG_DFL);
|
||||||
set_signal_handler(SIGCONT, SIG_DFL);
|
set_signal_handler(SIGCONT, SIG_DFL);
|
||||||
m_window = nullptr;
|
m_window.invalidate();
|
||||||
m_stdin_watcher.disable();
|
m_stdin_watcher.disable();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -544,9 +591,9 @@ Optional<Key> NCursesUI::get_next_key()
|
||||||
return resize(dimensions());
|
return resize(dimensions());
|
||||||
}
|
}
|
||||||
|
|
||||||
wtimeout(m_window, 0);
|
m_window.set_blocking(false);
|
||||||
const int c = wgetch(m_window);
|
const int c = m_window.get_char();
|
||||||
wtimeout(m_window, -1);
|
m_window.set_blocking(true);
|
||||||
|
|
||||||
if (c == ERR)
|
if (c == ERR)
|
||||||
return {};
|
return {};
|
||||||
|
@ -644,13 +691,13 @@ Optional<Key> NCursesUI::get_next_key()
|
||||||
ungetch(c);
|
ungetch(c);
|
||||||
struct getch_iterator
|
struct getch_iterator
|
||||||
{
|
{
|
||||||
getch_iterator(WINDOW* win) : window(win) {}
|
getch_iterator(Window& win) : window(win) {}
|
||||||
int operator*() { return wgetch(window); }
|
int operator*() { return window.get_char(); }
|
||||||
getch_iterator& operator++() { return *this; }
|
getch_iterator& operator++() { return *this; }
|
||||||
getch_iterator& operator++(int) { return *this; }
|
getch_iterator& operator++(int) { return *this; }
|
||||||
bool operator== (const getch_iterator&) const { return false; }
|
bool operator== (const getch_iterator&) const { return false; }
|
||||||
|
|
||||||
WINDOW* window;
|
Window& window;
|
||||||
};
|
};
|
||||||
return Key{utf8::codepoint(getch_iterator{m_window},
|
return Key{utf8::codepoint(getch_iterator{m_window},
|
||||||
getch_iterator{m_window})};
|
getch_iterator{m_window})};
|
||||||
|
@ -660,12 +707,12 @@ Optional<Key> NCursesUI::get_next_key()
|
||||||
|
|
||||||
constexpr auto direction = make_array({Key::Up, Key::Down, Key::Right, Key::Left, Key::Home, Key::End});
|
constexpr auto direction = make_array({Key::Up, Key::Down, Key::Right, Key::Left, Key::Home, Key::End});
|
||||||
auto parse_csi = [this, &direction]() -> Optional<Key> {
|
auto parse_csi = [this, &direction]() -> Optional<Key> {
|
||||||
const Codepoint c1 = wgetch(m_window);
|
const Codepoint c1 = m_window.get_char();
|
||||||
if (c1 >= 'A' and c1 <= 'F')
|
if (c1 >= 'A' and c1 <= 'F')
|
||||||
return Key{direction[c1 - 'A']};
|
return Key{direction[c1 - 'A']};
|
||||||
if (c1 == '1')
|
if (c1 == '1')
|
||||||
{
|
{
|
||||||
const Codepoint c2 = wgetch(m_window);
|
const Codepoint c2 = m_window.get_char();
|
||||||
if (c2 >= 'A' and c2 <= 'F')
|
if (c2 >= 'A' and c2 <= 'F')
|
||||||
return Key{direction[c2 - 'A']};
|
return Key{direction[c2 - 'A']};
|
||||||
if (c2 != ';')
|
if (c2 != ';')
|
||||||
|
@ -673,13 +720,13 @@ Optional<Key> NCursesUI::get_next_key()
|
||||||
ungetch(c2); ungetch(c1);
|
ungetch(c2); ungetch(c1);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const Codepoint c3 = wgetch(m_window);
|
const Codepoint c3 = m_window.get_char();
|
||||||
if (c3 < '2' or c3 > '8')
|
if (c3 < '2' or c3 > '8')
|
||||||
{
|
{
|
||||||
ungetch(c3); ungetch(c2); ungetch(c1);
|
ungetch(c3); ungetch(c2); ungetch(c1);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const Codepoint c4 = wgetch(m_window);
|
const Codepoint c4 = m_window.get_char();
|
||||||
if (c4 < 'A' or c4 > 'F')
|
if (c4 < 'A' or c4 > 'F')
|
||||||
{
|
{
|
||||||
ungetch(c4); ungetch(c3); ungetch(c2); ungetch(c1);
|
ungetch(c4); ungetch(c3); ungetch(c2); ungetch(c1);
|
||||||
|
@ -707,14 +754,14 @@ Optional<Key> NCursesUI::get_next_key()
|
||||||
|
|
||||||
if (c == 27)
|
if (c == 27)
|
||||||
{
|
{
|
||||||
wtimeout(m_window, 0);
|
m_window.set_blocking(false);
|
||||||
const int new_c = wgetch(m_window);
|
const int new_c = m_window.get_char();
|
||||||
if (new_c == '[') // potential CSI
|
if (new_c == '[') // potential CSI
|
||||||
{
|
{
|
||||||
if (auto key = parse_csi())
|
if (auto key = parse_csi())
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
wtimeout(m_window, -1);
|
m_window.set_blocking(true);
|
||||||
|
|
||||||
if (auto key = parse_key(new_c))
|
if (auto key = parse_key(new_c))
|
||||||
return alt(*key);
|
return alt(*key);
|
||||||
|
@ -738,9 +785,8 @@ void NCursesUI::draw_menu()
|
||||||
if (not m_menu)
|
if (not m_menu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto menu_bg = get_color_pair(m_menu.bg);
|
m_menu.set_face(m_palette, m_menu.bg, {});
|
||||||
wattron(m_menu.win, COLOR_PAIR(menu_bg));
|
m_menu.set_background_color(m_palette, m_menu.bg);
|
||||||
wbkgdset(m_menu.win, COLOR_PAIR(menu_bg));
|
|
||||||
|
|
||||||
const int item_count = (int)m_menu.items.size();
|
const int item_count = (int)m_menu.items.size();
|
||||||
if (m_menu.columns == 0)
|
if (m_menu.columns == 0)
|
||||||
|
@ -749,31 +795,31 @@ void NCursesUI::draw_menu()
|
||||||
kak_assert(m_menu.size.line == 1);
|
kak_assert(m_menu.size.line == 1);
|
||||||
ColumnCount pos = 0;
|
ColumnCount pos = 0;
|
||||||
|
|
||||||
wmove(m_menu.win, 0, 0);
|
m_menu.move_cursor({0, 0});
|
||||||
add_str(m_menu.win, m_menu.first_item > 0 ? "< " : " ");
|
m_menu.add_str(m_menu.first_item > 0 ? "< " : " ");
|
||||||
|
|
||||||
int i = m_menu.first_item;
|
int i = m_menu.first_item;
|
||||||
for (; i < item_count and pos < win_width; ++i)
|
for (; i < item_count and pos < win_width; ++i)
|
||||||
{
|
{
|
||||||
const DisplayLine& item = m_menu.items[i];
|
const DisplayLine& item = m_menu.items[i];
|
||||||
const ColumnCount item_width = item.length();
|
const ColumnCount item_width = item.length();
|
||||||
draw_line(m_menu.win, item, 0, win_width - pos,
|
m_menu.draw_line(m_palette, item, 0, win_width - pos,
|
||||||
i == m_menu.selected_item ? m_menu.fg : m_menu.bg);
|
i == m_menu.selected_item ? m_menu.fg : m_menu.bg);
|
||||||
|
|
||||||
if (item_width > win_width - pos)
|
if (item_width > win_width - pos)
|
||||||
add_str(m_menu.win, "…");
|
m_menu.add_str("…");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wattron(m_menu.win, COLOR_PAIR(menu_bg));
|
m_menu.set_face(m_palette, m_menu.bg, {});
|
||||||
add_str(m_menu.win, String{" "});
|
m_menu.add_str(String{" "});
|
||||||
}
|
}
|
||||||
pos += item_width + 1;
|
pos += item_width + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_face(m_menu.win, m_menu.bg, m_menu.bg);
|
m_menu.set_face(m_palette, m_menu.bg, {});
|
||||||
if (pos <= win_width)
|
if (pos <= win_width)
|
||||||
add_str(m_menu.win, String{' ', win_width - pos + 1});
|
m_menu.add_str(String{' ', win_width - pos + 1});
|
||||||
add_str(m_menu.win, i == item_count ? " " : ">");
|
m_menu.add_str(i == item_count ? " " : ">");
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -794,7 +840,7 @@ void NCursesUI::draw_menu()
|
||||||
|
|
||||||
for (auto line = 0_line; line < win_height; ++line)
|
for (auto line = 0_line; line < win_height; ++line)
|
||||||
{
|
{
|
||||||
wmove(m_menu.win, (int)line, 0);
|
m_menu.move_cursor(line);
|
||||||
for (int col = 0; col < m_menu.columns; ++col)
|
for (int col = 0; col < m_menu.columns; ++col)
|
||||||
{
|
{
|
||||||
int item_idx = (first_col + col) * (int)m_menu.size.line + (int)line;
|
int item_idx = (first_col + col) * (int)m_menu.size.line + (int)line;
|
||||||
|
@ -802,17 +848,17 @@ void NCursesUI::draw_menu()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const DisplayLine& item = m_menu.items[item_idx];
|
const DisplayLine& item = m_menu.items[item_idx];
|
||||||
draw_line(m_menu.win, item, 0, column_width,
|
m_menu.draw_line(m_palette, item, 0, column_width,
|
||||||
item_idx == m_menu.selected_item ? m_menu.fg : m_menu.bg);
|
item_idx == m_menu.selected_item ? m_menu.fg : m_menu.bg);
|
||||||
const ColumnCount pad = column_width - item.length();
|
const ColumnCount pad = column_width - item.length();
|
||||||
add_str(m_menu.win, String{' ', pad});
|
m_menu.add_str(String{' ', pad});
|
||||||
}
|
}
|
||||||
const bool is_mark = line >= mark_line and
|
const bool is_mark = line >= mark_line and
|
||||||
line < mark_line + mark_height;
|
line < mark_line + mark_height;
|
||||||
wclrtoeol(m_menu.win);
|
m_menu.clear_to_end_of_line();
|
||||||
wmove(m_menu.win, (int)line, (int)m_menu.size.column - 1);
|
m_menu.move_cursor({line, m_menu.size.column - 1});
|
||||||
wattron(m_menu.win, COLOR_PAIR(menu_bg));
|
m_menu.set_face(m_palette, m_menu.bg, {});
|
||||||
add_str(m_menu.win, is_mark ? "█" : "░");
|
m_menu.add_str(is_mark ? "█" : "░");
|
||||||
}
|
}
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -835,7 +881,7 @@ void NCursesUI::menu_show(ConstArrayView<DisplayLine> items,
|
||||||
{
|
{
|
||||||
if (m_menu)
|
if (m_menu)
|
||||||
{
|
{
|
||||||
mark_dirty(m_menu);
|
m_window.mark_dirty(m_menu.pos.line, m_menu.size.line);
|
||||||
m_menu.destroy();
|
m_menu.destroy();
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -949,7 +995,7 @@ void NCursesUI::menu_hide()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_menu.items.clear();
|
m_menu.items.clear();
|
||||||
mark_dirty(m_menu);
|
m_window.mark_dirty(m_menu.pos.line, m_menu.size.line);
|
||||||
m_menu.destroy();
|
m_menu.destroy();
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
|
||||||
|
@ -1137,11 +1183,12 @@ void NCursesUI::info_show(StringView title, StringView content,
|
||||||
|
|
||||||
m_info.create(anchor, info_box.size);
|
m_info.create(anchor, info_box.size);
|
||||||
|
|
||||||
wbkgd(m_info.win, COLOR_PAIR(get_color_pair(face)));
|
m_info.set_background_color(m_palette, face);
|
||||||
for (auto line = 0_line; line < info_box.size.line; ++line)
|
for (auto line = 0_line; line < info_box.size.line; ++line)
|
||||||
{
|
{
|
||||||
wmove(m_info.win, (int)line, 0);
|
m_info.move_cursor(line);
|
||||||
add_str(m_info.win, info_box.contents[(int)line]);
|
m_info.clear_to_end_of_line();
|
||||||
|
m_info.add_str(info_box.contents[(int)line]);
|
||||||
}
|
}
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -1150,16 +1197,11 @@ void NCursesUI::info_hide()
|
||||||
{
|
{
|
||||||
if (not m_info)
|
if (not m_info)
|
||||||
return;
|
return;
|
||||||
mark_dirty(m_info);
|
m_window.mark_dirty(m_info.pos.line, m_info.size.line);
|
||||||
m_info.destroy();
|
m_info.destroy();
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCursesUI::mark_dirty(const Window& win)
|
|
||||||
{
|
|
||||||
wredrawln(m_window, (int)win.pos.line, (int)win.size.line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCursesUI::set_on_key(OnKeyCallback callback)
|
void NCursesUI::set_on_key(OnKeyCallback callback)
|
||||||
{
|
{
|
||||||
m_on_key = std::move(callback);
|
m_on_key = std::move(callback);
|
||||||
|
@ -1246,20 +1288,13 @@ void NCursesUI::set_ui_options(const Options& options)
|
||||||
|
|
||||||
{
|
{
|
||||||
auto it = options.find("ncurses_change_colors"_sv);
|
auto it = options.find("ncurses_change_colors"_sv);
|
||||||
auto value = it == options.end() or
|
if (m_palette.set_change_colors(it == options.end() or
|
||||||
(it->value == "yes" or it->value == "true");
|
(it->value == "yes" or it->value == "true")))
|
||||||
|
|
||||||
if (can_change_color() and m_change_colors != value)
|
|
||||||
{
|
{
|
||||||
fputs("\033]104\007", stdout); // try to reset palette
|
m_window.m_active_pair = -1;
|
||||||
fflush(stdout);
|
m_menu.m_active_pair = -1;
|
||||||
m_colorpairs.clear();
|
m_info.m_active_pair = -1;
|
||||||
m_colors = default_colors;
|
|
||||||
m_next_color = 16;
|
|
||||||
m_next_pair = 1;
|
|
||||||
m_active_pair = -1;
|
|
||||||
}
|
}
|
||||||
m_change_colors = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1286,7 +1321,7 @@ void NCursesUI::set_ui_options(const Options& options)
|
||||||
m_builtin_key_parser = builtin_key_parser_it != options.end() and
|
m_builtin_key_parser = builtin_key_parser_it != options.end() and
|
||||||
(builtin_key_parser_it->value == "yes" or
|
(builtin_key_parser_it->value == "yes" or
|
||||||
builtin_key_parser_it->value == "true");
|
builtin_key_parser_it->value == "true");
|
||||||
keypad(m_window, not m_builtin_key_parser);
|
keypad(m_window.win, not m_builtin_key_parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
NCursesUI(const NCursesUI&) = delete;
|
NCursesUI(const NCursesUI&) = delete;
|
||||||
NCursesUI& operator=(const NCursesUI&) = delete;
|
NCursesUI& operator=(const NCursesUI&) = delete;
|
||||||
|
|
||||||
bool is_ok() const override { return m_window != nullptr; }
|
bool is_ok() const override { return (bool)m_window; }
|
||||||
|
|
||||||
void draw(const DisplayBuffer& display_buffer,
|
void draw(const DisplayBuffer& display_buffer,
|
||||||
const Face& default_face,
|
const Face& default_face,
|
||||||
|
@ -63,39 +63,61 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void check_resize(bool force = false);
|
void check_resize(bool force = false);
|
||||||
void redraw();
|
void redraw(bool force);
|
||||||
|
|
||||||
int get_color(Color color);
|
|
||||||
int get_color_pair(const Face& face);
|
|
||||||
void set_face(NCursesWin* window, Face face, const Face& default_face);
|
|
||||||
void draw_line(NCursesWin* window, const DisplayLine& line,
|
|
||||||
ColumnCount col_index, ColumnCount max_column,
|
|
||||||
const Face& default_face);
|
|
||||||
|
|
||||||
Optional<Key> get_next_key();
|
Optional<Key> get_next_key();
|
||||||
|
|
||||||
NCursesWin* m_window = nullptr;
|
struct Palette
|
||||||
|
{
|
||||||
DisplayCoord m_dimensions;
|
private:
|
||||||
|
static const std::initializer_list<HashMap<Kakoune::Color, int>::Item> default_colors;
|
||||||
|
|
||||||
using ColorPair = std::pair<Color, Color>;
|
using ColorPair = std::pair<Color, Color>;
|
||||||
HashMap<Color, int, MemoryDomain::Faces> m_colors;
|
HashMap<Color, int, MemoryDomain::Faces> m_colors = default_colors;
|
||||||
HashMap<ColorPair, int, MemoryDomain::Faces> m_colorpairs;
|
HashMap<ColorPair, int, MemoryDomain::Faces> m_colorpairs;
|
||||||
int m_next_color = 16;
|
int m_next_color = 16;
|
||||||
int m_next_pair = 1;
|
int m_next_pair = 1;
|
||||||
int m_active_pair = -1;
|
bool m_change_colors = true;
|
||||||
|
|
||||||
|
int get_color(Color color);
|
||||||
|
|
||||||
|
public:
|
||||||
|
int get_color_pair(const Face& face);
|
||||||
|
bool set_change_colors(bool change_colors);
|
||||||
|
};
|
||||||
|
|
||||||
|
Palette m_palette;
|
||||||
|
|
||||||
struct Window : Rect
|
struct Window : Rect
|
||||||
{
|
{
|
||||||
void create(const DisplayCoord& pos, const DisplayCoord& size);
|
void create(const DisplayCoord& pos, const DisplayCoord& size);
|
||||||
void destroy();
|
void destroy();
|
||||||
void refresh();
|
void invalidate();
|
||||||
|
void refresh(bool force);
|
||||||
|
void move_cursor(DisplayCoord coord);
|
||||||
|
void mark_dirty(LineCount pos, LineCount count);
|
||||||
|
void draw_line(Palette& palette,
|
||||||
|
const DisplayLine& line,
|
||||||
|
ColumnCount col_index,
|
||||||
|
ColumnCount max_column,
|
||||||
|
const Face& default_face);
|
||||||
|
void add_str(StringView str);
|
||||||
|
void clear_to_end_of_line();
|
||||||
|
void set_face(Palette& palette, Face face, const Face& default_face);
|
||||||
|
void set_background_color(Palette& palette, Face face);
|
||||||
|
int get_char();
|
||||||
|
void set_blocking(bool blocking);
|
||||||
|
|
||||||
explicit operator bool() const { return win; }
|
explicit operator bool() const { return win; }
|
||||||
|
|
||||||
NCursesWin* win = nullptr;
|
NCursesWin* win = nullptr;
|
||||||
|
int m_active_pair = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Window m_window;
|
||||||
|
|
||||||
|
DisplayCoord m_dimensions;
|
||||||
|
|
||||||
void mark_dirty(const Window& win);
|
void mark_dirty(const Window& win);
|
||||||
|
|
||||||
struct Menu : Window
|
struct Menu : Window
|
||||||
|
@ -146,7 +168,6 @@ private:
|
||||||
int m_shift_function_key = default_shift_function_key;
|
int m_shift_function_key = default_shift_function_key;
|
||||||
|
|
||||||
bool m_set_title = true;
|
bool m_set_title = true;
|
||||||
bool m_change_colors = true;
|
|
||||||
bool m_builtin_key_parser = false;
|
bool m_builtin_key_parser = false;
|
||||||
|
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user