Next Previous Contents

10. Single-item widgets

In this chapter, we discuss the single-item (i.e., derived from Gtk::Bin) container widgets, with the exception of the Button and Window widgets, which we've already covered.

We also discuss the Gtk::Paned widget, which allows you to divide a window into two separate "panes". This widget actually contains two child widgets, but it doesn't maintain a list, so we've included it here.

10.1 Frames

Frames can be used to enclose one or a group of widgets with a box which can optionally be labelled. The position of the label and the style of the box can be altered to suit.

The Gtk::Frame constructor is:

Gtk::Frame(const nstring &label);

The label is by default placed in the upper left-hand corner of the frame. A value of NULL for the label argument will result in no label being displayed. The text of the label can be changed using:

void Gtk::Frame::set_label(const nstring &label);

The position of the label can be changed using:

void Gtk::Frame::set_label_align(gfloat xalign,
                                 gfloat yalign);

xalign and yalign take values between 0.0 and 1.0. xalign indicates the position of the label along the top horizontal of the frame. A value of 0.0 will left-justify the label, 1.0 right-justifies it, and 0.5 centres it. Values in-between also work; for example, 0.25 will place the label a quarter of the way across the top. yalign is ignored. The default value of xalign is 0.0.

You can alter the frame's appearance using:

void Gtk::Frame::set_shadow_type(GtkShadowType type);

where type can is one of

The following code example illustrates the use of the Frame widget.

Source location: examples/frame/frame.cc

#include <gtk--/button.h>
#include <gtk--/frame.h>
#include <gtk--/main.h>
#include <gtk--/window.h>

class AppWindow : Gtk::Window
{
public: 
  AppWindow();
  ~AppWindow();

  /* It's a good idea to do this for all application windows. */
  gint delete_event_impl (GdkEventAny*)
  {
    Gtk::Main::quit();
    return 0;
  }

};

AppWindow::AppWindow()
  : Gtk::Window (GTK_WINDOW_TOPLEVEL)
{
  Gtk::Frame* frame;

  /* Set some window properties */
  set_title("Frame Example");
  set_usize(300, 300);

  /* Here we connect the "destroy" event to a signal handler */ 
  destroy.connect (Gtk::Main::quit.slot());

  /* Sets the border width of the window. */
  set_border_width (10);

  /* Create a Frame */
  frame = manage( new Gtk::Frame() );
  add(*frame);

  /* Set the frames label */
  frame->set_label( "GTK Frame Widget" );

  /* Align the label at the right of the frame */
  frame->set_label_align( 1.0, 0.0);

  /* Set the style of the frame */
  frame->set_shadow_type(GTK_SHADOW_ETCHED_OUT);

  /* Show the frame */
  frame->show();

  /* show this window */
  show ();

}

AppWindow::~AppWindow() {}

int main( int   argc,
          char *argv[] )
{
  /* Initialise GTK */
  Gtk::Main kit(&argc, &argv);
    
  /* Create a new window */
  AppWindow app;
    
  /* Enter the event loop */
  Gtk::Main::run ();
    
  return(0);
}

10.2 Aspect Frames

The aspect frame widget is like a frame widget, except that it also enforces the aspect ratio (the ratio of the width to the height) of the child widget to have a certain value, adding extra space if necessary. This is useful, for instance, if you want to display a photograph.

To create a new aspect frame, use:

Gtk::AspectFrame(const string label,
                 gfloat       xalign,
                 gfloat       yalign,
                 gfloat       ratio,
                 gint         obey_child);

xalign and yalign specify the alignment factors. If obey_child is true, the aspect ratio of a child widget will match the aspect ratio of the ideal size it requests. Otherwise, it is given by ratio.

To change the options of an existing aspect frame, you can use:

void Gtk::AspectFrame::set(gfloat xalign,
                           gfloat yalign,
                           gfloat ratio,
                           gint   obey_child);

The following program uses a Gtk::AspectFrame to present a drawing area whose aspect ratio will always be 2:1, no matter how the user resizes the top-level window.

Source location: examples/aspectframe/aspectframe.cc

/* example-start aspectframe aspectframe.cc */

#include <gtk--/window.h>
#include <gtk--/main.h>
#include <gtk--/drawingarea.h>
#include <gtk--/aspectframe.h>

class AspectWindow: public Gtk::Window
{
    Gtk::AspectFrame frame;
    Gtk::DrawingArea area;
public:
    AspectWindow();
private:
    gint delete_event_impl(GdkEventAny*) { 
        Gtk::Main::quit(); return 0; 
    }
};

AspectWindow::AspectWindow():
    frame("2x1", /* label */
          0.5, /* center x */
          0.5, /* center y */
          2, /* xsize/ysize = 2 */
          FALSE /* ignore child's aspect */)
{
    set_title("Aspect Frame");
    set_border_width(10);

    /* Add a child widget to the aspect frame */
    /* Ask for a 200x200 window, but the AspectFrame will give us a 200x100
     * window since we are forcing a 2x1 aspect ratio */
    area.set_usize(200, 200);
    frame.add(area);
    /* Create an aspect_frame and add it to our toplevel window */
    frame.show_all();
    add(frame);    
}

int
main (int argc, char *argv[])
{
    Gtk::Main m(&argc, &argv);
    AspectWindow window;
   
    window.show();
    m.run();

    return 0;
}
/* example-end */

10.3 The EventBox

Some Gtk-- widgets don't have associated X windows; they draw on their parents' windows. (Of course, you should not let your own children do this.) Because of this, they cannot receive events. Also, if they are incorrectly sized, they don't clip, so you can get messy overwriting etc. If you require more from these widgets, the EventBox is for you. Although the name EventBox emphasises the event-handling function, the widget can also be used for clipping (and more; see the example below).

The constructor for Gtk::EventBox is:

Gtk::EventBox();

A child widget can be added to the EventBox using:

event_box.add(child_widget);

The following example demonstrates both uses of an EventBox - a label is created that is clipped to a small box, and set up so that a mouse-click on the label causes the program to exit. Resizing the window reveals varying amounts of the label.

Source location: examples/eventbox/eventbox.cc

/* example-start eventbox eventbox.c */

#include <gtk--/window.h>
#include <gtk--/main.h>
#include <gtk--/eventbox.h>
#include <gtk--/label.h>
#include <gtk--/tooltips.h>

class AppWindow : public Gtk::Window
{
  Gtk::Tooltips tips;

public:
  AppWindow();
  ~AppWindow();

  virtual gint delete_event_impl (GdkEventAny*);
  gint callback(GdkEventButton*)
    { 
      Gtk::Main::quit();
      return 1;
    }
};


AppWindow::AppWindow()
  : Gtk::Window(GTK_WINDOW_TOPLEVEL)
{
  Gtk::EventBox *event_box;
  Gtk::Label *label;

  set_title ("Event Box");
  set_border_width (10);

  /* Create an EventBox and add it to our toplevel window */
    
  event_box = manage( new Gtk::EventBox () );
  add (*event_box);

  /* Create a long label */
    
  label = manage( new Gtk::Label ("Click here to quit, quit, quit, quit, quit") );
  event_box-> add (*label);

  /* Clip it short. */
  label->set_usize (110, 20);

  /* And bind an action to it */
  event_box->set_events (GDK_BUTTON_PRESS_MASK);
  event_box->button_press_event.connect(slot(*this, &AppWindow::callback));
  tips.set_tip(*event_box,"Click me!");
    
  /* Yet one more thing you need an X window for ... */
    
  event_box->realize ();
//  event_box->get_window().set_cursor (gdk_cursor_new (GDK_HAND1));

  show_all ();    
}

AppWindow::~AppWindow() {}

gint AppWindow::delete_event_impl (GdkEventAny*)
{
  Gtk::Main::quit();
  return 0;
}


int 
main (int argc, char *argv[])
{
    
    Gtk::Main main (argc, argv);
    AppWindow window;
    
    Gtk::Main::run();
    
    return(0);
}
/* example-end */

10.4 The Alignment widget

The Alignment widget allows you to place a widget within its window at a position and size relative to the size of the Alignment widget itself. It's useful for centering a widget within the window.

There is only one method associated with Gtk::Alignment, other than the constructor:

Gtk::Alignment(gfloat xalign,
               gfloat yalign,
               gfloat xscale,
               gfloat yscale);

void Gtk::Alignment::set(GtkAlignment *alignment,
                         gfloat        xalign,
                         gfloat        yalign,
                         gfloat        xscale,
                         gfloat        yscale);

set() allows the alignment parameters of an exisiting Alignment widget to be altered.

All four alignment parameters are floating point numbers which can range from 0.0 to 1.0. The xalign and yalign arguments affect the position of the widget placed within the Alignment widget. The xscale and yscale arguments affect the amount of space allocated to the widget.

A child widget can be added to the Alignment widget using the usual add() method:

alignment.add(child_widget);

For an example of using the Alignment widget, refer to the example for the Progress Bar widget.

10.5 Viewports

A viewport widget allows you to place a larger widget within it such that you can view a part of it at a time. It uses the ubiquitous adjustment object to define the area that is currently in view.

It's fairly unlikely that you will ever need to use the Viewport widget directly. You are much more likely to use the Scrolled Windows widget, which itself uses the Viewport, and gives you scrollbars.

Here are the Gtk::Viewport constructors:

Gtk::Viewport(Gtk::Adjustment &hadjustment,
              Gtk::Adjustment &vadjustment);
Gtk::Viewport();

You can specify the horizontal and vertical adjustment objects that the widget is to use when you create the widget, or you can let the viewport create its own, which it will do if you don't pass any arguments to the constructor.

You can get and set the adjustments after the widget has been created using the following four methods:

Gtk::Adjustment* Gtk::Viewport::get_hadjustment();
Gtk::Adjustment* Gtk::Viewport::get_vadjustment();
void Gtk::Viewport::set_hadjustment(GtkAdjustment &adjustment);
void Gtk::Viewport::set_vadjustment(GtkAdjustment &adjustment);

The only other viewport function is used to alter its appearance:

void Gtk::Viewport::set_shadow_type(GtkShadowType  type );

Possible values for the type parameter are:

10.6 Scrolled Windows

Scrolled windows are used to create a scrollable area inside a real window. You can insert any type of widget into a scrolled window, and it will be accessible regardless of its size by using the scrollbars.

Gtk::ScrolledWindow has the following constructors:

Gtk::ScrolledWindow(Gtk::Adjustment *hadjustment,
                    Gtk::Adjustment *vadjustment);
Gtk::ScrolledWindow();

As with the Viewport, you can supply Adjustments at creation time, or you can let the widget supply its own by passing no arguments.

Scrolled windows have scrollbar policies which determine whether the scrollbars will be displayed or not; the policy can be set for each scrollbar using:

void Gtk::ScrolledWindow::set_policy(GtkPolicyType hscrollbar_policy,
                                     GtkPolicyType vscrollbar_policy);

The policy may be one of GTK_POLICY_AUTOMATIC or GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC will cause the scrolled window to display the scrollbar only if the contained widget is larger than the visible area; GTK_POLICY_ALWAYS will cause the scrollbar to always be displayed.

Use the following method to add a child widget to the scrolled window:

void Gtk::ScrolledWindow::add_with_viewport(const Gtk::Widget &child);

Note that this differs from the usual add() method.

Here is a simple example that packs 100 toggle buttons into a scrolled window. Try resizing the window, and notice how the scrollbars react.

Source location: examples/scrolledwin/scrolledwin.cc

/* example-start scrolledwin scrolledwin.c */

#include <stdio.h>
#include <gtk--/table.h>
#include <gtk--/dialog.h>
#include <gtk--/button.h>
#include <gtk--/scrolledwindow.h>
#include <gtk--/togglebutton.h>
#include <gtk--/main.h>

class Scrolledwin : public Gtk::Dialog
{
public:
        Scrolledwin();
        ~Scrolledwin();
};

Scrolledwin::~Scrolledwin() {};

Scrolledwin::Scrolledwin() {
    char buffer[32];
    int i, j;
    Gtk::ScrolledWindow *scrolled_window;
    Gtk::Table *table;
    Gtk::Button *button;
    
    /* Create a new dialog window for the scrolled window to be
     * packed into. A dialog is just like a normal window except it has a 
     * vbox and a horizontal separator packed into it. It's just a shortcut
     * for creating dialogs */
    destroy.connect(Gtk::Main::quit.slot());

    set_title ("GtkScrolledWindow example");
    set_border_width (0);
    set_usize(300, 300);
    
    /* create a new scrolled window. */
    scrolled_window = manage(new Gtk::ScrolledWindow());
    
    scrolled_window->set_border_width (10);
    
    /* the policy is one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS.
     * GTK_POLICY_AUTOMATIC will automatically decide whether you need
     * scrollbars, whereas GTK_POLICY_ALWAYS will always leave the scrollbars
     * there.  The first one is the horizontal scrollbar, the second, 
     * the vertical. */
    scrolled_window->set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    /* The dialog window is created with a vbox packed into it. */
    get_vbox()->pack_start (*scrolled_window);
    scrolled_window->show();
    
    /* create a table of 10 by 10 squares. */
    table = manage(new Gtk::Table(10, 10, false));
    
    /* set the spacing to 10 on x and 10 on y */
    table->set_row_spacings (10);
    table->set_col_spacings (10);
    
    /* pack the table into the scrolled window */
    scrolled_window->add_with_viewport (*table);
    table->show();
    
    /* this simply creates a grid of toggle buttons on the table
     * to demonstrate the scrolled window. */
    for (i = 0; i < 10; i++)
       for (j = 0; j < 10; j++) {
          sprintf (buffer, "button (%d,%d)\n", i, j);
          button = manage(new Gtk::ToggleButton(buffer));
          table->attach(*button, i, i+1, j, j+1);
          button->show();
       }
    
    /* Add a "close" button to the bottom of the dialog */
    button = manage(new Gtk::Button("close"));
    button->clicked.connect(destroy.slot());
    
    /* this makes it so the button is the default. */
    button->set_flags(GTK_CAN_DEFAULT);
    get_action_area()->pack_start (*button);
    
    /* This grabs this button to be the default button. Simply hitting
     * the "Enter" key will cause this button to activate. */
    button->grab_default();
    button->show ();
    
    show();
}

int main (int argc, char *argv[])
{
    Gtk::Main myapp(argc, argv);
    Scrolledwin scrolledwin;
    myapp.run();
    return(0);
}
/* example-end */

10.7 Paned Window Widgets

Panes are used to divide a window into halves, separated by a moveable divider. GTK-- provides two of these widgets: Gtk::HPaned adds a horizontal divider, and Gtk::VPaned adds a vertical one. Other than the names and the orientations, there's no difference between the two, so we'll describe them together.

Unlike the other widgets in this chapter, pane widgets contain not one, but two child widgets, one in each pane. Otherwise, they're no different from the other single-item widgets; they do, however, have different add() methods.

The pane constructors are:

Gtk::HPaned();
Gtk::VPaned();

To add child widgets to the panes, call

void Gtk::Paned::add1 (const Gtk::Widget &child);
void Gtk::Paned::add2 (const Gtk::Widget &child);

For HPaned, add1() adds the child to the upper pane; for VPaned, add1() adds to the left-hand pane. add2() adds to the opposite pane.

The appearance of the divider is adjustable. Dividers have a small, square handle which the user can use to move the divider; the size of this handle is adjustable. The gutter is a separation area between the two panes; the width of the gutter is also adjustable. These two parameters can be set using:

void Gtk::Paned::set_handle_size(guint16 size);
void Gtk::Paned::set_gutter_size(guint16 size);

where size is in pixels. Typical values for these are 10 and 16, respectively.

You can adjust the position of the divider using:

void Gtk::Paned::set_position(gint position);

where position is in pixels. It's a good idea to set the initial position using this method when you create the pane, since the default position isn't always what you want.

It's example time again. For the example, we'll create part of the user interface of an imaginary E-mail program. A window is divided into two portions vertically, with the top portion being a list of email messages and the bottom portion the text of the email message.

A couple of things to notice: Text can't be added to a Gtk::Text widget until it is realized (actually displayed on the screen). This could be done by calling Gtk::Widget::realize(), but as a demonstration of an alternate technique, we connect a handler to the realize signal to add the text. Also, we need to add the GTK_SHRINK option to some of the items in the table containing the text window and its scrollbars, so that when the bottom portion is made smaller, the correct portions shrink instead of being pushed off the bottom of the window.

Source location: examples/paned/paned.cc

#include <gtk--/scrolledwindow.h>
#include <gtk--/table.h>
#include <gtk--/list.h>
#include <gtk--/main.h>
#include <gtk--/scrollbar.h>
#include <gtk--/window.h>
#include <gtk--/text.h>
#include <gtk--/paned.h>
  
class AppMessages: public Gtk::ScrolledWindow 
{
public:
  AppMessages();
};

/* Create the list of "messages" */
AppMessages::AppMessages()
  : Gtk::ScrolledWindow()
{
    Gtk::List *list;
   
    /* Create a new scrolled window, with scrollbars only if needed */
    set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   
    /* Create a new list and put it in the scrolled window */
    list = manage(new Gtk::List());
    add_with_viewport (*list);

    /* Add some "messages" to the window */
   
    for (int i=0; i<10; i++)
        list->items().push_back(*manage(new Gtk::ListItem("message")));
    list->show ();
}

class AppText : public Gtk::Table
{
  Gtk::Text* text;
public:
  AppText();

  virtual void realize_impl();
};
   
/* Create a scrolled text area that displays a "message" */
AppText::AppText()
  : Gtk::Table(2,2,false)
{
    Gtk::Scrollbar *scrollbar;
   
    /* Put a text widget in the upper left hand corner. Note the use of
     * GTK_SHRINK in the y direction */
    text = manage( new Gtk::Text () );
    attach (*text, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND,
                      GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
    text->show ();
   
    /* Put a HScrollbar in the lower left hand corner */
    scrollbar = manage( new Gtk::HScrollbar (*(text->get_hadjustment())) );
    attach (*scrollbar, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
    scrollbar->show ();
   
    /* And a VScrollbar in the upper right */
    scrollbar = manage( new Gtk::VScrollbar (*(text->get_vadjustment())) );
    attach (*scrollbar, 1, 2, 0, 1, GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
    scrollbar->show ();
}

/* Add some text to our text widget - this is a callback that is invoked
when our window is realized. We could also force our window to be
realized with gtk_widget_realize, but it would have to be part of
a hierarchy first */

void AppText::realize_impl ()
{
    Gtk::Widget::realize_impl(); // always call this!
    text->freeze ();
    text->insert(Gtk::Text_Helpers::Context(),
    "From: pathfinder@nasa.gov\n"
    "To: mom@nasa.gov\n"
    "Subject: Made it!\n"
    "\n"
    "We just got in this morning. The weather has been\n"
    "great - clear but cold, and there are lots of fun sights.\n"
    "Sojourner says hi. See you soon.\n"
    " -Path\n");
   
    text->thaw ();
}

class AppWindow : Gtk::Window
{
public:
  AppWindow();
  ~AppWindow();

  /* It's a good idea to do this for all application windows. */
  gint delete_event_impl (GdkEventAny*)
  {
    Gtk::Main::quit();
    return 0;
  }

};

   
AppWindow::AppWindow()
    : Gtk::Window (GTK_WINDOW_TOPLEVEL)
{
    Gtk::Paned *vpaned;
    Gtk::Widget *list;
    Gtk::Widget *text;

    set_title ("Paned Windows");
    set_border_width (10);
    set_usize (450, 400);

    /* create a vpaned widget and add it to our toplevel window */
   
    vpaned = manage( new Gtk::VPaned () );
    add (*vpaned);
    vpaned->set_handle_size (10);
    vpaned->set_gutter_size (15);                       
   
    /* Now create the contents of the two halves of the window */
    list = manage( new AppMessages() );
    vpaned->add1 (*list);
   
    text = manage( new AppText() );
    vpaned->add2 (*text);

    show_all ();
}

AppWindow::~AppWindow() {}


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

  /* Create a new window */
  AppWindow app;

  /* Enter the event loop */
  Gtk::Main::run ();

  return(0);
}


Next Previous Contents