Sophie

Sophie

distrib > Mandriva > 2010.0 > i586 > media > contrib-release > by-pkgid > 345aa895e80053137c21f8693106c3a0 > files > 17

gtkmm2.4-documentation-2.17.4-1mdv2010.0.noarch.rpm

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Chapter 25. Custom Widgets</title>
<link rel="stylesheet" href="style.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.75.1">
<link rel="home" href="index.html" title="Programming with gtkmm">
<link rel="up" href="index.html" title="Programming with gtkmm">
<link rel="prev" href="sec-i18n-getting-help-with-translations.html" title="Getting help with translations">
<link rel="next" href="sec-custom-widgets.html" title="Custom Widgets">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">Chapter 25. Custom Widgets</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="sec-i18n-getting-help-with-translations.html"><img src="icons/prev.png" alt="Prev"></a> </td>
<th width="60%" align="center"> </th>
<td width="20%" align="right"> <a accesskey="n" href="sec-custom-widgets.html"><img src="icons/next.png" alt="Next"></a>
</td>
</tr>
</table>
<hr>
</div>
<div class="chapter" title="Chapter 25. Custom Widgets">
<div class="titlepage"><div><div><h2 class="title">
<a name="chapter-customwidgets"></a>Chapter 25. Custom Widgets</h2></div></div></div>
<div class="toc">
<p><b>Table of Contents</b></p>
<ul>
<li><span class="sect1"><a href="chapter-customwidgets.html#sec-custom-containers">Custom Containers</a></span></li>
<li><span class="sect1"><a href="sec-custom-widgets.html">Custom Widgets</a></span></li>
</ul>
</div>
<p><span class="application">gtkmm</span> makes it very easy to derive new widgets by inheriting from an existing widget class, either by deriving from a container and adding child widgets, or by deriving from a single-item widget, and changing its behaviour. But you might occasionally find that no suitable starting point already exists. In this case, you can implement a widget from scratch.</p>
<div class="sect1" title="Custom Containers">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="sec-custom-containers"></a>Custom Containers</h2></div></div></div>
<p>When deriving from <code class="classname">Gtk::Container</code>, you should override the following virtual methods:
    </p>
<div class="itemizedlist"><ul class="itemizedlist" type="disc">
<li class="listitem"><p><code class="methodname">on_size_request()</code>: Calculate the minimum height and width needed by the container.</p></li>
<li class="listitem"><p><code class="methodname">on_size_allocate()</code>: Position the child widgets, given the height and width that the container has actually been given.</p></li>
<li class="listitem"><p><code class="methodname">forall_vfunc()</code>: Call the same callback for each of the children.</p></li>
<li class="listitem"><p><code class="methodname">on_add()</code>: </p></li>
<li class="listitem"><p><code class="methodname">on_remove()</code>: </p></li>
<li class="listitem"><p><code class="methodname">child_type_vfunc()</code>: Return what type of child can be added.</p></li>
</ul></div>
<p>
    </p>
<p>The <code class="methodname">on_size_request()</code> and
        <code class="methodname">on_size_allocate()</code> virtual methods control the
        layout of the child widgets. For instance, if your container has 2
        child widgets, with one below the other, your
        <code class="methodname">on_size_request()</code> might report the maximum of
        their widths and the sum of their heights. If you want padding between
        the child widgets then you would add that to the width and height too.
        Your widget's container will use this result to ensure that your widget
        gets enough space, and not less. By examining each widget's parent, and
        its parent, this logic will eventually decide the size of the top-level
        window.</p>
<p><code class="methodname">on_size_allocate()</code>, however, receives the actual
       height and width that the parent container has decided to give to your
       widget. This might be more than the minimum, for instance if the
       top-level window has been expanded. You might choose to ignore the extra
       space and leave a blank area, or you might choose to expand your child
       widgets to fill the space, or you might choose to expand the padding
       between your widgets. Its your container, so you decide. Don't forget to
       call <code class="methodname">set_allocation()</code> inside your
       <code class="methodname">on_size_allocate()</code> implementation to actually use the
       allocated space that has been offered by the parent container.</p>
<p>Unless your container is a top-level window that derives from
      <code class="classname">Gtk::Window</code>, you should also call
      <code class="methodname">Gtk::Container::set_flags(Gtk::NO_WINDOW)</code> in your
      constructor. Otherwise, your container will appear in its own window,
      regardless of what container you put it in. And unless your container
      draws directly onto the underlying <code class="classname">Gdk::Window</code>,
      you should probably call
      <code class="methodname">set_redraw_on_allocate(false)</code> to improve
      performance.</p>
<p>By overriding <code class="methodname">forall_vfunc()</code> you can allow
      applications to operate on all of the container's child widgets. For
      instance, <code class="methodname">show_all_children()</code> uses this to find all
      the child widgets and show them.</p>
<p>Although your container might have its own method to set the child
      widgets, you should still provide an implementation for the virtual
      <code class="methodname">on_add()</code> and <code class="methodname">on_remove()</code>
      methods from the base class, so that the add() and remove() methods will
      do something appropriate if they are called.</p>
<p>Your implementation of the <code class="methodname">child_type_vfunc()</code>
      method should report the type of widget that may be added to your
      container, if it is not yet full. This is usually
      <code class="methodname">Gtk::Widget::get_type()</code> to indicate that the
      container may contain any class derived from
      <code class="classname">Gtk::Widget</code>. If the container may not contain any
      more widgets, then this method should return
      <code class="literal">G_TYPE_NONE</code>.</p>
<div class="sect2" title="Example">
<div class="titlepage"><div><div><h3 class="title">
<a name="custom-container-example"></a>Example</h3></div></div></div>
<p>This example implements a container with two child widgets, one above
        the other. Of course, in this case it would be far simpler just to use
        a <code class="classname">Gtk::VBox</code>.</p>
<div class="figure">
<a name="figure-custom-container"></a><p class="title"><b>Figure 25.1. Custom Container</b></p>
<div class="figure-contents"><div class="screenshot"><div><img src="figures/custom_container.png" alt="Custom Container"></div></div></div>
</div>
<br class="figure-break"><p><a class="ulink" href="http://git.gnome.org/cgit/gtkmm-documentation/tree/examples/book/custom/custom_container/" target="_top">Source Code</a></p>
<p>File: <code class="filename">mycontainer.h</code>
</p>
<pre class="programlisting">
#ifndef GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H
#define GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H

#include &lt;gtkmm/container.h&gt;

class MyContainer : public Gtk::Container
{
public:
  MyContainer();
  virtual ~MyContainer();

  void set_child_widgets(Gtk::Widget&amp; child_one, Gtk::Widget&amp; child_two);

protected:

  //Overrides:
  virtual void on_size_request(Gtk::Requisition* requisition);
  virtual void on_size_allocate(Gtk::Allocation&amp; allocation);

  virtual void forall_vfunc(gboolean include_internals, GtkCallback callback, gpointer callback_data);

  virtual void on_add(Gtk::Widget* child);
  virtual void on_remove(Gtk::Widget* child);
  virtual GType child_type_vfunc() const;

  Gtk::Widget* m_child_one;
  Gtk::Widget* m_child_two;
};

#endif //GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H
</pre>
<p>File: <code class="filename">examplewindow.h</code>
</p>
<pre class="programlisting">
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include &lt;gtkmm.h&gt;
#include "mycontainer.h"

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();

  //Child widgets:
  Gtk::VBox m_VBox;
  MyContainer m_MyContainer;
  Gtk::Button m_Button_One;
  Gtk::Label m_Button_Two;
  Gtk::HButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H
</pre>
<p>File: <code class="filename">examplewindow.cc</code>
</p>
<pre class="programlisting">
#include &lt;iostream&gt;
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_Button_One("Child One"),
  m_Button_Two("Child 2"),
  m_Button_Quit("Quit")
{
  set_title("Custom Container example");
  set_border_width(6);
  set_default_size(400, 200);

  add(m_VBox);

  //Add the child widgets to the custom container:
  m_MyContainer.set_child_widgets(m_Button_One, m_Button_Two);
  m_Button_One.show();
  m_Button_Two.show();

#ifdef GLIBMM_PROPERTIES_ENABLED
  m_Button_Two.property_xalign() = 1.0f; 
#else
  m_Button_Two.set_property("xalign", 1.0f); 
#endif //GLIBMM_PROPERTIES_ENABLED

  m_VBox.pack_start(m_MyContainer, Gtk::PACK_EXPAND_WIDGET);
  m_MyContainer.show();

  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(6);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
              &amp;ExampleWindow::on_button_quit) );

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

</pre>
<p>File: <code class="filename">main.cc</code>
</p>
<pre class="programlisting">
#include &lt;gtkmm/main.h&gt;
#include "examplewindow.h"

int main(int argc, char *argv[])
{
  Gtk::Main kit(argc, argv);

  ExampleWindow window;
  //Shows the window and returns when it is closed.
  Gtk::Main::run(window);

  return 0;
}
</pre>
<p>File: <code class="filename">mycontainer.cc</code>
</p>
<pre class="programlisting">
#include &lt;iostream&gt;
#include "mycontainer.h"

MyContainer::MyContainer()
: m_child_one(0), m_child_two(0)
{
  set_flags(Gtk::NO_WINDOW);
  set_redraw_on_allocate(false);
}

MyContainer::~MyContainer()
{
}

void MyContainer::set_child_widgets(Gtk::Widget&amp; child_one,
        Gtk::Widget&amp; child_two)
{
  m_child_one = &amp;child_one;
  m_child_two = &amp;child_two;

  m_child_one-&gt;set_parent(*this);
  m_child_two-&gt;set_parent(*this);
}

void MyContainer::on_size_request(Gtk::Requisition* requisition)
{
  //Initialize the output parameter:
  *requisition = Gtk::Requisition();

  //Discover the total amount of minimum space needed by this container widget,
  //by examining its child widgets.  The layouts in this custom container will
  //be arranged vertically, one above the other.

   Gtk::Requisition child_requisition_one = {0, 0};
   Gtk::Requisition child_requisition_two = {0, 0};
   if(m_child_one &amp;&amp; m_child_one-&gt;is_visible())
     child_requisition_one = m_child_one-&gt;size_request();

   if(m_child_two &amp;&amp; m_child_two-&gt;is_visible())
     child_requisition_two = m_child_two-&gt;size_request();

  //See which one has the most width:
  int max_width = MAX(child_requisition_one.width,
          child_requisition_two.width);

  //Add the heights together:
  int total_height = child_requisition_one.height +
      child_requisition_two.height;

  //Request the width for this container based on the sizes requested by its
  //child widgets:
  requisition-&gt;height = total_height;
  requisition-&gt;width = max_width;
}

void MyContainer::on_size_allocate(Gtk::Allocation&amp; allocation)
{
  //Do something with the space that we have actually been given:
  //(We will not be given heights or widths less than we have requested, though
  //we might get more)

  //Use the offered allocation for this container:
  set_allocation(allocation);

  //Assign sign space to the child:
  Gtk::Allocation child_allocation_one, child_allocation_two;

  //Place the first child at the top-left, 
  child_allocation_one.set_x( allocation.get_x() );
  child_allocation_one.set_y( allocation.get_y() );

  //Make it take up the full width available:
  child_allocation_one.set_width( allocation.get_width() );

  //Make it take up half the height available:
  child_allocation_one.set_height( allocation.get_height() / 2);

  if(m_child_one &amp;&amp; m_child_one-&gt;is_visible())
    m_child_one-&gt;size_allocate(child_allocation_one);

  //Place the second child below the first child:
  child_allocation_two.set_x( allocation.get_x() );
  child_allocation_two.set_y( allocation.get_y() +
          child_allocation_one.get_height());

  //Make it take up the full width available:
  child_allocation_two.set_width( allocation.get_width() );

  //Make it take up half the height available:
  child_allocation_two.set_height( allocation.get_height() -
          child_allocation_one.get_height());

  if(m_child_two &amp;&amp; m_child_two-&gt;is_visible())
    m_child_two-&gt;size_allocate(child_allocation_two);
}

void MyContainer::forall_vfunc(gboolean, GtkCallback callback, gpointer callback_data)
{
  if(m_child_one)
    callback(m_child_one-&gt;gobj(), callback_data);

  if(m_child_two)
    callback(m_child_two-&gt;gobj(), callback_data);
}

void MyContainer::on_add(Gtk::Widget* child)
{
  if(!m_child_one)
  {
    m_child_one = child;
    m_child_one-&gt;set_parent(*this);
  }
  else if(!m_child_two)
  {
    m_child_two = child;

    m_child_two-&gt;set_parent(*this);
  }
}

void MyContainer::on_remove(Gtk::Widget* child)
{
  if(child)
  {
    const bool visible = child-&gt;is_visible();
    bool found = false;

    if(child == m_child_one)
    {
      m_child_one = 0;
      found = true;
    }
    else if(child == m_child_two)
    {
      m_child_two = 0;
      found = true;
    }

    if(found)
    {
      child-&gt;unparent();

      if(visible)
        queue_resize();
    }
  }
}

GType MyContainer::child_type_vfunc() const
{
  //If there is still space for one widget, then report the type of widget that
  //may be added.
  if(!m_child_one || !m_child_two)
    return Gtk::Widget::get_type();
  else
  {
    //No more widgets may be added.
    return G_TYPE_NONE;
  }
}

</pre>
</div>
</div>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="sec-i18n-getting-help-with-translations.html"><img src="icons/prev.png" alt="Prev"></a> </td>
<td width="20%" align="center"> </td>
<td width="40%" align="right"> <a accesskey="n" href="sec-custom-widgets.html"><img src="icons/next.png" alt="Next"></a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">Getting help with translations </td>
<td width="20%" align="center"><a accesskey="h" href="index.html"><img src="icons/home.png" alt="Home"></a></td>
<td width="40%" align="right" valign="top"> Custom Widgets</td>
</tr>
</table>
</div>
</body>
</html>