Xfce
Foundation Classes |
|||
« Main Page | Index | |||
WidgetsTable of Contents
OverviewA 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:
Life Cycle of a WidgetWidget 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(); 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(); 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:
Setting Widget AttributesAfter 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(); 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(); 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); 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); 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); Other Widget ConceptsThere are a few other widget concepts that you need to be aware of, including sensitivity, focus and widget states.SensitivityWidgets 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; 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. FocusWithin 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; 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. GrabsWidgets 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);
); 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 StatesWidgets 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:
Gtk::StateType
get_state() const;
Writing Your Own WidgetsAlthough 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.
|