Gtk - 为错误的小部件触发绘制事件,并且不重绘小部件
Gtk - draw event fired for wrong widget, and widget not redrawn
我正在尝试创建自定义可滚动文本区域。我在 Grid
中创建了一个 DrawingArea
和一个 ScrollBar
。我已将 DrawingArea
的 draw
事件附加到 this.on_draw
方法,该方法仅查看 ScrollBar
的值并在绘制 [=19] 之前适当移动 Cairo.Context
=].
第一个问题是 this.on_draw
会在触摸 ScrollBar
时被调用,即使我没有向 ScrollBar
注册任何事件。我该如何防止或检查它?
第二个问题是,即使 this.on_draw
被调用,对 Context
所做的更改也不会显示,除非 ScrollBar
值接近 0
或 100
(100是Adjustment的上限值)。为什么会这样?
我确实发现,如果我 connect
ScrollBar
的 value_changed
事件到调用 DrawingArea
的 queue_redraw
的方法,它将调用this.on_draw
并在其后正确显示。但是由于第二个问题,我认为 this.on_draw
被不必要地调用了太多次。那么,"proper" 实现此目的的方法是什么?
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;
}
}
此外,如果您在上述代码中发现任何错误(可能无关),请指出。
您缺少的部分是 draw
信号 而不是 意味着 "redraw everything"。相反,GTK+ 将 cairo 上下文的剪辑区域设置为需要重绘的部分,因此您所做的其他一切都没有任何效果。 cairo 函数 cairo_clip_extents()
会告诉你那个区域是什么。 GtkWidget 上的 queue_draw_area()
方法将允许您明确标记要绘制的特定区域,而不是整个小部件。
但无论如何,您处理滚动条的方法都是错误的:您正试图从头开始构建整个基础架构!考虑改用 GtkScrolledWindow。这会自动为您处理滚动的所有细节,并会为您提供我提到的覆盖滚动条。您需要做的就是将 GtkDrawingArea 的大小设置为您想要的大小,GtkScrolledWindow 将完成剩下的工作。最好的方法是子类化 GtkDrawingArea 并覆盖 get_preferred_height()
and/or get_preferred_width()
虚函数(确保将最小尺寸和自然尺寸都设置为该特定尺寸所需的尺寸) .如果以后需要更改此大小,请调用 GtkWidget 的 queue_resize()
方法。 (您可能 可以 仅使用 set_size_request()
,但我所描述的是执行此操作的首选方法。)这样做还可以让您不必担心关于转换您的开罗坐标; GtkScrolledWindow 会为您做这件事。
我正在尝试创建自定义可滚动文本区域。我在 Grid
中创建了一个 DrawingArea
和一个 ScrollBar
。我已将 DrawingArea
的 draw
事件附加到 this.on_draw
方法,该方法仅查看 ScrollBar
的值并在绘制 [=19] 之前适当移动 Cairo.Context
=].
第一个问题是 this.on_draw
会在触摸 ScrollBar
时被调用,即使我没有向 ScrollBar
注册任何事件。我该如何防止或检查它?
第二个问题是,即使 this.on_draw
被调用,对 Context
所做的更改也不会显示,除非 ScrollBar
值接近 0
或 100
(100是Adjustment的上限值)。为什么会这样?
我确实发现,如果我 connect
ScrollBar
的 value_changed
事件到调用 DrawingArea
的 queue_redraw
的方法,它将调用this.on_draw
并在其后正确显示。但是由于第二个问题,我认为 this.on_draw
被不必要地调用了太多次。那么,"proper" 实现此目的的方法是什么?
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;
}
}
此外,如果您在上述代码中发现任何错误(可能无关),请指出。
您缺少的部分是 draw
信号 而不是 意味着 "redraw everything"。相反,GTK+ 将 cairo 上下文的剪辑区域设置为需要重绘的部分,因此您所做的其他一切都没有任何效果。 cairo 函数 cairo_clip_extents()
会告诉你那个区域是什么。 GtkWidget 上的 queue_draw_area()
方法将允许您明确标记要绘制的特定区域,而不是整个小部件。
但无论如何,您处理滚动条的方法都是错误的:您正试图从头开始构建整个基础架构!考虑改用 GtkScrolledWindow。这会自动为您处理滚动的所有细节,并会为您提供我提到的覆盖滚动条。您需要做的就是将 GtkDrawingArea 的大小设置为您想要的大小,GtkScrolledWindow 将完成剩下的工作。最好的方法是子类化 GtkDrawingArea 并覆盖 get_preferred_height()
and/or get_preferred_width()
虚函数(确保将最小尺寸和自然尺寸都设置为该特定尺寸所需的尺寸) .如果以后需要更改此大小,请调用 GtkWidget 的 queue_resize()
方法。 (您可能 可以 仅使用 set_size_request()
,但我所描述的是执行此操作的首选方法。)这样做还可以让您不必担心关于转换您的开罗坐标; GtkScrolledWindow 会为您做这件事。