Problem with gtk_text_view_scroll_to_mark and gtk_text_view_scroll_to_iter

Hi and greeting to all people here (it is my first Thread)

I want to scroll in a GtkTextView. But neither gtk_text_view_scroll_to_mark nor gtk_text_view_scroll_to_iter are working. It happens nothing.

Here I saw something, that I tried with success:

There is the scrollfunction called in main before gtk_main();
This works.

But this is not, what I need. I want at every time on every position in the program
be able, to scroll in the Textview.

Is this a bug, that both mentioned functions are not working as expected ?
I cannot find any hint in the documentation.

If it is important: I work on LinuxMint Cinnamon 19.3 with gcc and gtk3.

I use gtk_text_view_scroll_to_mark in a GTK3 application. Everything works fine as expected. The program was tested with LM Cinnamon 18 and LMDE 5. It seems that rather something is wrong with your code. The additional scrolling parameters could cause strange effects, try scrolling without them (use_align is set to FALSE). And check whether the mark is at the right location.

I wrote an short example, that consists only of the important things:

/*
    I want, that the TextView is scrolled to its end.



    "scroll_to_end_markmethode" works only, if i call it in main BEFORE gtk_main. But then, when I try
    to call it via button, it fails and the text is not scrolled.

    "scroll_to_end_itermethode" doesn't work at all.

    I tried, to delete the Content of the Textbuffer, before I fill it with new data. But this shows no effect.
    So I removed that code from this example-code here.


*/




#include <stdbool.h>
#include <gtk/gtk.h>


#pragma GCC diagnostic ignored "-Wdeprecated-declarations"


GtkWidget *gText_view;
GtkTextBuffer *gText_buffer;

char gTestbuffer[1000];


void scroll_to_end_markmethode(GtkWidget *text_view,GtkTextBuffer *text_buffer);
void scroll_to_end_itermethode(GtkWidget *text_view,GtkTextBuffer *text_buffer);
bool callback_buttonMarkTest(void);




// This function doesn't work at all !
void scroll_to_end_itermethode(GtkWidget *text_view,GtkTextBuffer *text_buffer)
{
    GtkTextIter enditer;

    gtk_text_buffer_get_end_iter(text_buffer,&enditer);

    gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(text_view),&enditer,0.0,TRUE,0.0,0.0);
}



// This function doesn't work if it is called via button and after gtk_main in main is called !
void scroll_to_end_markmethode(GtkWidget *text_view,GtkTextBuffer *text_buffer)
{
    GtkTextIter iterend;
    GtkTextMark *mark;

    gtk_text_buffer_get_end_iter(text_buffer,&iterend);


    mark = gtk_text_buffer_create_mark(text_buffer,NULL,&iterend,TRUE);
    gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text_view),mark,0.0,TRUE,0.0,0.0);
    gtk_text_buffer_delete_mark(text_buffer,mark);
}



bool callback_buttonMarkTest(void)
{
    int i;


    gTestbuffer[0] = 0;

    for(i=0;i!=30;i++)
    {
        strcat(gTestbuffer,"Mark-Test per Button\n");
    }

    gText_buffer=gtk_text_view_get_buffer((GtkTextView*)gText_view);

    gtk_text_buffer_set_text(gText_buffer,gTestbuffer, -1); // buffer

    scroll_to_end_markmethode(gText_view,gText_buffer);

    return true;
}




bool callback_buttonIterTest(void)
{
    int i;

    gTestbuffer[0] = 0;

    for(i=0;i!=30;i++)
    {
        strcat(gTestbuffer,"Iter-Test per Button\n");
    }

    gText_buffer=gtk_text_view_get_buffer((GtkTextView*)gText_view);
    gtk_text_buffer_set_text(gText_buffer,gTestbuffer, -1); // buffer

    scroll_to_end_itermethode(gText_view,gText_buffer);

    return true; // No matter whether true or false is given
}



int main ( void )
{
    GtkWidget *window;
    GtkWidget *scroll_window;
    GtkWidget *grid_window;
    GtkWidget *buttonMarkTest,*buttonIterTest;
    int i;


    gtk_init ( NULL, NULL );

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window),600,200);
    g_signal_connect(window,"delete-event",gtk_main_quit,NULL);
    g_signal_connect(window,"destroy",gtk_main_quit,NULL);

    grid_window = gtk_grid_new();
    gtk_container_add( GTK_CONTAINER ( window ), (GtkWidget*)grid_window );


    buttonMarkTest = gtk_button_new_with_label("Mark-Test");
    gtk_grid_attach((GtkGrid*)grid_window,buttonMarkTest,1,1,10,10);

    g_signal_connect(buttonMarkTest,"clicked",(GCallback)callback_buttonMarkTest,NULL);

    buttonIterTest = gtk_button_new_with_label("Iter-Test");
    gtk_grid_attach((GtkGrid*)grid_window,buttonIterTest,1,15,10,10);

    g_signal_connect(buttonIterTest,"clicked",(GCallback)callback_buttonIterTest,NULL);


    scroll_window = gtk_scrolled_window_new(NULL,NULL);
    gtk_widget_set_size_request(scroll_window, 500, 100);

    gtk_grid_attach((GtkGrid*)grid_window,scroll_window,11,1,150,150);

    gText_view = gtk_text_view_new();

    gtk_container_add(GTK_CONTAINER(scroll_window),gText_view);
    gText_buffer=gtk_text_view_get_buffer((GtkTextView*)gText_view);

    gTestbuffer[0] = 0;

    for(i=0;i!=30;i++)
    {
        strcat(gTestbuffer,"Test: This works.\n");
    }

    gtk_text_buffer_set_text(gText_buffer,gTestbuffer, -1);


    // This function doesn't work at all!
    // scroll_to_end_itermethode(gText_view,gText_buffer);



    // This function only work, when called here on this position.
    scroll_to_end_markmethode(gText_view,gText_buffer);


    gtk_widget_show_all(window);
    gtk_main();
}

Strange, but it works fine for me (both methods), I click on the button and the window of GtkTextView is scrolled to the end.

Anything wrong with the environment? But then, this must have impact on other programs too.

1 Like

This is not a callback for a GtkButton::clicked signal. The signature is:

void
gtk_button__clicked (GtkButton *button, gpointer user_data)

So of course returning true or false won’t change anything. You should really read the GTK3 documentation.

If you read the documentation, you would get this warning in gtk_text_view_scroll_to_iter():

Note that this function uses the currently-computed height of the lines in the text buffer. Line heights are computed in an idle handler; so this function may not have the desired effect if it’s called before the height computations. To avoid oddness, consider using gtk_text_view_scroll_to_mark() which saves a point to be scrolled to after line validation.

In general, I would recommend scheduling a scroll using a low priority idle handler, so that the main loop will run all the higher priority tasks that GTK schedules internally. For instance:

gboolean
scroll_to_end_iter (gpointer data)
{
  GtkTextView *text_view = data;
  GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
  GtkTextIter end_iter;

  gtk_text_buffer_get_end_iter (buffer, &end_iter);
  gtk_text_view_scroll_to_iter (text_view, &end_iter, 0, TRUE, 0, 0);

  return G_SOURCE_REMOVE;
}

static void
scroll_iter_button__clicked (GtkButton *button, gpointer user_data)
{
  // Fill the text buffer...

  g_idle_add_full (G_PRIORITY_IDLE_LOW, scroll_to_end_iter, text_view);
}

The same thing applies to the “scroll_to_mark” method: you’re filling up the text buffer and then telling it to scroll immediately, before GTK has had a chance to compute the size of the text.

1 Like

At first, thank you, your hints helped me, to solve that problem.
And thank you too, emergenz, for testing my program.

Just for technical interest: Why then “gtk_text_view_scroll_to_mark” didn’t work
on every system ? The warning because of calculations is only given for gtk_text_view_scroll_to_iter(). So I didn’t was aware enough of this problem and
thought, this does not affect gtk_text_view_scroll_to_mark.

The scroll_to_mark() method still does a bunch of validations and calls scroll_to_iter(). The main difference is that the validations happen synchronously up until scroll_to_iter() gets called. Your scroll_to_end_markmethod() works before the main loop starts because it will schedule a invalidation, and you’ll get a redraw once GTK enters the main loop.

1 Like

It is fine that the issue is solved. But what about the fact, that the test program works well on some machines? Because we both use almost identical software, the only difference is the hardware. Both computers that I used for testing are rather slow machines, while – I guess – the computer of Gerd is a fast one. This also means that my own program that runs fine on several machines would not run as expected on his machine. Does this mean that GTK is hardware dependent? In other words, does the behaviour of a GTK-based application depend on the number of CPU cores or CPU speed, etc.? I don’t believe it, but…

BTW, I found by testing a constellation where the scrolling is not done: If I click on the Iter-Button for the very first time after the test application is launched or the Mark-Button was clicked. But the next click on the Iter-button scrolls the window. The Mark-Button works always well.

Of course it does.

The scrolling depends on computing the height of the text inside the buffer, since the text view widget needs to know where to scroll to as a function of the pixel height of the text. In some cases, that height might be available because it hasn’t changed from the previous state; or it might just be a case that you’re pressing the button and the text height has already been processed by the previous frame.

That’s why it’s recommended to use a idle callback: you’re deferring the work to the next main loop iteration is a reliable way.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.