从gtk+3.0-3.14.5
(在 Debian/Stable 中)的源代码来看,Gtk::Layout
没有调整绘图上下文。它只是调用draw()
从GtkWidget
. 另一方面,Gtk::Layout
它是一个成熟的容器(它继承自Gtk::Container
),并且它是可滚动的,这意味着它gtk_layout_size_allocate()
通过将合适的分配(屏幕区域)传递给每个嵌入的子小部件来进行处理——在这方面它确实处理与滚动虚拟画布(调用)相关的移动和剪辑gdk_window_move_resize()
。
因此,如果我们想将嵌入的子小部件与自定义绘图相结合,我们需要手动弥合这种差异。这实际上很简单:我们需要做的就是查看Gtk::Adjusment
与滚动条对应的 s 。因为这些调整的值正好是可见视口的左上角。现在,如果我们希望我们的自定义绘图使用绝对画布坐标,我们只需translate()
要给定的 Cairo 上下文。注意:完成后save()
对状态和restore()
原始状态很重要,否则这些翻译会累积。
这是一些示例代码来演示此自定义绘图
- 我们派生一个自定义容器类,调用
Canvas
自Gtk::Layout
- 我们覆盖了
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);
}