From 5e8b39f02a58aebcadb77854149a29bba03f6cf7 Mon Sep 17 00:00:00 2001 From: Paul Cornett Date: Mon, 12 Oct 2020 10:01:54 -0700 Subject: [PATCH] Fix WriteText() not scrolling to bottom with GTK >= 3.14, part 2 Detecting that the scrollbar position was at the bottom did not work while a scrollbar animation was in progress. Work around this by updating the position whenever the scrollbar parameters are changed during the incremental layout. Setting the position directly will prevent any scrollbar animation from starting. See #18864 --- src/gtk/textctrl.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index 977954ca35..2fe014a3c8 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -1105,8 +1105,34 @@ bool wxTextCtrl::IsEmpty() const return wxTextEntry::IsEmpty(); } +extern "C" { +static void adjustmentChanged(GtkAdjustment* adj, GtkTextMark** mark) +{ + if (*mark) + { + const double value = gtk_adjustment_get_value(adj); + const double upper = gtk_adjustment_get_upper(adj); + const double page_size = gtk_adjustment_get_page_size(adj); + if (value < upper - page_size) + { + GtkTextIter iter; + GtkTextBuffer* buffer = gtk_text_mark_get_buffer(*mark); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, *mark); + if (gtk_text_iter_is_end(&iter)) + { + // Keep position at bottom as scrollbar is updated during layout + gtk_adjustment_set_value(adj, upper - page_size); + } + } + } +} +} + void wxTextCtrl::GTKAfterLayout() { + g_signal_handlers_disconnect_by_func( + gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget)), + (void*)adjustmentChanged, &m_showPositionDefer); m_afterLayoutId = 0; if (m_showPositionDefer && !IsFrozen()) { @@ -1118,8 +1144,12 @@ void wxTextCtrl::GTKAfterLayout() extern "C" { static gboolean afterLayout(void* data) { + gdk_threads_enter(); + wxTextCtrl* win = static_cast(data); win->GTKAfterLayout(); + + gdk_threads_leave(); return false; } } @@ -1193,10 +1223,11 @@ void wxTextCtrl::WriteText( const wxString &text ) gtk_text_buffer_insert( m_buffer, &iter, buffer, buffer.length() ); + GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget)); + // Scroll to cursor, if it is at the end and scrollbar thumb is at the bottom if (insertIsEnd) { - GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget)); const double value = gtk_adjustment_get_value(adj); const double upper = gtk_adjustment_get_upper(adj); const double page_size = gtk_adjustment_get_page_size(adj); @@ -1212,6 +1243,7 @@ void wxTextCtrl::WriteText( const wxString &text ) } if (m_afterLayoutId == 0) { + g_signal_connect(adj, "changed", G_CALLBACK(adjustmentChanged), &m_showPositionDefer); m_afterLayoutId = g_idle_add_full(GTK_TEXT_VIEW_PRIORITY_VALIDATE + 1, afterLayout, this, NULL); } @@ -1397,7 +1429,11 @@ void wxTextCtrl::SetInsertionPoint( long pos ) // defer until Thaw, text view is not using m_buffer now m_showPositionDefer = mark; else + { gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text), mark); + if (m_afterLayoutId) + m_showPositionDefer = mark; + } } else // single line { @@ -1514,7 +1550,11 @@ void wxTextCtrl::ShowPosition( long pos ) // defer until Thaw, text view is not using m_buffer now m_showPositionDefer = mark; else + { gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text), mark); + if (m_afterLayoutId) + m_showPositionDefer = mark; + } } else // single line { // This function not only shows character at required position