Gtk::Layout中的可滚动绘图



我想在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);
    }
    

相关内容

  • 没有找到相关文章

最新更新