我想在Gtk::Layout中使用自定义绘图。也就是说,我正在使用Gtk3 (GTKmm 3.14.0
)的c++绑定,并且我已经在"画布"上嵌入了小部件,在我的自定义绘图之上。
现在问题与滚动有关。Gtk::Layout
可以放置到Gtk::ScrolledWindow
中,当可滚动区域设置为大于可见分配时,滚动条将显示出来。不幸的是,这些滚动条只影响嵌入小部件的位置,而我的自定义绘图保持在窗口内的固定位置。
这意味着,Gtk::Allocation
和cairo上下文似乎都与可见区域相关,而不是与扩展的虚拟"画布"相关。我可以通过访问滚动条的调整来解决这个问题,然后相应地翻译cairo上下文…
- 这是正确的方式来处理这样一个可滚动的绘图?
- 或者有一些方法让框架为我做这项工作?
从gtk+3.0-3.14.5
的源代码(在Debian/Stable中)来看,Gtk::Layout
没有对绘图上下文进行任何调整。它只是调用从GtkWidget
继承的draw()
函数。另一方面,Gtk::Layout
是一个成熟的容器(它继承自Gtk::Container
),它是可滚动的,这意味着它通过向每个嵌入的子部件传递适当的分配(屏幕区域)来处理gtk_layout_size_allocate()
——在这方面,它确实处理与滚动虚拟画布相关的移动和剪切(调用gdk_window_move_resize()
)。
因此,如果我们想要将嵌入的子部件与自定义绘图结合起来,我们需要手动弥合这个差异。这实际上很简单:我们所需要做的就是查看与滚动条对应的Gtk::Adjusment
。因为这些调整的值恰好位于可视视窗的左上角。现在,如果我们想让我们的自定义绘图使用绝对画布坐标,我们只需要translate()
给定的Cairo上下文。注意:重要的是save()
的状态和restore()
的原始状态完成后,否则这些翻译将积累。
下面是一些示例代码来演示这个自定义绘图
- 我们从
Gtk::Layout
中派生了一个自定义容器类Canvas
- 我们覆盖
on_draw()
处理器,因为只有在那里所有的大小分配嵌入的子部件已经被处理 -
分层:子部件总是按照它们添加到
Gtk::Layout
容器的顺序绘制。任何在调用继承的on_draw()
函数之前完成的自定义绘图将位于这些小部件的下面;之后的任何绘图都将在之上进行。 -
如果有必要,我们可以使用
foreach(callback)
机制来访问所有子部件,以找出它们的当前位置和扩展void Canvas::determineExtension() { if (not recalcExtension_) return; uint extH=20, extV=20; Gtk::Container::ForeachSlot callback = [&](Gtk::Widget& chld) { auto alloc = chld.get_allocation(); uint x = alloc.get_x(); uint y = alloc.get_y(); x += alloc.get_width(); y += alloc.get_height(); extH = max (extH, x); extV = max (extV, y); }; foreach(callback); recalcExtension_ = false; set_size (extH, extV); // define extension of the virtual canvas } bool Canvas::on_draw(Cairo::RefPtr<Cairo::Context> const& cox) { if (shallDraw_) { uint extH, extV; determineExtension(); get_size (extH, extV); auto adjH = get_hadjustment(); auto adjV = get_vadjustment(); double offH = adjH->get_value(); double offV = adjV->get_value(); cox->save(); cox->translate(-offH, -offV); // draw red diagonal line cox->set_source_rgb(0.8, 0.0, 0.0); cox->set_line_width (10.0); cox->move_to(0, 0); cox->line_to(extH, extV); cox->stroke(); cox->restore(); // cause child widgets to be redrawn bool event_is_handled = Gtk::Layout::on_draw(cox); // any drawing which follows happens on top of child widgets... cox->save(); cox->translate(-offH, -offV); cox->set_source_rgb(0.2, 0.4, 0.9); cox->set_line_width (2.0); cox->rectangle(0,0, extH, extV); cox->stroke(); cox->restore(); return event_is_handled; } else return Gtk::Layout::on_draw(cox); }