diff options
author | Christian Persch <chpe@src.gnome.org> | 2021-03-13 22:10:42 +0100 |
---|---|---|
committer | Christian Persch <chpe@src.gnome.org> | 2021-03-13 22:10:42 +0100 |
commit | 910bc02329925e6e7aec8ef2d7dbb717cdc9f38c (patch) | |
tree | 33a03560f756482763a7391f8f5a33ceb888991d | |
parent | c346a0dd6a149f93ffd63bc4b36dbaa34a02bafe (diff) | |
download | vte-910bc02329925e6e7aec8ef2d7dbb717cdc9f38c.tar.gz |
widget: Add API to make adjustment values pixels
Fixes: https://gitlab.gnome.org/GNOME/vte/-/issues/335
-rw-r--r-- | doc/reference/vte-sections.txt.in | 2 | ||||
-rw-r--r-- | src/app/app.cc | 4 | ||||
-rw-r--r-- | src/vte.cc | 251 | ||||
-rw-r--r-- | src/vte/vteterminal.h | 6 | ||||
-rw-r--r-- | src/vtegtk.cc | 68 | ||||
-rw-r--r-- | src/vtegtk.hh | 1 | ||||
-rw-r--r-- | src/vteinternal.hh | 32 | ||||
-rw-r--r-- | src/vteseq.cc | 3 | ||||
-rw-r--r-- | src/widget.cc | 198 | ||||
-rw-r--r-- | src/widget.hh | 28 |
10 files changed, 385 insertions, 208 deletions
diff --git a/doc/reference/vte-sections.txt.in b/doc/reference/vte-sections.txt.in index 53079292..7e735f5b 100644 --- a/doc/reference/vte-sections.txt.in +++ b/doc/reference/vte-sections.txt.in @@ -35,6 +35,8 @@ vte_terminal_set_scroll_on_output vte_terminal_get_scroll_on_output vte_terminal_set_scroll_on_keystroke vte_terminal_get_scroll_on_keystroke +vte_terminal_set_scroll_unit_is_pixels +vte_terminal_get_scroll_unit_is_pixels vte_terminal_set_cell_height_scale vte_terminal_get_cell_height_scale vte_terminal_set_cell_width_scale diff --git a/src/app/app.cc b/src/app/app.cc index 7f623a55..e38733dd 100644 --- a/src/app/app.cc +++ b/src/app/app.cc @@ -81,6 +81,7 @@ public: gboolean object_notifications{false}; gboolean require_systemd_scope{false}; gboolean reverse{false}; + gboolean scroll_unit_is_pixels{false}; gboolean test_mode{false}; gboolean track_clipboard_targets{false}; gboolean use_theme_colors{false}; @@ -622,6 +623,8 @@ public: "Reverse foreground/background colors", nullptr }, { "require-systemd-scope", 0, 0, G_OPTION_ARG_NONE, &require_systemd_scope, "Require use of a systemd user scope", nullptr }, + { "scroll-unit-is-pixels", 0, 0, G_OPTION_ARG_NONE, &scroll_unit_is_pixels, + "Use pixels as scroll unit", nullptr }, { "scrollback-lines", 'n', 0, G_OPTION_ARG_INT, &scrollback_lines, "Specify the number of scrollback-lines (-1 for infinite)", nullptr }, { "sixel", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &no_sixel, @@ -2528,6 +2531,7 @@ vteapp_window_constructed(GObject *object) vte_terminal_set_rewrap_on_resize(window->terminal, !options.no_rewrap); vte_terminal_set_scroll_on_output(window->terminal, false); vte_terminal_set_scroll_on_keystroke(window->terminal, true); + vte_terminal_set_scroll_unit_is_pixels(window->terminal, options.scroll_unit_is_pixels); vte_terminal_set_scrollback_lines(window->terminal, options.scrollback_lines); vte_terminal_set_text_blink_mode(window->terminal, options.text_blink_mode); @@ -998,18 +998,6 @@ Terminal::child_exited_eos_wait_callback() return false; // don't run again } -/* Emit a "char-size-changed" signal. */ -void -Terminal::emit_char_size_changed(int width, - int height) -{ - _vte_debug_print(VTE_DEBUG_SIGNALS, - "Emitting `char-size-changed'.\n"); - /* FIXME on next API break, change the signature */ - g_signal_emit(m_terminal, signals[SIGNAL_CHAR_SIZE_CHANGED], 0, - (guint)width, (guint)height); -} - /* Emit an "increase-font-size" signal. */ void Terminal::emit_increase_font_size() @@ -1941,92 +1929,19 @@ Terminal::regex_match_check_extra(vte::grid::column_t col, void Terminal::emit_adjustment_changed() { - if (m_adjustment_changed_pending) { - bool changed = false; - gdouble current, v; - - auto vadjustment = m_vadjustment.get(); - - auto const freezer = vte::glib::FreezeObjectNotify{vadjustment}; - - v = _vte_ring_delta (m_screen->row_data); - current = gtk_adjustment_get_lower(vadjustment); - if (!_vte_double_equal(current, v)) { - _vte_debug_print(VTE_DEBUG_ADJ, - "Changing lower bound from %.0f to %f\n", - current, v); - gtk_adjustment_set_lower(vadjustment, v); - changed = true; - } - - v = m_screen->insert_delta + m_row_count; - current = gtk_adjustment_get_upper(vadjustment); - if (!_vte_double_equal(current, v)) { - _vte_debug_print(VTE_DEBUG_ADJ, - "Changing upper bound from %.0f to %f\n", - current, v); - gtk_adjustment_set_upper(vadjustment, v); - changed = true; - } - - /* The step increment should always be one. */ - v = gtk_adjustment_get_step_increment(vadjustment); - if (!_vte_double_equal(v, 1)) { - _vte_debug_print(VTE_DEBUG_ADJ, - "Changing step increment from %.0lf to 1\n", v); - gtk_adjustment_set_step_increment(vadjustment, 1); - changed = true; - } + if (!widget()) + return; - /* Set the number of rows the user sees to the number of rows the - * user sees. */ - v = gtk_adjustment_get_page_size(vadjustment); - if (!_vte_double_equal(v, m_row_count)) { - _vte_debug_print(VTE_DEBUG_ADJ, - "Changing page size from %.0f to %ld\n", - v, m_row_count); - gtk_adjustment_set_page_size(vadjustment, - m_row_count); - changed = true; - } + if (m_adjustment_changed_pending) { + widget()->notify_scroll_bounds_changed(m_adjustment_value_changed_pending); - /* Clicking in the empty area should scroll one screen, so set the - * page size to the number of visible rows. */ - v = gtk_adjustment_get_page_increment(vadjustment); - if (!_vte_double_equal(v, m_row_count)) { - _vte_debug_print(VTE_DEBUG_ADJ, - "Changing page increment from " - "%.0f to %ld\n", - v, m_row_count); - gtk_adjustment_set_page_increment(vadjustment, - m_row_count); - changed = true; - } + m_adjustment_changed_pending = m_adjustment_value_changed_pending = false; + } + else if (m_adjustment_value_changed_pending) { + widget()->notify_scroll_value_changed(); - if (changed) - _vte_debug_print(VTE_DEBUG_SIGNALS, - "Emitting adjustment_changed.\n"); - m_adjustment_changed_pending = FALSE; - } - if (m_adjustment_value_changed_pending) { - double v, delta; - _vte_debug_print(VTE_DEBUG_SIGNALS, - "Emitting adjustment_value_changed.\n"); - m_adjustment_value_changed_pending = FALSE; - - auto vadjustment = m_vadjustment.get(); - v = gtk_adjustment_get_value(vadjustment); - if (!_vte_double_equal(v, m_screen->scroll_delta)) { - /* this little dance is so that the scroll_delta is - * updated immediately, but we still handled scrolling - * via the adjustment - e.g. user interaction with the - * scrollbar - */ - delta = m_screen->scroll_delta; - m_screen->scroll_delta = v; - gtk_adjustment_set_value(vadjustment, delta); - } - } + m_adjustment_value_changed_pending = false; + } } /* Queue an adjustment-changed signal to be delivered when convenient. */ @@ -2041,34 +1956,47 @@ Terminal::queue_adjustment_changed() void Terminal::queue_adjustment_value_changed(double v) { - if (!_vte_double_equal(v, m_screen->scroll_delta)) { - _vte_debug_print(VTE_DEBUG_ADJ, - "Adjustment value changed to %f\n", - v); - m_screen->scroll_delta = v; - m_adjustment_value_changed_pending = true; - add_update_timeout(this); - } + /* FIXME: do this check in pixel space? */ + if (_vte_double_equal(v, m_screen->scroll_delta)) + return; + + _vte_debug_print(VTE_DEBUG_ADJ, + "Scroll value changed to %f\n", v); + + /* Save the difference. */ + auto const dy = v - m_screen->scroll_delta; + + m_screen->scroll_delta = v; + m_adjustment_value_changed_pending = true; + add_update_timeout(this); + + if (!widget_realized()) [[unlikely]] + return; + + _vte_debug_print(VTE_DEBUG_ADJ, + "Scrolling by %f\n", dy); + + invalidate_all(); + match_contents_clear(); + emit_text_scrolled(dy); + queue_contents_changed(); } void Terminal::queue_adjustment_value_changed_clamped(double v) { - auto vadjustment = m_vadjustment.get(); - auto const lower = gtk_adjustment_get_lower(vadjustment); - auto const upper = gtk_adjustment_get_upper(vadjustment); - - v = CLAMP(v, lower, MAX (lower, upper - m_row_count)); + auto const lower = _vte_ring_delta (m_screen->row_data); + auto const upper_minus_row_count = m_screen->insert_delta; + v = std::clamp(v, + double(lower), + double(std::max(lower, upper_minus_row_count))); queue_adjustment_value_changed(v); } void Terminal::adjust_adjustments() { - g_assert(m_screen != nullptr); - g_assert(m_screen->row_data != nullptr); - queue_adjustment_changed(); /* The lower value should be the first row in the buffer. */ @@ -2090,9 +2018,6 @@ Terminal::adjust_adjustments() void Terminal::adjust_adjustments_full() { - g_assert(m_screen != NULL); - g_assert(m_screen->row_data != NULL); - adjust_adjustments(); queue_adjustment_changed(); } @@ -6737,21 +6662,19 @@ Terminal::mouse_autoscroll_timer_callback() /* Provide an immediate effect for mouse wigglers. */ if (m_mouse_last_position.y < 0) { - if (m_vadjustment) { - /* Try to scroll up by one line. */ - adj = m_screen->scroll_delta - 1; - queue_adjustment_value_changed_clamped(adj); - extend = true; - } + /* Try to scroll up by one line. */ + adj = m_screen->scroll_delta - 1; + queue_adjustment_value_changed_clamped(adj); + extend = true; + _vte_debug_print(VTE_DEBUG_EVENTS, "Autoscrolling down.\n"); } if (m_mouse_last_position.y >= m_view_usable_extents.height()) { - if (m_vadjustment) { - /* Try to scroll up by one line. */ - adj = m_screen->scroll_delta + 1; - queue_adjustment_value_changed_clamped(adj); - extend = true; - } + /* Try to scroll up by one line. */ + adj = m_screen->scroll_delta + 1; + queue_adjustment_value_changed_clamped(adj); + extend = true; + _vte_debug_print(VTE_DEBUG_EVENTS, "Autoscrolling up.\n"); } if (extend) { @@ -7256,7 +7179,9 @@ Terminal::apply_font_metrics(int cell_width_unscaled, m_cell_height_unscaled, m_cell_width_unscaled); } - emit_char_size_changed(m_cell_width, m_cell_height); + + if (widget()) + widget()->notify_char_size_changed(m_cell_width, m_cell_height); } /* Repaint. */ invalidate_all(); @@ -7705,25 +7630,13 @@ Terminal::set_size(long columns, } } -/* Redraw the widget. */ -static void -vte_terminal_vadjustment_value_changed_cb(vte::terminal::Terminal* that) noexcept -try -{ - that->vadjustment_value_changed(); -} -catch (...) -{ - vte::log_exception(); -} - void -Terminal::vadjustment_value_changed() +Terminal::set_scroll_value(double value) { - /* Read the new adjustment value and save the difference. */ - auto const adj = gtk_adjustment_get_value(m_vadjustment.get()); - double dy = adj - m_screen->scroll_delta; - m_screen->scroll_delta = adj; + /* Save the difference. */ + auto const dy = value - m_screen->scroll_delta; + + m_screen->scroll_delta = value; /* Sanity checks. */ if (G_UNLIKELY(!widget_realized())) @@ -7732,7 +7645,7 @@ Terminal::vadjustment_value_changed() /* FIXME: do this check in pixel space */ if (!_vte_double_equal(dy, 0)) { _vte_debug_print(VTE_DEBUG_ADJ, - "Scrolling by %f\n", dy); + "Scrolling by %f\n", dy); invalidate_all(); match_contents_clear(); @@ -7743,33 +7656,6 @@ Terminal::vadjustment_value_changed() } } -void -Terminal::widget_set_vadjustment(vte::glib::RefPtr<GtkAdjustment>&& adjustment) -{ - if (adjustment && adjustment == m_vadjustment) - return; - if (!adjustment && m_vadjustment) - return; - - if (m_vadjustment) { - /* Disconnect our signal handlers from this object. */ - g_signal_handlers_disconnect_by_func(m_vadjustment.get(), - (void*)vte_terminal_vadjustment_value_changed_cb, - this); - } - - if (adjustment) - m_vadjustment = std::move(adjustment); - else - m_vadjustment = vte::glib::make_ref_sink(GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0))); - - /* We care about the offset, not the top or bottom. */ - g_signal_connect_swapped(m_vadjustment.get(), - "value-changed", - G_CALLBACK(vte_terminal_vadjustment_value_changed_cb), - this); -} - Terminal::Terminal(vte::platform::Widget* w, VteTerminal *t) : m_real_widget(w), @@ -7779,8 +7665,6 @@ Terminal::Terminal(vte::platform::Widget* w, m_alternate_screen(VTE_ROWS, false), m_screen(&m_normal_screen) { - widget_set_vadjustment({}); - /* Inits allocation to 1x1 @ -1,-1 */ cairo_rectangle_int_t allocation; gtk_widget_get_allocation(m_widget, &allocation); @@ -8065,7 +7949,7 @@ Terminal::~Terminal() stop_autoscroll(); /* Cancel pending adjustment change notifications. */ - m_adjustment_changed_pending = FALSE; + m_adjustment_changed_pending = false; /* Free any selected text, but if we currently own the selection, * throw the text onto the clipboard without an owner so that it @@ -8098,14 +7982,6 @@ Terminal::~Terminal() _vte_byte_array_free(m_outgoing); m_outgoing = nullptr; - /* Free public-facing data. */ - if (m_vadjustment) { - /* Disconnect our signal handlers from this object. */ - g_signal_handlers_disconnect_by_func(m_vadjustment.get(), - (void*)vte_terminal_vadjustment_value_changed_cb, - this); - } - /* Update rects */ g_array_free(m_update_rects, TRUE /* free segment */); } @@ -9542,7 +9418,7 @@ Terminal::widget_mouse_scroll(vte::platform::ScrollEvent const& event) return true; } - v = MAX (1., ceil (gtk_adjustment_get_page_increment (m_vadjustment.get()) / 10.)); + v = MAX (1., ceil (m_row_count /* page increment */ / 10.)); _vte_debug_print(VTE_DEBUG_EVENTS, "Scroll speed is %d lines per non-smooth scroll unit\n", (int) v); @@ -10779,7 +10655,6 @@ Terminal::search_rows(pcre2_match_context_8 *match_context, long start_col, end_col; VteCharAttributes *ca; GArray *attrs; - gdouble value, page_size; auto row_text = get_text(start_row, 0, end_row, 0, @@ -10848,9 +10723,9 @@ Terminal::search_rows(pcre2_match_context_8 *match_context, g_string_free (row_text, TRUE); select_text(start_col, start_row, end_col, end_row); - /* Quite possibly the math here should not access adjustment directly... */ - value = gtk_adjustment_get_value(m_vadjustment.get()); - page_size = gtk_adjustment_get_page_size(m_vadjustment.get()); + /* Quite possibly the math here should not access the scroll values directly... */ + auto const value = m_screen->scroll_delta; + auto const page_size = m_row_count; if (backward) { if (end_row < value || end_row > value + page_size - 1) queue_adjustment_value_changed_clamped(end_row - page_size + 1); diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index 346523ad..05f6674f 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -270,6 +270,12 @@ void vte_terminal_set_enable_fallback_scrolling(VteTerminal *terminal, _VTE_PUBLIC gboolean vte_terminal_get_enable_fallback_scrolling(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +_VTE_PUBLIC +void vte_terminal_set_scroll_unit_is_pixels(VteTerminal *terminal, + gboolean enable) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +_VTE_PUBLIC +gboolean vte_terminal_get_scroll_unit_is_pixels(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + /* Set the color scheme. */ _VTE_PUBLIC void vte_terminal_set_color_bold(VteTerminal *terminal, diff --git a/src/vtegtk.cc b/src/vtegtk.cc index 47baa30b..900c9cfb 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -999,6 +999,9 @@ try case PROP_SCROLL_ON_OUTPUT: g_value_set_boolean (value, vte_terminal_get_scroll_on_output(terminal)); break; + case PROP_SCROLL_UNIT_IS_PIXELS: + g_value_set_boolean (value, vte_terminal_get_scroll_unit_is_pixels(terminal)); + break; case PROP_TEXT_BLINK_MODE: g_value_set_enum (value, vte_terminal_get_text_blink_mode (terminal)); break; @@ -1117,6 +1120,9 @@ try case PROP_SCROLL_ON_OUTPUT: vte_terminal_set_scroll_on_output (terminal, g_value_get_boolean (value)); break; + case PROP_SCROLL_UNIT_IS_PIXELS: + vte_terminal_set_scroll_unit_is_pixels(terminal, g_value_get_boolean(value)); + break; case PROP_TEXT_BLINK_MODE: vte_terminal_set_text_blink_mode (terminal, (VteTextBlinkMode)g_value_get_enum (value)); break; @@ -2238,6 +2244,20 @@ vte_terminal_class_init(VteTerminalClass *klass) GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); /** + * VteTerminal:scroll-unit-is-pixels: + * + * Controls whether the terminal's GtkAdjustment values unit is lines + * or pixels. This can be enabled when the terminal is the child of a + * GtkScrolledWindow to fix some bugs with its kinetic scrolling. + * + * Since: 0.66 + */ + pspecs[PROP_SCROLL_UNIT_IS_PIXELS] = + g_param_spec_boolean ("scroll-unit-is-pixels", nullptr, nullptr, + false, + GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + + /** * VteTerminal:text-blink-mode: * * Controls whether or not the terminal will allow blinking text. @@ -5833,6 +5853,54 @@ catch (...) } /** + * vte_terminal_set_scroll_unit_is_pixels: + * @terminal: a #VteTerminal + * @enable: whether to use pixels as scroll unit + * + * Controls whether the terminal's scroll unit is lines or pixels. + * + * This function is rarely useful, except when the terminal is added to a + * #GtkScrolledWindow. + * + * Since: 0.66 + */ +void +vte_terminal_set_scroll_unit_is_pixels(VteTerminal *terminal, + gboolean enable) noexcept +try +{ + g_return_if_fail(VTE_IS_TERMINAL(terminal)); + + if (WIDGET(terminal)->set_scroll_unit_is_pixels(enable != false)) + g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_SCROLL_UNIT_IS_PIXELS]); +} +catch (...) +{ + vte::log_exception(); +} + +/** + * vte_terminal_get_scroll_unit_is_pixels: + * @terminal: a #VteTerminal + * + * Returns: %TRUE if the scroll unit is pixels; or %FALSE if the unit is lines + * + * Since: 0.66 + */ +gboolean +vte_terminal_get_scroll_unit_is_pixels(VteTerminal *terminal) noexcept +try +{ + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), false); + return WIDGET(terminal)->scroll_unit_is_pixels(); +} +catch (...) +{ + vte::log_exception(); + return false; +} + +/** * vte_terminal_get_window_title: * @terminal: a #VteTerminal * diff --git a/src/vtegtk.hh b/src/vtegtk.hh index e56314ad..6b7a1ea2 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -88,6 +88,7 @@ enum { PROP_SCROLLBACK_LINES, PROP_SCROLL_ON_KEYSTROKE, PROP_SCROLL_ON_OUTPUT, + PROP_SCROLL_UNIT_IS_PIXELS, PROP_TEXT_BLINK_MODE, PROP_WINDOW_TITLE, PROP_WORD_CHAR_EXCEPTIONS, diff --git a/src/vteinternal.hh b/src/vteinternal.hh index f2fafe40..1489062e 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -268,6 +268,9 @@ public: vte::grid::row_t m_row_count{VTE_ROWS}; vte::grid::column_t m_column_count{VTE_COLUMNS}; + inline constexpr auto row_count() const noexcept -> long { return m_row_count; } + inline constexpr auto column_count() const noexcept -> long { return m_column_count; } + vte::terminal::Tabstops m_tabstops{}; vte::parser::Parser m_parser; /* control sequence state machine */ @@ -430,6 +433,21 @@ public: bool m_scroll_on_keystroke{true}; vte::grid::row_t m_scrollback_lines{0}; + inline auto scroll_limit_lower() const noexcept + { + return _vte_ring_delta (m_screen->row_data); + } + + inline auto scroll_limit_upper() const noexcept + { + return m_screen->insert_delta + m_row_count; + } + + inline constexpr auto scroll_position() const noexcept + { + return m_screen->scroll_delta; + } + /* Restricted scrolling */ struct vte_scrolling_region m_scrolling_region; /* the region we scroll in */ gboolean m_scrolling_restricted; @@ -657,8 +675,8 @@ public: int m_im_preedit_cursor; /* Adjustment updates pending. */ - gboolean m_adjustment_changed_pending; - gboolean m_adjustment_value_changed_pending; + bool m_adjustment_changed_pending; + bool m_adjustment_value_changed_pending; gboolean m_cursor_moved_pending; gboolean m_contents_changed_pending; @@ -713,9 +731,6 @@ public: #endif auto padding() const noexcept { return &m_padding; } - vte::glib::RefPtr<GtkAdjustment> m_vadjustment{}; - auto vadjustment() noexcept { return m_vadjustment.get(); } - /* Hyperlinks */ bool m_allow_hyperlink{false}; vte::base::Ring::hyperlink_idx_t m_hyperlink_hover_idx; @@ -878,8 +893,6 @@ public: vte::platform::ClipboardFormat format); void widget_clipboard_data_clear(vte::platform::Clipboard const& clipboard); - void widget_set_vadjustment(vte::glib::RefPtr<GtkAdjustment>&& adjustment); - void widget_realize(); void widget_unrealize(); void widget_unmap(); @@ -1100,8 +1113,6 @@ public: long old_rows, bool do_rewrap); - void vadjustment_value_changed(); - unsigned translate_ctrlkey(vte::platform::KeyEvent const& event) const noexcept; void apply_mouse_cursor(); @@ -1109,6 +1120,7 @@ public: void beep(); + void set_scroll_value(double value); void emit_adjustment_changed(); void emit_commit(std::string_view const& str); void emit_eof(); @@ -1175,8 +1187,6 @@ public: #endif /* WITH_A11Y */ void emit_pending_signals(); - void emit_char_size_changed(int width, - int height); void emit_increase_font_size(); void emit_decrease_font_size(); void emit_bell(); diff --git a/src/vteseq.cc b/src/vteseq.cc index 2a8d777d..833167c6 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -545,8 +545,7 @@ Terminal::set_mode_private(int mode, } /* Reset scrollbars and repaint everything. */ - gtk_adjustment_set_value(m_vadjustment.get(), - m_screen->scroll_delta); + queue_adjustment_value_changed(m_screen->scroll_delta); set_scrollback_lines(m_scrollback_lines); queue_contents_changed(); invalidate_all(); diff --git a/src/widget.cc b/src/widget.cc index 08c61593..29b633de 100644 --- a/src/widget.cc +++ b/src/widget.cc @@ -30,6 +30,7 @@ #include "vtegtk.hh" #include "vteptyinternal.hh" #include "debug.h" +#include "gobject-glue.hh" #if VTE_GTK == 4 #include "graphene-glue.hh" @@ -47,6 +48,8 @@ namespace vte { namespace platform { +static void vadjustment_value_changed_cb(vte::platform::Widget* that) noexcept; + static void im_commit_cb(GtkIMContext* im_context, char const* text, @@ -222,10 +225,11 @@ catch (...) #endif /* VTE_GTK == 4 */ Widget::Widget(VteTerminal* t) - : m_widget{&t->widget}, - m_hscroll_policy{GTK_SCROLL_NATURAL}, - m_vscroll_policy{GTK_SCROLL_NATURAL} + : m_widget{&t->widget} { + // Create a default adjustment + set_vadjustment({}); + #if VTE_GTK == 3 gtk_widget_set_can_focus(gtk(), true); #endif @@ -253,6 +257,12 @@ try 0, 0, NULL, NULL, this); + if (m_vadjustment) { + g_signal_handlers_disconnect_by_func(m_vadjustment.get(), + (void*)vadjustment_value_changed_cb, + this); + } + m_widget = nullptr; m_terminal->~Terminal(); @@ -877,6 +887,132 @@ Widget::mouse_event_from_gdk(GdkEvent* event) const /* throws */ y}; } +#endif /* VTE_GTK == 3 */ + +void +Widget::notify_char_size_changed(int width, + int height) +{ + + if (scroll_unit_is_pixels()) [[unlikely]] { + /* When using pixels as adjustment values, changing the + * char size means we need to adjust the scroll bounds + * and value to keep the actual scroll position constant. + */ + notify_scroll_bounds_changed(true); + } + + _vte_debug_print(VTE_DEBUG_SIGNALS, + "Emitting `char-size-changed'.\n"); + /* FIXME on next API break, change the signature */ + g_signal_emit(gtk(), signals[SIGNAL_CHAR_SIZE_CHANGED], 0, + guint(width), guint(height)); +} + +void +Widget::notify_scroll_bounds_changed(bool value_changed) +{ + _vte_debug_print(VTE_DEBUG_ADJ, + "Updating scroll adjustment\n"); + + auto const freezer = vte::glib::FreezeObjectNotify{m_vadjustment.get()}; + auto changed = false; + + auto dlower = double(terminal()->scroll_limit_lower()); + auto dupper = double(terminal()->scroll_limit_upper()); + auto dline = 1.; + auto row_count = terminal()->row_count(); + if (scroll_unit_is_pixels()) [[unlikely]] { + auto const factor = m_terminal->get_cell_height(); + dlower *= factor; + dupper *= factor; + dline *= factor; + row_count *= factor; + } + + auto current = gtk_adjustment_get_lower(m_vadjustment.get()); + if (!_vte_double_equal(current, dlower)) { + _vte_debug_print(VTE_DEBUG_ADJ, + "Changing lower bound from %.0f to %f\n", + current, dlower); + gtk_adjustment_set_lower(m_vadjustment.get(), dlower); + changed = true; + } + + current = gtk_adjustment_get_upper(m_vadjustment.get()); + if (!_vte_double_equal(current, dupper)) { + _vte_debug_print(VTE_DEBUG_ADJ, + "Changing upper bound from %.0f to %f\n", + current, dupper); + gtk_adjustment_set_upper(m_vadjustment.get(), dupper); + changed = true; + } + + /* The step increment should always be one. */ + current = gtk_adjustment_get_step_increment(m_vadjustment.get()); + if (!_vte_double_equal(current, dline)) { + _vte_debug_print(VTE_DEBUG_ADJ, + "Changing step increment from %.0lf to 1.0\n", + current); + gtk_adjustment_set_step_increment(m_vadjustment.get(), dline); + changed = true; + } + + current = gtk_adjustment_get_page_size(m_vadjustment.get()); + if (!_vte_double_equal(current, row_count)) { + _vte_debug_print(VTE_DEBUG_ADJ, + "Changing page size from %.0f to %ld\n", + current, row_count); + gtk_adjustment_set_page_size(m_vadjustment.get(), row_count); + changed = true; + } + + /* Clicking in the empty area should scroll exactly one screen, + * so set the page size to the number of visible rows. + */ + current = gtk_adjustment_get_page_increment(m_vadjustment.get()); + if (!_vte_double_equal(current, row_count)) { + _vte_debug_print(VTE_DEBUG_ADJ, + "Changing page increment from " + "%.0f to %ld\n", + current, row_count); + gtk_adjustment_set_page_increment(m_vadjustment.get(), row_count); + changed = true; + } + + if (value_changed) + notify_scroll_value_changed(); + + if (changed) + _vte_debug_print(VTE_DEBUG_SIGNALS, + "Adjustment changed.\n"); +} + +void +Widget::notify_scroll_value_changed() +{ + _vte_debug_print(VTE_DEBUG_ADJ, + "Updating scroll adjustment value\n"); + + m_changing_scroll_position = true; + + auto value = terminal()->scroll_position(); + if (scroll_unit_is_pixels()) [[unlikely]] { + auto const factor = m_terminal->get_cell_height(); + value *= factor; + } + + auto const v = gtk_adjustment_get_value(m_vadjustment.get()); + if (!_vte_double_equal(v, value)) { + /* Note that this will generate a 'value-changed' signal */ + gtk_adjustment_set_value(m_vadjustment.get(), value); + } + + m_changing_scroll_position = false; +} + +#if VTE_GTK == 3 + ScrollEvent Widget::scroll_event_from_gdk(GdkEvent* event) const /* throws */ { @@ -1194,6 +1330,44 @@ Widget::set_pty(VtePty* pty_obj) noexcept return true; } + +static void +vadjustment_value_changed_cb(vte::platform::Widget* that) noexcept +try +{ + that->vadjustment_value_changed(); +} +catch (...) +{ + vte::log_exception(); +} + +void +Widget::set_vadjustment(vte::glib::RefPtr<GtkAdjustment> adjustment) +{ + if (adjustment && adjustment == m_vadjustment) + return; + if (!adjustment && m_vadjustment) + return; + + if (m_vadjustment) { + g_signal_handlers_disconnect_by_func(m_vadjustment.get(), + (void*)vadjustment_value_changed_cb, + this); + } + + if (adjustment) + m_vadjustment = std::move(adjustment); + else + m_vadjustment = vte::glib::make_ref_sink(GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0))); + + /* We care about the offset only, not the top or bottom. */ + g_signal_connect_swapped(m_vadjustment.get(), + "value-changed", + G_CALLBACK(vadjustment_value_changed_cb), + this); +} + bool Widget::set_word_char_exceptions(std::optional<std::string_view> stropt) { @@ -1405,6 +1579,24 @@ Widget::unroot() #endif /* VTE_GTK == 4 */ +void +Widget::vadjustment_value_changed() +{ + if (!m_terminal) + return; + + if (m_changing_scroll_position) + return; + + auto adj = gtk_adjustment_get_value(m_vadjustment.get()); + if (scroll_unit_is_pixels()) [[unlikely]] { + auto const factor = m_terminal->get_cell_height(); + adj /= factor; + } + + m_terminal->set_scroll_value(adj); +} + } // namespace platform } // namespace vte diff --git a/src/widget.hh b/src/widget.hh index a44ae303..277ca1ea 100644 --- a/src/widget.hh +++ b/src/widget.hh @@ -356,13 +356,23 @@ public: void beep() noexcept; void set_hadjustment(vte::glib::RefPtr<GtkAdjustment> adjustment) noexcept { m_hadjustment = std::move(adjustment); } - void set_vadjustment(vte::glib::RefPtr<GtkAdjustment> adjustment) { terminal()->widget_set_vadjustment(std::move(adjustment)); } + void set_vadjustment(vte::glib::RefPtr<GtkAdjustment> adjustment); auto hadjustment() noexcept { return m_hadjustment.get(); } - auto vadjustment() noexcept { return terminal()->vadjustment(); } + auto vadjustment() noexcept { return m_vadjustment.get(); } void set_hscroll_policy(GtkScrollablePolicy policy); void set_vscroll_policy(GtkScrollablePolicy policy); auto hscroll_policy() const noexcept { return m_hscroll_policy; } auto vscroll_policy() const noexcept { return m_vscroll_policy; } + + constexpr bool set_scroll_unit_is_pixels(bool enable) noexcept + { + auto const rv = m_scroll_unit_is_pixels != enable; + m_scroll_unit_is_pixels = enable; + return rv; + } + + constexpr auto scroll_unit_is_pixels() const noexcept { return m_scroll_unit_is_pixels; } + auto padding() const noexcept { return terminal()->padding(); } bool set_cursor_blink_mode(VteCursorBlinkMode mode) { return terminal()->set_cursor_blink_mode(vte::terminal::Terminal::CursorBlinkMode(mode)); } @@ -477,8 +487,14 @@ protected: unsigned key_event_translate_ctrlkey(KeyEvent const& event) const noexcept; + void notify_scroll_bounds_changed(bool value_changed = false); + void notify_scroll_value_changed(); + void notify_char_size_changed(int width, + int height); + public: // FIXMEchpe void im_preedit_changed() noexcept; + void vadjustment_value_changed(); private: KeyEvent key_event_from_gdk(GdkEvent* event) const; @@ -526,9 +542,13 @@ private: /* Misc */ std::optional<std::string> m_word_char_exceptions{}; + vte::glib::RefPtr<GtkAdjustment> m_vadjustment{}; vte::glib::RefPtr<GtkAdjustment> m_hadjustment{}; - uint32_t m_hscroll_policy : 1; - uint32_t m_vscroll_policy : 1; + + unsigned m_hscroll_policy:1{GTK_SCROLL_NATURAL}; + unsigned m_vscroll_policy:1{GTK_SCROLL_NATURAL}; + unsigned m_scroll_unit_is_pixels:1{false}; + unsigned m_changing_scroll_position:1{false}; }; } // namespace platform |