summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Persch <chpe@src.gnome.org>2021-03-13 22:10:42 +0100
committerChristian Persch <chpe@src.gnome.org>2021-03-13 22:10:42 +0100
commit910bc02329925e6e7aec8ef2d7dbb717cdc9f38c (patch)
tree33a03560f756482763a7391f8f5a33ceb888991d
parentc346a0dd6a149f93ffd63bc4b36dbaa34a02bafe (diff)
downloadvte-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.in2
-rw-r--r--src/app/app.cc4
-rw-r--r--src/vte.cc251
-rw-r--r--src/vte/vteterminal.h6
-rw-r--r--src/vtegtk.cc68
-rw-r--r--src/vtegtk.hh1
-rw-r--r--src/vteinternal.hh32
-rw-r--r--src/vteseq.cc3
-rw-r--r--src/widget.cc198
-rw-r--r--src/widget.hh28
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);
diff --git a/src/vte.cc b/src/vte.cc
index 69dfe113..fd933e8b 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -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