1

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

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

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

我的问题是:

  • 这是处理这种可滚动绘图的正确方法吗?
  • 或者有什么方法可以让框架为我完成这项工作?
4

1 回答 1

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与滚动条对应的 s 。因为这些调整的正好是可见视口的左上角。现在,如果我们希望我们的自定义绘图使用绝对画布坐标,我们只需translate()要给定的 Cairo 上下文。注意:完成后save()对状态和restore()原始状态很重要,否则这些翻译会累积。


这是一些示例代码来演示此自定义绘图

  • 我们派生一个自定义容器类,调用CanvasGtk::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);
    }
    
于 2016-11-01T22:57:27.720 回答