From 7341dc1f1a9d08e286bb3f8ebf4016812fa2faa8 Mon Sep 17 00:00:00 2001 From: Tim Stack Date: Wed, 10 Apr 2024 22:18:52 -0700 Subject: [PATCH] [mouse] breathe some life back into mouse support --- src/breadcrumb_curses.cc | 10 ++- src/breadcrumb_curses.hh | 11 +-- src/filter_sub_source.cc | 4 +- src/hotkeys.cc | 3 +- src/listview_curses.cc | 168 +++++++++++++++++++++++--------------- src/listview_curses.hh | 58 +++++++------ src/lnav.cc | 54 +++--------- src/readline_curses.cc | 33 ++++---- src/readline_curses.hh | 2 +- src/statusview_curses.cc | 8 +- src/statusview_curses.hh | 6 +- src/textview_curses.cc | 118 +++++++++++++------------- src/textview_curses.hh | 15 +--- src/view_curses.cc | 9 ++ src/view_curses.hh | 68 +++++++++++++-- src/view_helpers.cc | 86 +++++++++++++++++-- src/view_helpers.hh | 10 +++ src/vt52_curses.cc | 36 ++++---- src/vt52_curses.hh | 30 ++----- src/xterm_mouse.hh | 6 ++ test/drive_view_colors.cc | 6 +- 21 files changed, 430 insertions(+), 311 deletions(-) diff --git a/src/breadcrumb_curses.cc b/src/breadcrumb_curses.cc index 47e01971..62778d0c 100644 --- a/src/breadcrumb_curses.cc +++ b/src/breadcrumb_curses.cc @@ -38,6 +38,7 @@ breadcrumb_curses::breadcrumb_curses() { this->bc_match_search_overlay.sos_parent = this; this->bc_match_source.set_reverse_selection(true); + this->bc_match_view.set_title("breadcrumb popup"); this->bc_match_view.set_selectable(true); this->bc_match_view.set_overlay_source(&this->bc_match_search_overlay); this->bc_match_view.set_sub_source(&this->bc_match_source); @@ -47,11 +48,11 @@ breadcrumb_curses::breadcrumb_curses() this->add_child_view(&this->bc_match_view); } -void +bool breadcrumb_curses::do_update() { if (!this->bc_line_source) { - return; + return false; } size_t crumb_index = 0; @@ -94,12 +95,14 @@ breadcrumb_curses::do_update() line_range lr{0, static_cast(width)}; view_curses::mvwattrline( - this->bc_window, this->bc_y, 0, crumbs_line, lr, role_t::VCR_STATUS); + this->bc_window, this->vc_y, 0, crumbs_line, lr, role_t::VCR_STATUS); if (this->bc_selected_crumb) { this->bc_match_view.set_x(sel_crumb_offset); } view_curses::do_update(); + + return true; } void @@ -172,6 +175,7 @@ breadcrumb_curses::reload_data() void breadcrumb_curses::focus() { + this->bc_match_view.set_y(this->vc_y + 1); this->bc_focused_crumbs = this->bc_line_source(); if (this->bc_focused_crumbs.empty()) { return; diff --git a/src/breadcrumb_curses.hh b/src/breadcrumb_curses.hh index d0ca7469..9db822e3 100644 --- a/src/breadcrumb_curses.hh +++ b/src/breadcrumb_curses.hh @@ -42,14 +42,6 @@ class breadcrumb_curses : public view_curses { public: breadcrumb_curses(); - void set_y(int y) - { - this->bc_y = y; - this->bc_match_view.set_y(y + 1); - } - - int get_y() const { return this->bc_y; } - void set_window(WINDOW* win) { this->bc_window = win; @@ -66,7 +58,7 @@ public: bool handle_key(int ch); - void do_update() override; + bool do_update() override; void reload_data(); @@ -90,7 +82,6 @@ private: WINDOW* bc_window{nullptr}; std::function()> bc_line_source; - int bc_y{0}; std::vector bc_focused_crumbs; nonstd::optional bc_selected_crumb; nonstd::optional bc_last_selected_crumb; diff --git a/src/filter_sub_source.cc b/src/filter_sub_source.cc index c9ee9106..128dbf2b 100644 --- a/src/filter_sub_source.cc +++ b/src/filter_sub_source.cc @@ -44,7 +44,7 @@ using namespace lnav::roles::literals; filter_sub_source::filter_sub_source(std::shared_ptr editor) : fss_editor(editor) { - this->fss_editor->set_left(25); + this->fss_editor->set_x(25); this->fss_editor->set_width(-1); this->fss_editor->set_save_history(!(lnav_data.ld_flags & LNF_SECURE_MODE)); this->fss_regex_context.set_highlighter(readline_regex_highlighter) @@ -656,7 +656,7 @@ filter_sub_source::rl_display_matches(readline_curses* rc) this->fss_match_view.set_window(this->tss_view->get_window()); this->fss_match_view.set_y(rc->get_y() + 1); - this->fss_match_view.set_x(rc->get_left() + rc->get_match_start()); + this->fss_match_view.set_x(rc->get_x() + rc->get_match_start()); this->fss_match_view.set_width(width + 3); this->fss_match_view.set_needs_update(); this->fss_match_view.reload_data(); diff --git a/src/hotkeys.cc b/src/hotkeys.cc index 66b93288..732eb2a8 100644 --- a/src/hotkeys.cc +++ b/src/hotkeys.cc @@ -275,8 +275,7 @@ handle_paging_key(int ch) } else { lnav_data.ld_rl_view->set_value( "error: mouse support is not available, make sure your " - "TERM is set to " - "xterm or xterm-256color"); + "TERM is set to xterm or xterm-256color"); } break; diff --git a/src/listview_curses.cc b/src/listview_curses.cc index 21cf1299..50c13ea5 100644 --- a/src/listview_curses.cc +++ b/src/listview_curses.cc @@ -29,6 +29,7 @@ * @file listview_curses.cc */ +#include #include #include "listview_curses.hh" @@ -40,10 +41,25 @@ #include "base/lnav_log.hh" #include "config.h" +using namespace std::chrono_literals; + list_gutter_source listview_curses::DEFAULT_GUTTER_SOURCE; listview_curses::listview_curses() : lv_scroll(noop_func{}) {} +bool +listview_curses::contains(int x, int y) const +{ + auto dim = this->get_dimensions(); + + if (this->vc_x <= x && x < this->vc_x + dim.second && this->vc_y <= y + && y < this->vc_y + dim.first) + { + return true; + } + return false; +} + void listview_curses::update_top_from_selection() { @@ -369,34 +385,36 @@ listview_curses::get_overlay_top(vis_line_t row, size_t count, size_t total) return 0_vl; } -void +bool listview_curses::do_update() { + bool retval = false; + if (this->lv_window == nullptr || this->lv_height == 0 || !this->vc_visible) { - view_curses::do_update(); - return; + return view_curses::do_update(); } std::vector row_overlay_content; vis_line_t height; unsigned long width; - this->update_top_from_selection(); this->get_dimensions(height, width); + if (height <= 0) { + return retval; + } + + this->update_top_from_selection(); while (this->vc_needs_update) { auto& vc = view_colors::singleton(); vis_line_t row; attr_line_t overlay_line; struct line_range lr; unsigned long wrap_width; - int y = this->lv_y, bottom; + int y = this->vc_y, bottom; auto role_attrs = vc.attrs_for_role(this->vc_default_role); - if (height <= 0) { - return; - } - + retval = true; if (this->vc_width > 0) { width = std::min((unsigned long) this->vc_width, width); } @@ -413,14 +431,16 @@ listview_curses::do_update() std::vector rows( std::min((size_t) height, row_count - (int) this->lv_top)); this->lv_source->listview_value_for_rows(*this, row, rows); + this->lv_display_lines.clear(); while (y < bottom) { lr.lr_start = this->lv_left; lr.lr_end = this->lv_left + wrap_width; if (this->lv_overlay_source != nullptr && this->lv_overlay_source->list_static_overlay( - *this, y - this->lv_y, bottom - this->lv_y, overlay_line)) + *this, y - this->vc_y, bottom - this->vc_y, overlay_line)) { - mvwattrline(this->lv_window, y, this->lv_x, overlay_line, lr); + this->lv_display_lines.push_back(static_overlay_content{}); + mvwattrline(this->lv_window, y, this->vc_x, overlay_line, lr); overlay_line.clear(); ++y; } else if (row < (int) row_count) { @@ -430,14 +450,15 @@ listview_curses::do_update() require_ge(attr.sa_range.lr_start, 0); } + this->lv_display_lines.push_back(main_content{row}); view_curses::mvwattrline_result write_res; do { if (this->lv_word_wrap) { - mvwhline(this->lv_window, y, this->lv_x, ' ', width); + mvwhline(this->lv_window, y, this->vc_x, ' ', width); } write_res = mvwattrline(this->lv_window, y, - this->lv_x, + this->vc_x, al, lr, this->vc_default_role); @@ -469,9 +490,11 @@ listview_curses::do_update() ov_hdr_attrs.ta_attrs |= A_UNDERLINE; auto ov_hdr = hdr.value().with_attr_for_all( VC_STYLE.value(ov_hdr_attrs)); + this->lv_display_lines.push_back( + static_overlay_content{}); mvwattrline(this->lv_window, y, - this->lv_x, + this->vc_x, ov_hdr, lr, role_t::VCR_STATUS_INFO); @@ -488,9 +511,11 @@ listview_curses::do_update() row_overlay_content[overlay_row].with_attr_for_all( VC_ROLE.value(role_t::VCR_CURSOR_LINE)); } + this->lv_display_lines.push_back( + overlay_content{overlay_row}); mvwattrline(this->lv_window, y, - this->lv_x, + this->vc_x, row_overlay_content[overlay_row], lr, role_t::VCR_ALT_ROW); @@ -545,7 +570,7 @@ listview_curses::do_update() nullptr); mvwaddch(this->lv_window, gutter_y, - this->lv_x + width - 2, + this->vc_x + width - 2, ch); } } @@ -558,7 +583,8 @@ listview_curses::do_update() vc.ensure_color_pair(role_attrs.ta_fg_color, role_attrs.ta_bg_color), nullptr); - mvwhline(this->lv_window, y, this->lv_x, ' ', width); + this->lv_display_lines.push_back(empty_space{}); + mvwhline(this->lv_window, y, this->vc_x, ' ', width); ++y; blank_rows += 1; } @@ -577,19 +603,18 @@ listview_curses::do_update() double progress = 1.0; double coverage = 1.0; double adjusted_height = (double) row_count / (double) height; - vis_line_t lines; if (row_count > 0) { progress = (double) this->lv_top / (double) row_count; coverage = (double) height / (double) row_count; } - y = this->lv_y + (int) (progress * (double) height); - lines = vis_line_t( - y + std::min((int) height, (int) (coverage * (double) height))); + this->lv_scroll_top = (int) (progress * (double) height); + this->lv_scroll_bottom = this->lv_scroll_top + + std::min((int) height, (int) (coverage * (double) height)); - for (unsigned int gutter_y = this->lv_y; - gutter_y < (this->lv_y + height); + for (unsigned int gutter_y = this->vc_y; + gutter_y < (this->vc_y + height); gutter_y++) { int range_start = 0, range_end; @@ -600,14 +625,14 @@ listview_curses::do_update() if (row_count > 0) { range_start - = (double) (gutter_y - this->lv_y) * adjusted_height; + = (double) (gutter_y - this->vc_y) * adjusted_height; } range_end = range_start + adjusted_height; this->lv_gutter_source->listview_gutter_value_for_range( *this, range_start, range_end, ch, role, bar_role); - if (gutter_y >= (unsigned int) y - && gutter_y <= (unsigned int) lines) + if (gutter_y >= this->vc_y + this->lv_scroll_top + && gutter_y <= this->vc_y + this->lv_scroll_bottom) { role = bar_role; } @@ -617,26 +642,26 @@ listview_curses::do_update() attrs.ta_attrs, vc.ensure_color_pair(attrs.ta_fg_color, attrs.ta_bg_color), nullptr); - mvwaddch(this->lv_window, gutter_y, this->lv_x + width - 1, ch); + mvwaddch(this->lv_window, gutter_y, this->vc_x + width - 1, ch); } - wmove(this->lv_window, this->lv_y + height - 1, this->lv_x); + wmove(this->lv_window, this->vc_y + height - 1, this->vc_x); } if (this->lv_show_bottom_border) { cchar_t row_ch[width]; - int y = this->lv_y + height - 1; + int y = this->vc_y + height - 1; - mvwin_wchnstr(this->lv_window, y, this->lv_x, row_ch, width - 1); + mvwin_wchnstr(this->lv_window, y, this->vc_x, row_ch, width - 1); for (unsigned long lpc = 0; lpc < width - 1; lpc++) { row_ch[lpc].attr |= A_UNDERLINE; } - mvwadd_wchnstr(this->lv_window, y, this->lv_x, row_ch, width - 1); + mvwadd_wchnstr(this->lv_window, y, this->vc_x, row_ch, width - 1); } this->vc_needs_update = false; } - view_curses::do_update(); + return view_curses::do_update() || retval; } void @@ -766,6 +791,9 @@ scroll_polarity(mouse_button_t button) bool listview_curses::handle_mouse(mouse_event& me) { + auto GUTTER_REPEAT_DELAY + = std::chrono::duration_cast(100ms).count(); + vis_line_t inner_height, height; struct timeval diff; unsigned long width; @@ -776,30 +804,40 @@ listview_curses::handle_mouse(mouse_event& me) switch (me.me_button) { case mouse_button_t::BUTTON_SCROLL_UP: - case mouse_button_t::BUTTON_SCROLL_DOWN: - if (diff.tv_sec > 0 || diff.tv_usec > 80000) { - this->lv_scroll_accel = 1; - this->lv_scroll_velo = 0; - } else { - this->lv_scroll_accel += 2; - } - this->lv_scroll_velo += this->lv_scroll_accel; - - this->shift_top(vis_line_t(scroll_polarity(me.me_button) - * this->lv_scroll_velo), + case mouse_button_t::BUTTON_SCROLL_DOWN: { + this->shift_top(vis_line_t(scroll_polarity(me.me_button) * 2_vl), true); - break; + return true; + } default: break; } - this->lv_mouse_time = me.me_time; - - if (me.me_button != mouse_button_t::BUTTON_LEFT || inner_height == 0 - || (this->lv_mouse_mode != lv_mode_t::DRAG - && me.me_x < (int) (width - 2))) - { + if (me.me_button != mouse_button_t::BUTTON_LEFT || inner_height == 0) { return false; } + switch (this->lv_mouse_mode) { + case lv_mode_t::NONE: { + if (me.me_x < (int) (width - 2)) { + return false; + } + break; + } + case lv_mode_t::DRAG: + break; + case lv_mode_t::UP: + case lv_mode_t::DOWN: + if (me.me_x < (int) (width - 2)) { + return true; + } + break; + } + if (me.me_state != mouse_button_state_t::BUTTON_STATE_RELEASED + && this->lv_mouse_mode != lv_mode_t::DRAG && diff.tv_sec == 0 + && diff.tv_usec < GUTTER_REPEAT_DELAY) + { + return true; + } + this->lv_mouse_time = me.me_time; if (me.me_state == mouse_button_state_t::BUTTON_STATE_RELEASED) { this->lv_mouse_y = -1; @@ -807,19 +845,14 @@ listview_curses::handle_mouse(mouse_event& me) return true; } - int scroll_top, scroll_bottom, shift_amount = 0, new_top = 0; - double top_pct, bot_pct, pct; - - top_pct = (double) this->get_top() / (double) inner_height; - bot_pct = (double) this->get_bottom() / (double) inner_height; - scroll_top = (this->get_y() + (int) (top_pct * (double) height)); - scroll_bottom = (this->get_y() + (int) (bot_pct * (double) height)); + int shift_amount = 0; if (this->lv_mouse_mode == lv_mode_t::NONE) { - if ((scroll_top - 1) <= me.me_y && me.me_y <= (scroll_bottom + 1)) { + if (this->lv_scroll_top <= me.me_y && me.me_y <= this->lv_scroll_bottom) + { this->lv_mouse_mode = lv_mode_t::DRAG; - this->lv_mouse_y = me.me_y - scroll_top; - } else if (me.me_y < scroll_top) { + this->lv_mouse_y = me.me_y - this->lv_scroll_top; + } else if (me.me_y < this->lv_scroll_top) { this->lv_mouse_mode = lv_mode_t::UP; } else { this->lv_mouse_mode = lv_mode_t::DOWN; @@ -832,27 +865,28 @@ listview_curses::handle_mouse(mouse_event& me) break; case lv_mode_t::UP: - if (me.me_y < scroll_top) { + if (me.me_y < this->lv_scroll_top) { shift_amount = -1 * height; } break; case lv_mode_t::DOWN: - if (me.me_y > scroll_bottom) { + if (me.me_y > this->lv_scroll_bottom) { shift_amount = height; } break; - case lv_mode_t::DRAG: - pct = (double) inner_height / (double) height; - new_top = me.me_y - this->get_y() - this->lv_mouse_y; + case lv_mode_t::DRAG: { + auto pct = (double) inner_height / (double) height; + auto new_top = me.me_y - this->lv_mouse_y; new_top = (int) floor(((double) new_top * pct) + 0.5); - this->set_top(vis_line_t(new_top)); + this->set_top(vis_line_t(new_top), true); break; + } } if (shift_amount != 0) { - this->shift_top(vis_line_t(shift_amount)); + this->shift_top(vis_line_t(shift_amount), true); } return true; diff --git a/src/listview_curses.hh b/src/listview_curses.hh index e4232032..58fd4e84 100644 --- a/src/listview_curses.hh +++ b/src/listview_curses.hh @@ -307,26 +307,6 @@ public: /** @return The curses window this view is attached to. */ WINDOW* get_window() const { return this->lv_window; } - void set_y(unsigned int y) - { - if (y != this->lv_y) { - this->lv_y = y; - this->set_needs_update(); - } - } - - unsigned int get_y() const { return this->lv_y; } - - void set_x(unsigned int x) - { - if (x != this->lv_x) { - this->lv_x = x; - this->set_needs_update(); - } - } - - unsigned int get_x() const { return this->lv_x; } - /** * Set the line number to be displayed at the top of the view. If the * value is invalid, flash() will be called. If the value is valid, the @@ -478,7 +458,7 @@ public: getmaxyx(this->lv_window, height, width_out); if (this->lv_height < 0) { height_out = vis_line_t(height) + this->lv_height - - vis_line_t(this->lv_y); + - vis_line_t(this->vc_y); if (height_out < 0_vl) { height_out = 0_vl; } @@ -486,8 +466,8 @@ public: height_out = this->lv_height; } } - if (this->lv_x < width_out) { - width_out -= this->lv_x; + if (this->vc_x < width_out) { + width_out -= this->vc_x; } else { width_out = 0; } @@ -514,9 +494,11 @@ public: /** * Query the data source and draw the visible lines on the display. */ - void do_update(); + bool do_update() override; - bool handle_mouse(mouse_event& me); + bool handle_mouse(mouse_event& me) override; + + bool contains(int x, int y) const override; listview_curses& set_tail_space(vis_line_t space) { @@ -527,14 +509,14 @@ public: vis_line_t get_tail_space() const { return this->lv_tail_space; } - void log_state() + void log_state() override { log_debug("listview_curses=%p", this); log_debug( - " lv_title=%s; lv_y=%u; lv_top=%d; lv_left=%d; lv_height=%d; " + " lv_title=%s; vc_y=%u; lv_top=%d; lv_left=%d; lv_height=%d; " "lv_selection=%d; inner_height=%d", this->lv_title.c_str(), - this->lv_y, + this->vc_y, (int) this->lv_top, (int) this->lv_left, this->lv_height, @@ -572,8 +554,6 @@ protected: list_overlay_source* lv_overlay_source{nullptr}; action lv_scroll; /*< The scroll action. */ WINDOW* lv_window{nullptr}; /*< The window that contains this view. */ - unsigned int lv_x{0}; - unsigned int lv_y{0}; /*< The y offset of this view. */ vis_line_t lv_top{0}; /*< The line at the top of the view. */ unsigned int lv_left{0}; /*< The column at the left of the view. */ vis_line_t lv_height{0}; /*< The abs/rel height of the view. */ @@ -598,6 +578,24 @@ protected: int lv_mouse_y{-1}; lv_mode_t lv_mouse_mode{lv_mode_t::NONE}; vis_line_t lv_tail_space{1}; + + struct main_content { + vis_line_t mc_line; + }; + struct static_overlay_content {}; + struct overlay_content { + vis_line_t oc_line; + }; + struct empty_space {}; + + using display_line_content_t = mapbox::util::variant; + + std::vector lv_display_lines; + unsigned int lv_scroll_top{0}; + unsigned int lv_scroll_bottom{0}; }; #endif diff --git a/src/lnav.cc b/src/lnav.cc index 7ee88ab8..581cb6e4 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -665,47 +665,6 @@ update_view_position(listview_curses* lv) }; } -class lnav_behavior : public mouse_behavior { -public: - void mouse_event(int button, bool release, int x, int y) override - { - textview_curses* tc = *(lnav_data.ld_view_stack.top()); - struct mouse_event me; - - switch (button & xterm_mouse::XT_BUTTON__MASK) { - case xterm_mouse::XT_BUTTON1: - me.me_button = mouse_button_t::BUTTON_LEFT; - break; - case xterm_mouse::XT_BUTTON2: - me.me_button = mouse_button_t::BUTTON_MIDDLE; - break; - case xterm_mouse::XT_BUTTON3: - me.me_button = mouse_button_t::BUTTON_RIGHT; - break; - case xterm_mouse::XT_SCROLL_UP: - me.me_button = mouse_button_t::BUTTON_SCROLL_UP; - break; - case xterm_mouse::XT_SCROLL_DOWN: - me.me_button = mouse_button_t::BUTTON_SCROLL_DOWN; - break; - } - - if (button & xterm_mouse::XT_DRAG_FLAG) { - me.me_state = mouse_button_state_t::BUTTON_STATE_DRAGGED; - } else if (release) { - me.me_state = mouse_button_state_t::BUTTON_STATE_RELEASED; - } else { - me.me_state = mouse_button_state_t::BUTTON_STATE_PRESSED; - } - - gettimeofday(&me.me_time, nullptr); - me.me_x = x - 1; - me.me_y = y - tc->get_y() - 1; - - tc->handle_mouse(me); - } -}; - static bool handle_config_ui_key(int ch) { @@ -1373,11 +1332,11 @@ looper() auto top_source = injector::get>(); - lnav_data.ld_status[LNS_TOP].set_top(0); + lnav_data.ld_status[LNS_TOP].set_y(0); lnav_data.ld_status[LNS_TOP].set_default_role( role_t::VCR_INACTIVE_STATUS); lnav_data.ld_status[LNS_TOP].set_data_source(top_source.get()); - lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc->get_height() + 1)); + lnav_data.ld_status[LNS_BOTTOM].set_y(-(rlc->get_height() + 1)); for (auto& stat_bar : lnav_data.ld_status) { stat_bar.set_window(lnav_data.ld_window); } @@ -1596,7 +1555,9 @@ looper() { lnav_data.ld_view_stack.set_needs_update(); } - lnav_data.ld_view_stack.do_update(); + if (lnav_data.ld_view_stack.do_update()) { + breadcrumb_view.set_needs_update(); + } lnav_data.ld_doc_view.do_update(); lnav_data.ld_example_view.do_update(); lnav_data.ld_match_view.do_update(); @@ -1685,6 +1646,11 @@ looper() gettimeofday(¤t_time, nullptr); lnav_data.ld_input_dispatcher.poll(current_time); + if (lb.lb_last_view != nullptr) { + lb.lb_last_event.me_time = current_time; + lb.lb_last_view->handle_mouse(lb.lb_last_event); + } + if (rc < 0) { switch (errno) { case 0: diff --git a/src/readline_curses.cc b/src/readline_curses.cc index 703a8c44..dc6bab35 100644 --- a/src/readline_curses.cc +++ b/src/readline_curses.cc @@ -821,7 +821,7 @@ readline_curses::start() if (this->vc_width > 0) { ws.ws_col = this->vc_width; } else if (this->vc_width < 0) { - ws.ws_col -= this->vc_left; + ws.ws_col -= this->vc_x; ws.ws_col += this->vc_width; } @@ -1294,11 +1294,11 @@ readline_curses::check_poll_set(const std::vector& pollfds) rc = read(this->rc_pty[RCF_MASTER], buffer, sizeof(buffer)); if (rc > 0) { - int old_x = this->vc_x; + int old_x = this->vc_cursor_x; this->rc_suggestion.clear(); this->map_output(buffer, rc); - if (this->vc_x != old_x) { + if (this->vc_cursor_x != old_x) { this->rc_change(this); } } @@ -1451,7 +1451,7 @@ readline_curses::focus(int context, al.append(lnav::roles::suggestion(this->rc_suggestion)); view_curses::mvwattrline(this->vc_window, this->get_actual_y(), - this->vc_left, + this->vc_x, al, line_range{0, (int) this->get_actual_width()}); if (!initial.empty()) { @@ -1495,7 +1495,7 @@ readline_curses::abort() { char buffer[1024]; - this->vc_x = 0; + this->vc_cursor_x = 0; snprintf(buffer, sizeof(buffer), "a"); if (sendstring(this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer)) == -1) @@ -1600,11 +1600,11 @@ readline_curses::clear_possibilities(int context, std::string type) } } -void +bool readline_curses::do_update() { if (!this->vc_visible || this->vc_window == nullptr) { - return; + return false; } auto actual_width = this->get_actual_width(); @@ -1614,7 +1614,7 @@ readline_curses::do_update() attr_line_t alt_al; auto& vc = view_colors::singleton(); - wmove(this->vc_window, this->get_actual_y(), this->vc_left); + wmove(this->vc_window, this->get_actual_y(), this->vc_x); auto attrs = vc.attrs_for_role(role_t::VCR_TEXT); wattr_set(this->vc_window, attrs.ta_attrs, @@ -1642,7 +1642,7 @@ readline_curses::do_update() lr.lr_end = this->rc_value.length(); view_curses::mvwattrline(this->vc_window, this->get_actual_y(), - this->vc_left, + this->vc_x, this->rc_value, lr); this->set_x(0); @@ -1654,24 +1654,27 @@ readline_curses::do_update() auto al = this->vc_line; if (hl != nullptr) { - hl(al, this->vc_left + this->vc_x); + hl(al, this->vc_x + this->vc_cursor_x); } al.append(lnav::roles::suggestion(this->rc_suggestion)); view_curses::mvwattrline(this->vc_window, this->get_actual_y(), - this->vc_left, + this->vc_x, al, line_range{0, (int) actual_width}); - wmove( - this->vc_window, this->get_actual_y(), this->vc_left + this->vc_x); + wmove(this->vc_window, + this->get_actual_y(), + this->vc_x + this->vc_cursor_x); } + + return true; } std::string readline_curses::get_match_string() const { - auto len = std::min((size_t) this->vc_x, this->rc_line_buffer.size()) + auto len = std::min((size_t) this->vc_cursor_x, this->rc_line_buffer.size()) - this->rc_match_start; auto* context = this->get_active_context(); @@ -1734,7 +1737,7 @@ readline_curses::window_change() if (this->vc_width > 0) { ws.ws_col = this->vc_width; } else if (this->vc_width < 0) { - ws.ws_col -= this->vc_left; + ws.ws_col -= this->vc_x; ws.ws_col += this->vc_width; } if (ioctl(this->rc_pty[RCF_MASTER], TIOCSWINSZ, &ws) == -1) { diff --git a/src/readline_curses.hh b/src/readline_curses.hh index 3c68c8ca..a05236e2 100644 --- a/src/readline_curses.hh +++ b/src/readline_curses.hh @@ -186,7 +186,7 @@ public: void start(); - void do_update() override; + bool do_update() override; void window_change(); diff --git a/src/statusview_curses.cc b/src/statusview_curses.cc index f27bff00..146f741b 100644 --- a/src/statusview_curses.cc +++ b/src/statusview_curses.cc @@ -84,7 +84,7 @@ status_field::set_stitch_value(role_t left, role_t right) sa.emplace_back(lr, VC_ROLE.value(right)); } -void +bool statusview_curses::do_update() { int top, field, field_count, left = 0, right; @@ -92,13 +92,13 @@ statusview_curses::do_update() unsigned long width, height; if (!this->vc_visible || this->sc_window == nullptr) { - return; + return false; } getmaxyx(this->sc_window, height, width); this->window_change(); - top = this->sc_top < 0 ? height + this->sc_top : this->sc_top; + top = this->vc_y < 0 ? height + this->vc_y : this->vc_y; right = width; auto attrs = vc.attrs_for_role( this->sc_enabled ? this->sc_default_role : role_t::VCR_INACTIVE_STATUS); @@ -181,6 +181,8 @@ statusview_curses::do_update() } } wmove(this->sc_window, top + 1, 0); + + return true; } void diff --git a/src/statusview_curses.hh b/src/statusview_curses.hh index c9c59161..46f00ab9 100644 --- a/src/statusview_curses.hh +++ b/src/statusview_curses.hh @@ -162,9 +162,6 @@ public: void set_data_source(status_data_source* src) { this->sc_source = src; } status_data_source* get_data_source() { return this->sc_source; } - void set_top(int top) { this->sc_top = top; } - int get_top() const { return this->sc_top; } - void set_window(WINDOW* win) { this->sc_window = win; } WINDOW* get_window() { return this->sc_window; } @@ -176,12 +173,11 @@ public: void window_change(); - void do_update() override; + bool do_update() override; private: status_data_source* sc_source{nullptr}; WINDOW* sc_window{nullptr}; - int sc_top{0}; bool sc_enabled{true}; role_t sc_default_role{role_t::VCR_STATUS}; }; diff --git a/src/textview_curses.cc b/src/textview_curses.cc index f1954e94..2781d817 100644 --- a/src/textview_curses.cc +++ b/src/textview_curses.cc @@ -396,8 +396,7 @@ textview_curses::handle_mouse(mouse_event& me) unsigned long width; vis_line_t height; - if (this->tc_selection_start == -1_vl && listview_curses::handle_mouse(me)) - { + if (!this->tc_selection_start && listview_curses::handle_mouse(me)) { return true; } @@ -411,64 +410,58 @@ textview_curses::handle_mouse(mouse_event& me) return false; } - vis_line_t mouse_line(this->get_top() + me.me_y); - - if (mouse_line > this->get_bottom()) { - mouse_line = this->get_bottom(); - } - + auto mouse_line = this->lv_display_lines[me.me_y]; this->get_dimensions(height, width); switch (me.me_state) { - case mouse_button_state_t::BUTTON_STATE_PRESSED: - this->tc_selection_start = mouse_line; - this->tc_selection_last = -1_vl; - this->tc_selection_cleared = false; + case mouse_button_state_t::BUTTON_STATE_PRESSED: { + if (!this->lv_selectable) { + this->set_selectable(true); + } + mouse_line.match( + [this, &me](const main_content& mc) { + if (me.is_modifier_pressed(mouse_event::modifier_t::shift)) + { + this->tc_selection_start = mc.mc_line; + } + this->set_selection(mc.mc_line); + this->tc_press_event = me; + }, + [](const static_overlay_content& soc) { + + }, + [](const overlay_content& oc) { + + }, + [](const empty_space& es) {}); break; - case mouse_button_state_t::BUTTON_STATE_DRAGGED: + } + case mouse_button_state_t::BUTTON_STATE_DRAGGED: { if (me.me_y <= 0) { - this->shift_top(-1_vl); + this->shift_selection(listview_curses::shift_amount_t::up_line); me.me_y = 0; - mouse_line = this->get_top(); - } - if (me.me_y >= height - && this->get_top() < this->get_top_for_last_row()) + mouse_line = main_content{this->get_top()}; + } else if (me.me_y >= height + && this->get_top() < this->get_top_for_last_row()) { - this->shift_top(1_vl); + this->shift_selection( + listview_curses::shift_amount_t::down_line); me.me_y = height; - mouse_line = this->get_bottom(); - } - - if (this->tc_selection_last == mouse_line) - break; - - if (this->tc_selection_last != -1) { - this->toggle_user_mark(&textview_curses::BM_USER, - this->tc_selection_start, - this->tc_selection_last); - } - if (this->tc_selection_start == mouse_line) { - this->tc_selection_last = -1_vl; - } else { - if (!this->tc_selection_cleared) { - if (this->tc_sub_source != nullptr) { - this->tc_sub_source->text_clear_marks(&BM_USER); - } - this->tc_bookmarks[&BM_USER].clear(); - - this->tc_selection_cleared = true; - } - this->toggle_user_mark( - &BM_USER, this->tc_selection_start, mouse_line); - this->tc_selection_last = mouse_line; + } else if (mouse_line.is()) { + this->set_selection(mouse_line.get().mc_line); } - this->reload_data(); break; - case mouse_button_state_t::BUTTON_STATE_RELEASED: - this->tc_selection_start = -1_vl; - this->tc_selection_last = -1_vl; - this->tc_selection_cleared = false; + } + case mouse_button_state_t::BUTTON_STATE_RELEASED: { + if (this->tc_selection_start) { + this->toggle_user_mark(&BM_USER, + this->tc_selection_start.value(), + this->get_selection()); + this->reload_data(); + } + this->tc_selection_start = nonstd::nullopt; break; + } } return true; @@ -509,15 +502,28 @@ textview_curses::textview_value_for_row(vis_line_t row, attr_line_t& value_out) format_name = format_attr_opt.value().get(); } - if (this->is_selectable() && row == this->get_selection() - && this->tc_cursor_role && this->tc_disabled_cursor_role) + if (this->is_selectable() && this->tc_cursor_role + && this->tc_disabled_cursor_role) { - auto role = this->get_overlay_selection() - ? this->tc_disabled_cursor_role.value() - : this->tc_cursor_role.value(); + vis_line_t sel_start, sel_end; - sa.emplace_back(line_range{orig_line.lr_start, -1}, - VC_ROLE.value(role)); + sel_start = sel_end = this->get_selection(); + if (this->tc_selection_start) { + if (this->tc_selection_start.value() < sel_end) { + sel_start = this->tc_selection_start.value(); + } else { + sel_end = this->tc_selection_start.value(); + } + } + + if (sel_start <= row && row <= sel_end) { + auto role = this->get_overlay_selection() + ? this->tc_disabled_cursor_role.value() + : this->tc_cursor_role.value(); + + sa.emplace_back(line_range{orig_line.lr_start, -1}, + VC_ROLE.value(role)); + } } for (auto& tc_highlight : this->tc_highlights) { diff --git a/src/textview_curses.hh b/src/textview_curses.hh index 70ae34af..bc1ef6a4 100644 --- a/src/textview_curses.hh +++ b/src/textview_curses.hh @@ -551,8 +551,6 @@ class text_delegate { public: virtual ~text_delegate() = default; - virtual void text_overlay(textview_curses& tc) {} - virtual bool text_handle_mouse(textview_curses& tc, mouse_event& me) { return false; @@ -726,14 +724,6 @@ public: void reload_data(); - void do_update() - { - this->listview_curses::do_update(); - if (this->tc_delegate != nullptr) { - this->tc_delegate->text_overlay(*this); - } - } - bool toggle_hide_fields() { bool retval = this->tc_hide_fields; @@ -857,9 +847,8 @@ protected: highlight_map_t tc_highlights; std::set tc_disabled_highlights; - vis_line_t tc_selection_start{-1_vl}; - vis_line_t tc_selection_last{-1_vl}; - bool tc_selection_cleared{false}; + nonstd::optional tc_selection_start; + mouse_event tc_press_event; bool tc_hide_fields{true}; bool tc_paused{false}; diff --git a/src/view_curses.cc b/src/view_curses.cc index 9dc671f5..d7bbde13 100644 --- a/src/view_curses.cc +++ b/src/view_curses.cc @@ -129,6 +129,15 @@ struct utf_to_display_adjustment { } }; +bool +view_curses::contains(int x, int y) const +{ + if (this->vc_x <= x && x < this->vc_x + this->vc_width && this->vc_y == y) { + return true; + } + return false; +} + void view_curses::awaiting_user_input() { diff --git a/src/view_curses.hh b/src/view_curses.hh index 4981bf20..cba68abb 100644 --- a/src/view_curses.hh +++ b/src/view_curses.hh @@ -320,14 +320,28 @@ struct mouse_event { mouse_event(mouse_button_t button = mouse_button_t::BUTTON_LEFT, mouse_button_state_t state = mouse_button_state_t::BUTTON_STATE_PRESSED, + uint8_t mods = 0, int x = -1, int y = -1) - : me_button(button), me_state(state), me_x(x), me_y(y) + : me_button(button), me_state(state), me_modifiers(mods), me_x(x), + me_y(y) { } + enum class modifier_t : uint8_t { + shift = 4, + meta = 8, + ctrl = 16, + }; + + bool is_modifier_pressed(modifier_t mod) const + { + return this->me_modifiers & lnav::enums::to_underlying(mod); + } + mouse_button_t me_button; mouse_button_state_t me_state; + uint8_t me_modifiers; struct timeval me_time {}; int me_x; int me_y; @@ -343,21 +357,26 @@ public: /** * Update the curses display. */ - virtual void do_update() + virtual bool do_update() { + bool retval = false; + this->vc_needs_update = false; if (!this->vc_visible) { - return; + return retval; } for (auto* child : this->vc_children) { - child->do_update(); + retval = child->do_update() || retval; } + return retval; } virtual bool handle_mouse(mouse_event& me) { return false; } + virtual bool contains(int x, int y) const; + void set_needs_update() { this->vc_needs_update = true; @@ -381,6 +400,33 @@ public: bool is_visible() const { return this->vc_visible; } + /** + * Set the Y position of this view on the display. A value greater than + * zero is considered to be an absolute size. A value less than zero makes + * the position relative to the bottom of the enclosing window. + * + * @param y The Y position of the cursor on the curses display. + */ + void set_y(int y) + { + if (y != this->vc_y) { + this->vc_y = y; + this->set_needs_update(); + } + } + + int get_y() const { return this->vc_y; } + + void set_x(unsigned int x) + { + if (x != this->vc_x) { + this->vc_x = x; + this->set_needs_update(); + } + } + + unsigned int get_x() const { return this->vc_x; } + void set_width(long width) { this->vc_width = width; } long get_width() const { return this->vc_width; } @@ -403,6 +449,8 @@ protected: bool vc_visible{true}; /** Flag to indicate if a display update is needed. */ bool vc_needs_update{true}; + unsigned int vc_x{0}; + int vc_y{0}; long vc_width{0}; std::vector vc_children; role_t vc_default_role{role_t::VCR_TEXT}; @@ -421,22 +469,24 @@ public: return this->vs_views.back(); } - void do_update() override + bool do_update() override { if (!this->vc_visible) { - return; + return false; } - this->top() | [this](T* vc) { + bool retval; + this->top() | [this, &retval](T* vc) { if (this->vc_needs_update) { vc->set_needs_update(); } - vc->do_update(); + retval = vc->do_update(); }; - view_curses::do_update(); + retval = view_curses::do_update() || retval; this->vc_needs_update = false; + return retval; } void push_back(T* view) diff --git a/src/view_helpers.cc b/src/view_helpers.cc index abaad61a..34c15a44 100644 --- a/src/view_helpers.cc +++ b/src/view_helpers.cc @@ -718,7 +718,7 @@ layout_views() lnav_data.ld_user_message_view.set_visible(vis); bottom -= 1; - lnav_data.ld_status[LNS_BOTTOM].set_top(bottom); + lnav_data.ld_status[LNS_BOTTOM].set_y(bottom); lnav_data.ld_status[LNS_BOTTOM].set_enabled(!filters_open && !breadcrumb_open); @@ -727,7 +727,7 @@ layout_views() lnav_data.ld_preview_view[1].set_y(bottom + 1); lnav_data.ld_preview_view[1].set_visible(vis); - lnav_data.ld_status[LNS_PREVIEW1].set_top(bottom); + lnav_data.ld_status[LNS_PREVIEW1].set_y(bottom); lnav_data.ld_status[LNS_PREVIEW1].set_visible(vis); vis = preview_open0 && bottom.try_consume(preview_height0 + 1); @@ -735,7 +735,7 @@ layout_views() lnav_data.ld_preview_view[0].set_y(bottom + 1); lnav_data.ld_preview_view[0].set_visible(vis); - lnav_data.ld_status[LNS_PREVIEW0].set_top(bottom); + lnav_data.ld_status[LNS_PREVIEW0].set_y(bottom); lnav_data.ld_status[LNS_PREVIEW0].set_visible(vis); if (doc_side_by_side && doc_height > 0) { @@ -770,7 +770,7 @@ layout_views() auto has_doc = lnav_data.ld_example_view.get_height() > 0_vl || lnav_data.ld_doc_view.get_height() > 0_vl; - lnav_data.ld_status[LNS_DOC].set_top(bottom); + lnav_data.ld_status[LNS_DOC].set_y(bottom); lnav_data.ld_status[LNS_DOC].set_visible(has_doc && vis); if (is_gantt) { @@ -783,7 +783,7 @@ layout_views() lnav_data.ld_gantt_details_view.set_width(width); lnav_data.ld_gantt_details_view.set_visible(vis); - lnav_data.ld_status[LNS_GANTT].set_top(bottom); + lnav_data.ld_status[LNS_GANTT].set_y(bottom); lnav_data.ld_status[LNS_GANTT].set_visible(vis); vis = bottom.try_consume(filter_height + (filters_open ? 1 : 0) @@ -799,11 +799,11 @@ layout_views() lnav_data.ld_files_view.set_visible(filters_open && vis); lnav_data.ld_status[LNS_FILTER_HELP].set_visible(filters_open && vis); - lnav_data.ld_status[LNS_FILTER_HELP].set_top(bottom + 1); + lnav_data.ld_status[LNS_FILTER_HELP].set_y(bottom + 1); lnav_data.ld_status[LNS_FILTER].set_visible(vis); lnav_data.ld_status[LNS_FILTER].set_enabled(filters_open); - lnav_data.ld_status[LNS_FILTER].set_top(bottom); + lnav_data.ld_status[LNS_FILTER].set_y(bottom); vis = is_spectro && bottom.try_consume(5 + 1); lnav_data.ld_spectro_details_view.set_y(bottom + 1); @@ -811,7 +811,7 @@ layout_views() lnav_data.ld_spectro_details_view.set_width(width); lnav_data.ld_spectro_details_view.set_visible(vis); - lnav_data.ld_status[LNS_SPECTRO].set_top(bottom); + lnav_data.ld_status[LNS_SPECTRO].set_y(bottom); lnav_data.ld_status[LNS_SPECTRO].set_visible(vis); lnav_data.ld_status[LNS_SPECTRO].set_enabled(lnav_data.ld_mode == ln_mode_t::SPECTRO_DETAILS); @@ -1401,3 +1401,73 @@ clear_preview() lnav_data.ld_preview_view[lpc].set_overlay_source(nullptr); } } + +void +lnav_behavior::mouse_event(int button, bool release, int x, int y) +{ + struct mouse_event me; + + switch (button & xterm_mouse::XT_BUTTON__MASK) { + case xterm_mouse::XT_BUTTON1: + me.me_button = mouse_button_t::BUTTON_LEFT; + break; + case xterm_mouse::XT_BUTTON2: + me.me_button = mouse_button_t::BUTTON_MIDDLE; + break; + case xterm_mouse::XT_BUTTON3: + me.me_button = mouse_button_t::BUTTON_RIGHT; + break; + case xterm_mouse::XT_SCROLL_UP: + me.me_button = mouse_button_t::BUTTON_SCROLL_UP; + break; + case xterm_mouse::XT_SCROLL_DOWN: + me.me_button = mouse_button_t::BUTTON_SCROLL_DOWN; + break; + } + + me.me_modifiers = button & xterm_mouse::XT_MODIFIER_MASK; + + if (button & xterm_mouse::XT_DRAG_FLAG) { + me.me_state = mouse_button_state_t::BUTTON_STATE_DRAGGED; + } else if (release) { + me.me_state = mouse_button_state_t::BUTTON_STATE_RELEASED; + } else { + me.me_state = mouse_button_state_t::BUTTON_STATE_PRESSED; + } + + auto width = getmaxx(lnav_data.ld_window); + gettimeofday(&me.me_time, nullptr); + + me.me_x = x - 1; + if (me.me_x >= width) { + me.me_x = width - 1; + } + me.me_y = y - 1; + + switch (me.me_state) { + case mouse_button_state_t::BUTTON_STATE_PRESSED: { + auto* tc = *(lnav_data.ld_view_stack.top()); + if (tc->contains(me.me_x, me.me_y)) { + this->lb_last_view = tc; + } + break; + } + case mouse_button_state_t::BUTTON_STATE_DRAGGED: + case mouse_button_state_t::BUTTON_STATE_RELEASED: { + break; + } + } + + if (this->lb_last_view != nullptr) { + me.me_y -= this->lb_last_view->get_y(); + me.me_x -= this->lb_last_view->get_x(); + this->lb_last_view->handle_mouse(me); + } + this->lb_last_event = me; + if (me.me_state == mouse_button_state_t::BUTTON_STATE_RELEASED + || me.me_button == mouse_button_t::BUTTON_SCROLL_UP + || me.me_button == mouse_button_t::BUTTON_SCROLL_DOWN) + { + this->lb_last_view = nullptr; + } +} diff --git a/src/view_helpers.hh b/src/view_helpers.hh index 2771edfe..92c7e54a 100644 --- a/src/view_helpers.hh +++ b/src/view_helpers.hh @@ -34,8 +34,10 @@ #include "bookmarks.hh" #include "help_text.hh" +#include "listview_curses.hh" #include "logfile_fwd.hh" #include "vis_line.hh" +#include "xterm_mouse.hh" class textview_curses; class hist_source2; @@ -100,4 +102,12 @@ bool moveto_cluster(nonstd::optional ( vis_line_t search_forward_from(textview_curses* tc); textview_curses* get_textview_for_mode(ln_mode_t mode); +class lnav_behavior : public mouse_behavior { +public: + void mouse_event(int button, bool release, int x, int y) override; + + view_curses* lb_last_view{nullptr}; + struct mouse_event lb_last_event; +}; + #endif diff --git a/src/vt52_curses.cc b/src/vt52_curses.cc index 01dc6ffa..e75ddd60 100644 --- a/src/vt52_curses.cc +++ b/src/vt52_curses.cc @@ -17,7 +17,7 @@ * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHAN`TABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; @@ -208,8 +208,8 @@ vt52_curses::map_output(const char* output, int len) if (this->vc_expected_escape_len != -1) { if (this->vc_escape_len == this->vc_expected_escape_len) { auto& line_string = this->vc_line.get_string(); - auto x_byte_index - = utf8_char_to_byte_index(line_string, this->vc_x); + auto x_byte_index = utf8_char_to_byte_index( + line_string, this->vc_cursor_x); for (int esc_index = 0; esc_index < this->vc_escape_len; esc_index++) @@ -222,7 +222,7 @@ vt52_curses::map_output(const char* output, int len) } x_byte_index += 1; } - this->vc_x += 1; + this->vc_cursor_x += 1; this->vc_escape_len = 0; } } else if ((cap = vt52_escape_map::singleton()[this->vc_escape]) @@ -230,11 +230,11 @@ vt52_curses::map_output(const char* output, int len) { this->vc_escape_len = 0; if (strcmp(cap, "ce") == 0) { - this->vc_line.erase_utf8_chars(this->vc_x); + this->vc_line.erase_utf8_chars(this->vc_cursor_x); } else if (strcmp(cap, "kl") == 0) { - this->vc_x -= 1; + this->vc_cursor_x -= 1; } else if (strcmp(cap, "kr") == 0) { - this->vc_x += 1; + this->vc_cursor_x += 1; } else if (strcmp(cap, "BE") == 0 || strcmp(cap, "BD") == 0) { // TODO pass bracketed paste mode through } else { @@ -256,7 +256,7 @@ vt52_curses::map_output(const char* output, int len) switch (next_ch) { case STX: - this->vc_x = 0; + this->vc_cursor_x = 0; this->vc_line.clear(); break; @@ -265,7 +265,7 @@ vt52_curses::map_output(const char* output, int len) break; case BACKSPACE: - this->vc_x -= 1; + this->vc_cursor_x -= 1; break; case ESCAPE: @@ -275,25 +275,25 @@ vt52_curses::map_output(const char* output, int len) break; case '\n': - this->vc_x = 0; + this->vc_cursor_x = 0; this->vc_line.clear(); break; case '\r': - this->vc_x = 0; + this->vc_cursor_x = 0; break; default: { auto& line_string = this->vc_line.get_string(); - auto x_byte_index - = utf8_char_to_byte_index(line_string, this->vc_x); + auto x_byte_index = utf8_char_to_byte_index( + line_string, this->vc_cursor_x); if (x_byte_index < this->vc_line.length()) { line_string[x_byte_index] = next_ch; } else { this->vc_line.append(1, next_ch); } - this->vc_x += 1; + this->vc_cursor_x += 1; break; } } @@ -301,14 +301,16 @@ vt52_curses::map_output(const char* output, int len) } } -void +bool vt52_curses::do_update() { auto actual_width = this->get_actual_width(); view_curses::mvwattrline(this->vc_window, this->get_actual_y(), - this->vc_left, + this->vc_x, this->vc_line, line_range{0, (int) actual_width}); - wmove(this->vc_window, this->get_actual_y(), this->vc_left + this->vc_x); + wmove( + this->vc_window, this->get_actual_y(), this->vc_x + this->vc_cursor_x); + return true; } diff --git a/src/vt52_curses.hh b/src/vt52_curses.hh index c78b0e40..0249e69d 100644 --- a/src/vt52_curses.hh +++ b/src/vt52_curses.hh @@ -59,27 +59,11 @@ public: /** @return The curses window this view is attached to. */ WINDOW* get_window() { return this->vc_window; } - void set_left(int left) { this->vc_left = left; } - - int get_left() const { return this->vc_left; } - - /** - * Set the Y position of this view on the display. A value greater than - * zero is considered to be an absolute size. A value less than zero makes - * the position relative to the bottom of the enclosing window. - * - * @param y The Y position of the cursor on the curses display. - */ - void set_y(int y) { this->vc_y = y; } - - /** @return The abs/rel Y position of the cursor on the curses display. */ - int get_y() const { return this->vc_y; } - /** @param x The X position of the cursor on the curses display. */ - void set_x(int x) { this->vc_x = x; } + void set_cursor_x(int x) { this->vc_cursor_x = x; } /** @return The X position of the cursor on the curses display. */ - int get_x() const { return this->vc_x; } + int get_cursor_x() const { return this->vc_cursor_x; } /** * @return The height of this view, which consists of a single line for @@ -112,7 +96,7 @@ public: /** * Paints any past lines and moves the cursor to the current X position. */ - void do_update(); + bool do_update() override; const static char ESCAPE = 27; /*< VT52 Escape key value. */ const static char BACKSPACE = 8; /*< VT52 Backspace key value. */ @@ -141,20 +125,18 @@ protected: auto retval = getmaxx(this->vc_window); if (this->vc_width < 0) { - retval -= this->vc_left; + retval -= this->vc_x; retval += this->vc_width; } else if (this->vc_width > 0) { retval = this->vc_width; } else { - retval = retval - this->vc_left; + retval = retval - this->vc_x; } return retval; } WINDOW* vc_window{nullptr}; /*< The window that contains this view. */ - int vc_left{0}; - int vc_x{0}; /*< The X position of the cursor. */ - int vc_y{0}; /*< The Y position of the cursor. */ + int vc_cursor_x{0}; /*< The X position of the cursor. */ int vc_max_height{0}; char vc_escape[16]; /*< Storage for escape sequences. */ int vc_escape_len{0}; /*< The number of chars in vc_escape. */ diff --git a/src/xterm_mouse.hh b/src/xterm_mouse.hh index ce29a145..151444fb 100644 --- a/src/xterm_mouse.hh +++ b/src/xterm_mouse.hh @@ -84,6 +84,12 @@ public: static const int XT_BUTTON__MASK = XT_SCROLL_WHEEL_FLAG | XT_BUTTON1 | XT_BUTTON2 | XT_BUTTON3; + static const int XT_MODIFIER_SHIFT = 4; + static const int XT_MODIFIER_META = 8; + static const int XT_MODIFIER_CTRL = 16; + static const int XT_MODIFIER_MASK + = XT_MODIFIER_SHIFT | XT_MODIFIER_META | XT_MODIFIER_CTRL; + static const char* XT_TERMCAP; static const char* XT_TERMCAP_TRACKING; static const char* XT_TERMCAP_SGR; diff --git a/test/drive_view_colors.cc b/test/drive_view_colors.cc index f7b38c10..e8061922 100644 --- a/test/drive_view_colors.cc +++ b/test/drive_view_colors.cc @@ -38,7 +38,7 @@ class test_colors : public view_curses { public: test_colors() : tc_window(nullptr) {} - void do_update() override + bool do_update() override { auto& vc = view_colors::singleton(); int lpc; @@ -68,7 +68,9 @@ public: al.with_attr( {line_range{8, 11}, VC_STYLE.value(text_attrs{A_REVERSE})}); test_colors::mvwattrline(this->tc_window, lpc, 0, al, lr); - }; + + return true; + } WINDOW* tc_window; };