Xfce Foundation Classes
 « Main Page | Index

Widgets

Table of Contents

  1. Overview
  2. Life Cycle of  a Widget
  3. Setting Widget Attributes
  4. Other Widget Concepts
  5. Writing Your Own Widgets

Overview

A widget is the on-screen representation of a graphical user interface element that can be manipulated by the user. Windows, menus, buttons and scrollbars are all examples of widgets. A widget type is implemented as a class that defines the functionality and attributes of a widget. Widgets are arranged into a hierarchy of classes. Each widget in the hierarchy inherits the attributes of its base widget class and adds some new functionality. For example, Gtk::Dialog is derived from Gtk::Window so it is a window, but it comes prepacked with a vertical box, a horizontal button box and a horizontal separator.

There are several ways to classify widgets:
  • Containers are widgets that store other widgets inside, such as boxes and tables. Containers can be subdivided into those that add functionality to a single child (such as Button, Frame, or EventBox), and those that manage layout for multiple widgets (such as Box or Table).
  • Composite widgets are containers that come prepacked with useful child widgets. For example, the FileChooserDialog is a subclass of Gtk::Window that contains a combobox, several dialog buttons and a TreeView widget to display a list of files. Composite widgets are easy to write and are a convenient way to reuse code in an application.
  • Non-container widgets can be actual controls (buttons or scroll bars), information displays (labels), or decorative elements (separators). Widgets that need to receive events or draw their own background have an associated Gdk::Window. Widgets that have no Gdk::Window draw on their parent container and are called 'no window' widgets.
Gtk::Widget is the base class for all widgets in XFC. Widgets are very easy to use. You can create an instance of a standard widget and add it to a container, you can derive your own widget class from a standard widget and add some new functionality, you can write a composite widget, or you can create your own custom widget class from scratch. Widgets provide public methods that can be called to set widget-specific attributes such as widget size or position. When a user interacts with a widget, such as pulling down a menu and making a selection, clicking a button, or moving a scrollbar the widget emits a signal. An application needs to respond to these signal emissions by calling a callback slot that you supply. A callback slot is a C++ function object (functor) that calls the class member or static function that you want attached to a user action.

Life Cycle of a Widget

Widget resource and memory management is mostly automatic in XFC. However, there are a couple of 'gotchas' to keep in mind if you're doing more complicated things. Internally, a reference count is maintained for all widgets (actually, all G::Objects). Widgets begin their life with an initial reference count of 1. At this stage the widget is said to be 'floating' and is flagged as such. Removing a widget's initial reference is called 'sinking' the floating object and would destroy the widget if the floating reference was the only one.

Containers first reference and then sink any floating widgets that are added to them. By sinking a widget, a container 'takes ownership' of it for resource management purposes. Thus, the reference count of the widget remains 1, but the object is no longer flagged as floating. When a widget is removed from a container, or the container is destroyed, the reference count is decremented to 0. When an object's reference count reaches 0, it is destroyed.

In practice, this means that you only have to destroy top level widgets; any widgets that are inside a container will be destroyed along with the container. There's a danger here, however. Sometimes you want to remove a widget from a container; perhaps some element of your interface is optional or only appears under certain circumstances. When you remove the widget (using Gtk::Container::remove()), it will be unreferenced, its reference count will drop to 0, and it will be destroyed. To avoid this situation, you should add a reference to the widget before you remove it.

Use the following inherited G::Object methods to manipulate a widget's reference count:

virtual void ref();

virtual void unref();

The ref() and unref() methods respectively increment and decrement the reference count of a widget. They are declared virtual for XFC's internal use only and are not meant to be overridden. The reason ref() and unref() are declared virtual is so XFC can handle floating references automatically, which leads to the next point. The first call to ref() will automatically reference and then sink a widget if it has been flagged as floating, so there is no sink function.

To safely remove a widget from a container, you might do this:

widget->ref();
container->remove(*widget);

The widget now has one reference, held by your code. At some point you will need to release the reference, destroying the widget. It would make sense to do so after re-adding the widget to some other container. Removing widgets from containers is uncommon; in general it's faster to simply hide the widget with Gtk::Widget::hide(), then show it again with Gtk::Widget::show() some later time.

A widget can be destroyed at any time by calling its inherited dispose method:

widget->dispose();

Destroying a widget frees any associated memory and resources. If the widget is inside a container, it is automatically removed from the container before it's destroyed.

It is important to remember these few simple rules:
  • You must destroy any top level widgets when you are done with them, but child widgets are destroyed automatically.
  • If you want to remove a widget from a container without destroying it, you must first add a reference to the widget.
  • If you add a reference to a widget, you are responsible for unreferencing the widget again when you're done with it.

Setting Widget Attributes

After creating a widget you will need to set one or more attributes so it behaves as expected. The universal attribute you need to set  is the widget's visibility flag. This is done by calling one of the following Gtk::Widget show methods:

void show();

void show_all();

Any widget that isn't shown will not appear on the screen. If you want to show all the widgets in a container, it's easier to call show_all() rather individually calling show() for each widget. Remember, you have to show the containers containing a widget, in addition to the widget itself, before it will appear on screen.

The hide methods reverse the effects of the show methods causing the widget to be hidden (invisible to the user):

void hide();

void hide_all();

The show() and hide() methods are also signals and so have corresponding proxy signal functions and virtual signal handlers. You seldom, if ever, need to connect to or override these signals.

For widgets that can be 'activated' (such as buttons and menu items) the next method activates them:

bool activate();

Activation is what happens when you press 'Enter' on a widget during key navigation; clicking a button, selecting a menu item, etc. If the widget can't be activated, the method returns false.

Rather than move a widget from one container to another yourself you can call the following method:

void reparent(Gtk::Widget& new_parent);

which reparents the widget handling the reference counting issues to avoid destroying the widget. The 'new_parent' argument is the new container for the widget.

When you want a widget to have the keyboard focus call this next method.

void grab_focus();

The widget must be a focusable widget, such as a Gtk::Entry; something like Gtk::Frame won't work. (More precisely, it must have the Gtk::CAN_FOCUS flag set.)

Widgets can be named, which allows you to refer to them from a gtkrc file. You can apply a style to widgets with a particular name in the gtkrc file (see the GTK+ documentation for more information). Call the following methods to set and get a widget name:

void set_name(const String& name);

String get_name() const;

You can set the minimum size of a widget; that is, the widget's 'size request':

void set_size_request(int width, int height);

The 'width' and 'height' are the width and height the widget should request, in pixels, or -1 to unset, in which case the natural size request will be used instead. The size request of a widget is the smallest size a widget can accept while still functioning well and drawing itself correctly. In some strange cases a widget may be allocated less than its requested size, and in many cases a widget may be allocated more space than it requested. Passing (0,0) to this method means 'as small as possible'.

In most cases, Gtk::Window::set_default_size() is a better choice for top level windows than this method; setting the default size will still allow users to shrink the window. Setting the size request will force them to leave the window at least as large as the size request.

Note the inherent danger of setting any fixed size; themes, translations into other languages, different fonts, and user action can all change the appropriate size for a given widget. So, it's basically impossible to hard code a size that will always be correct.

You can also get the size request for a widget that was explicitly set using set_size_request().

void get_size_request(int *width, int *height) const;

A return value of -1 for width or height indicates that that dimension has not been set explicitly and the natural requisition of the widget is being being used.

Commonly you will want to alter one or more widget style attributes. You should not call this method unless you know what you are doing:

void set_style(Gtk::Style& style);

It interacts badly with themes, because themes work by replacing the Gtk::Style. Instead, you should use one of the Gtk::Widget 'modify style' methods:

void modify_style(Gtk::RcStyle& style);

void modify_fg(StateType state, const Gdk::Color* color);

void modify_bg(StateType state, const Gdk::Color *color);

void modify_text(StateType state, const Gdk::Color *color);

void modify_base(StateType state, const Gdk::Color *color);

void modify_font(const Pango::FontDescription *font_desc);


The first method, modify_style(), allows you to set multiple attributes at one time by specifying a new RcStyle. Modifications made using this method take precedence over style values set via an RC file. RcStyle  is designed so each field can either be set or unset, so it is possible, using this method, to modify some style values and leave the others unchanged. Modifications made take precedence over style values set via an RC file, however, they will be overridden if a style is explicitly set on the widget using set_style(). Modifications made are not cumulative with previous calls to modify_style() or with such functions as modify_fg(). If you wish to retain previous values, you must first call get_modifier_style(), make your modifications to the returned style, then call modify_style() with that style. On the other hand, if you first call modify_style(), subsequent calls to such methods as modify_fg() will have a cumulative effect with the initial modifications.

The other modify methods are convenience methods that let you change one style attribute. modify_fg() and modify_bg() set the foreground and background colors respectively for a widget in a particular state. modify_text() and modify_base() set the text and base colors respectively for a widget in a particular state. modify_font() sets the font to use for the widget.

To modify the font a label widget should use, do something like this:

Pango::FontDescription font_desc(font_name);
label->modify_font(&font_desc);

Other Widget Concepts

There are a few other widget concepts that you need to be aware of, including sensitivity, focus and widget states.

Sensitivity

Widgets can be sensitive or insensitive. Insensitive widgets are grayed out and do not respond to user input. Insensitive widgets are known as inactive, disabled, or ghosted in some other toolkits. You can call the following method to change a widget's sensitivity:

void set_sensitive(bool sensitive);

If the 'sensitive' argument is true, the user can interact with the widget, otherwise the widget is insensitive. By default GTK+ sets the sensitivity to true. A widget is only 'really' sensitive if all its parents are sensitive. The sensitivity of a widget can be tested with the following three functions:

bool is_sensitive() const;

bool sensitive() const;

bool parent_sensitive() const;

sensitive() returns true if the widget is sensitive and parent_sensitive() returns true if the widget's parent is sensitive. is_sensitive() returns true only if both the widget and all its parents are sensitive. The sensitivity of the widget itself only matters if the widget's parent is sensitive. For example, you can set the sensitivity of an entire container full of widgets just by setting the sensitivity of the container.

Focus

Within each top-level window, only one widget at a time can have the keyboard focus. Any key events received by the top-level window are forwarded to the focused widget. This is important because typing something on the keyboard should have only one effect - changing only one text entry field, for example.

Most widgets will give some visual indication that they have the current focus. Using the default GTK+ theme, the focused widget is typically surrounded by a thin black frame. The user can move the focus between widgets, using the arrow keys or the Tab key. Focus can also move to a widget if the user clicks it.

The following two functions test whether a widget can have, or has the focus:

bool can_focus() const;

bool has_focus() const;


can_focus() returns true if a widget is able to handle focus grabs and has_focus() returns true if a widget has grabbed the focus, and no other widget has done so more recently. The concept of focus is important for keyboard navigation. For example, pressing Enter or the space bar 'activates' many widgets if they have the focus; you can move between buttons with the Tab key, and press one with Space, for example.

bool is_focus() const;

The is_focus() function returns true if the widget is the focus widget within its top-level window.

void grab_focus();

Calling grab_focus() causes the widget to have the keyboard focus for the window it's inside. The widget must be a focusable widget, such as an Entry; something like Frame won't work. More precisely, can_focus() must return true.

Grabs

Widgets can grab the pointer and keyboard away from other widgets. This essentially means that the widget becomes 'modal': input goes only to that widget, and the focus can't be changed to another widget. A typical reason to grab input is to create a modal dialog; if a window has the grab, interaction with other windows is blocked. The widget grab methods are declared in the Main namespace.

void Main::grab_add(Gtk::Widget& widget);

After calling grab_add() to make 'widget' the current grab widget, all mouse and keyboard interaction with other widgets in the same application is blocked. To remove the grab from the current grab widget call:

void Main::grab_remove(Gtk::Widget& widget);

You have to pair calls to grab_add() and grab_remove(). To determine if a widget currently has the grab call this next function:

Gtk::Widget* Main::grab_get_current();

If no widget has the grab, grab_get_current() returns null. These grabs are a GTK+ concept and only grab events away from other widgets in the same application. There is another, GDK-level grab; a GDK keyboard or pointer grab occurs on an X-server-wide basis, with other applications unable to receive keyboard or mouse events.

To perform a GDK keyboard or pointer grab call the following Gtk::Widget methods:

Gdk::GrabStatus keyboard_grab(bool owner_events, unsigned int time);

Gdk::GrabStatus pointer_grab(Gdk::EventMaskField event_mask, Widget *confine_to, Gdk::Cursor *cursor, bool owner_events, unsigned int time
);

A call to either of these functions overrides any previous keyboard or pointer grab by this client. Most of the arguments in these two methods have default values, so they're easy to use. The widget's GDK window will own the grab until there is a corresponding call to keyboard_ungrab() or pointer_ungrab(), or the grab widget becomes unviewable. Like the GTK+ grab functions, keyboard_grab() must be paired with a call to keyboard_ungrab(), and pointer_grab must be paired with a call to pointer_ungrab().

void Gdk::keyboard_ungrab(unsigned int time = GDK_CURRENT_TIME);

void Gdk::pointer_ungrab(unsigned int time = GDK_CURRENT_TIME);

These two functions ungrab for the default display only. To ungrab for a specific display, there are two equivalent ungrab methods declared in Gdk::Display.

Widget States

Widgets have states which determine their appearance. The exact meaning and visual representation of a given state depends on the particular widget and the current theme. A widget's state can be one of the following values from the Gtk::StateType enum:
  • STATE_NORMAL - just like it sounds, the state during normal operation.
  • STATE_ACTIVE - a button is currently pressed in, or a check box is currently checked, for example.
  • STATE_PRELIGHT - the mouse is over the widget (and typically clicking would have some effect). Buttons "highlight" when you move over them, for example.
  • STATE_SELECTED - the widget is in a list or other set of alternatives, and is the currently selected option.
  • STATE_INSENSITIVE - the widget is inactive, or unresponsive and will not respond to user input.
You can retrieve the current state of a widget by calling the following function:

Gtk::StateType get_state() const;

Writing Your Own Widgets

Although XFC comes with many types of widgets that should cover your most basic needs, there will come a time when you need to create your own new widget type. Writing a new widget in XFC is a lot easier that in GTK+. That's because C can only try to be an object orientated language; C++ is inherently object orientated. How many times have you had to rewrite C functions for a new widget. In C++ you write it once and use it over and over again. But it's not all that simple. A new widget written in XFC can only be used in an XFC application. If you want a new widget that can be used by other language bindings then you need to write the GTK+ widget in C.
 
Since XFC uses widget inheritance extensively, if there is already a widget that is close to what you want, it is often possible to write a useful new widget type in just a few lines of code. This is often the case when writing a new compostie widget. Writing a new widget from scratch is somewhat harder because it requires more code to implement. If you are unfamiliar with writing your own widgets there are two examples you can work through. The Tictactoe example shows you how to write a new composite widget and the Dial example shows you how to write a new widget from scratch. Both examples are well documented and are discussed in detail.


Copyright © 2004-2005 The XFC Development Team Top
XFC 4.4