Gtk - 为错误的小部件触发了绘制事件,并且未重绘小部件

时间:2017-06-07 12:45:40

标签: gtk gtk3 vala

我正在尝试创建自定义可滚动文本区域。我在DrawingArea内创建了ScrollBarGrid。我已将draw DrawingArea事件附加到this.on_draw方法,该方法仅查看ScrollBar的值并在绘制之前适当移动Cairo.Context Pango.Layout

第一个问题是,即使我没有使用this.on_draw注册任何事件,只要触摸ScrollBar就会调用ScrollBar。我该如何防止这种情况,或者检查一下?

第二个问题是,即使this.on_draw被调用,除非Context值接近ScrollBar或{{1},否则不会显示对0所做的更改}(100是Adjustment的上限值)。为什么会这样?

我确实发现如果100 connect value_changed事件ScrollBar发送给调用queue_redraw DrawingArea的方法,它会调用this.on_draw 1}}并在它之后正确显示它。但由于第二个问题,我认为this.on_draw被不必要地调用了太多次。那么,什么是"适当的"实现这个目标的方法?

using Cairo;
using Gdk;
using Gtk;
using Pango;

public class Texter : Gtk.Window {
    private Gtk.DrawingArea darea;
    private Gtk.Scrollbar scroll;

    private string text = "Hello\nWorld!";

    public Texter () {
        GLib.Object (type: Gtk.WindowType.TOPLEVEL);

        Gtk.Grid grid = new Gtk.Grid();
        this.add (grid);

        var drawing_area = new Gtk.DrawingArea ();
        drawing_area.set_size_request (200, 200);
        drawing_area.expand = true;
        drawing_area.draw.connect (this.on_draw);

        grid.attach (drawing_area, 0, 0);

        var scrollbar = new Gtk.Scrollbar (Gtk.Orientation.VERTICAL,
                                           new Gtk.Adjustment(0, 0, 100, 0, 0, 1));

        grid.attach (scrollbar, 1, 0);

        this.darea = drawing_area;
        this.scroll = scrollbar;

        this.destroy.connect (Gtk.main_quit);
    }

    private bool on_draw (Gtk.Widget sender, Cairo.Context ctx) {
        ctx.set_source_rgb (0.9, 0.9, 0.9);
        ctx.paint ();

        var y_offset = this.scroll.get_value();
        stdout.printf("%f\n", y_offset);

        ctx.set_source_rgb (0.25, 0.25, 0.25);
        ctx.move_to(0, 100 - y_offset);

        var layout = Pango.cairo_create_layout(ctx);
        layout.set_font_description(Pango.FontDescription.from_string("Sans 12"));
        layout.set_auto_dir(false);
        layout.set_text(this.text, this.text.length);
        Pango.cairo_show_layout(ctx, layout);

        return false;
    }

    static int main (string[] args) {
        Gtk.init (ref args);

        var window = new Texter ();
        window.show_all ();

        Gtk.main ();

        return 0;
    }
}

另外,如果您在上面的代码中找到错误,请指出任何(可能无关的)错误。

1 个答案:

答案 0 :(得分:2)

您缺少的部分是draw信号意味着“重绘所有内容”。相反,GTK +将cairo上下文的剪辑区域设置为需要重绘的部分,因此您执行的其他操作都没有任何效果。 cairo函数cairo_clip_extents()将告诉您该区域是什么。 GtkWidget上的queue_draw_area()方法将允许您明确标记某个绘图区域,而不是整个小部件。

但是你的滚动条方法无论如何都是错误的:你正试图从头开始构建整个基础架构!请考虑使用GtkScrolledWindow。这会自动处理滚动的所有细节,并会为您提供我提到的叠加滚动条。您需要做的就是将GtkDrawingArea的大小设置为您想要的大小,GtkScrolledWindow将完成剩下的工作。执行此操作的最佳方法是继承GtkDrawingArea并覆盖get_preferred_height()和/或get_preferred_width()虚函数(确保将最小和自然大小都设置为您希望特定维度的大小)。如果您以后需要更改此大小,请调用GtkWidget的queue_resize()方法。 (你可能可以只使用set_size_request(),但我所描述的是这样做的首选方式。)这样做也可以让你不必担心改变你的开罗坐标; GtkScrolledWindow为你做这件事。