首页 > 解决方案 > 在 gtkmm 中滚动到 TextView 的底部

问题描述

布局如下:Gtk::ScrollWindow里面有一个 和Gtk::TextView,后者是一个名为 的派生类TextArea

作为测试,有一个按钮可以一次将一些 texgt 添加到 TextView 一行并尝试立即滚动到底部。

编码:

void MainWindow::onButtonPress()
{
    Glib::RefPtr<Gtk::TextBuffer::Tag> boldTag = Gtk::TextBuffer::Tag::create();
    boldTag->property_weight()=800;

    textarea->get_buffer()->get_tag_table()->add(boldTag);

    textarea->get_buffer()->insert_with_tag(textarea->get_buffer()->end(), "\ntest text", boldTag);

    auto iter = textarea->get_buffer()->end();
    iter.set_line_offset(0);
    textarea->scroll_to(iter);
}

有趣的是,滚动确实发生了,但不是到最后一行,而是到倒数第二行。添加偏移量的愚蠢尝试-1会产生即时错误,因为该值必须为非负数。

标签: c++gtkgtk3gtkmmgtkmm3

解决方案


我相信您的问题是您使用迭代器在插入文本的缓冲区中移动。正如文档所示

迭代器不是无限期有效的;每当以影响缓冲区中字符数的方式修改缓冲区时,所有未完成的迭代器都将变为无效。

我建议不要使用迭代器,而是使用Gtk::TextBuffer::Mark指向缓冲区末尾的 a 。与迭代器不同,标记表示:

缓冲区中的一个位置,在缓冲区修改中保留。

Gtk::TextView小部件还具有scroll_to处理标记的方法的重载,其中之一是:

void Gtk::TextView::scroll_to(const Glib::RefPtr< TextBuffer::Mark >& mark,
                              double within_margin = 0 
                             )  

您可以在以下简化示例中看到它的实际效果:

#include <iostream>
#include <string>
#include <gtkmm.h>

// Move mark to the right of newly added text (see below):
constexpr bool RIGHT_GRAVITY = false;

class MyWindow : public Gtk::ApplicationWindow
{

public:

    MyWindow()
    {
        m_button.signal_clicked().connect([this](){OnButtonPressed();});

        // Create a mark that "points" to the end of the buffer. This
        // mark will be updated accordinly as the buffer is modified:
        m_endMark = m_textArea.get_buffer()->create_mark(m_textArea.get_buffer()->end(), RIGHT_GRAVITY);

        m_scrolledWindow.add(m_textArea);

        m_layout.attach(m_scrolledWindow, 0, 0, 1, 1);
        m_layout.attach(m_button, 0, 1, 1, 1);

        add(m_layout);
    }

    void OnButtonPressed()
    {
        // Insert new line at the end of the Gtk::TextView:
        static int lineNumber = 0;
        m_textArea.get_buffer()->insert(m_textArea.get_buffer()->end(), "\n" + std::to_string(lineNumber) +" - test text");
        ++lineNumber;

        // Scroll down to the mark:
        m_textArea.scroll_to(m_endMark);
    }

private:

    Gtk::Grid m_layout;

    Gtk::ScrolledWindow m_scrolledWindow;
    Gtk::TextView m_textArea;

    Glib::RefPtr<Gtk::TextBuffer::Mark> m_endMark;

    Gtk::Button m_button{"Add line at the end..."};

};

int main(int argc, char* argv[]) 
{
    auto app = Gtk::Application::create(argc, argv, "so.question.q66329582");

    MyWindow window;
    window.show_all();

    return app->run(window);
}

推荐阅读