Gtk :: Layout中的可滚动绘图

时间:2016-10-30 02:59:08

标签: scroll drawing gtk3 cairo gtkmm3

我想在Gtk::Layout内使用自定义绘图。也就是说,我正在使用Gtk3(GTKmm 3.14.0)的C ++绑定,并且我在自定义绘图之上放置了嵌入在“画布”上的小部件。基本上这很好用。

现在问题与滚动有关。 Gtk::Layout可以放在Gtk::ScrolledWindow中,当可滚动区域设置为大于可见分配的值时,滚动条将显示出来。不幸的是,这些滚动条仅影响 嵌入式小部件的位置,而我的自定义绘图仍保留在窗口内的固定位置。

这意味着,Gtk::Allocation和cairo上下文似乎都与精确可见区域相关,而不是与扩展虚拟“画布”相关。我可以通过从滚动条访问调整然后相应地翻译cairo上下文来解决这个问题...

我的问题是:

  • 这是处理这种可滚动绘图的正确方法吗?
  • 或者是否有某种方法让框架为我工作?

1 个答案:

答案 0 :(得分:1)

根据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。因为这些调整的恰好是可见视口的左上角。现在,如果我们希望自定义绘图使用绝对画布坐标,我们只需要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);
    }