mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-25 18:19:09 +02:00
592 lines
27 KiB
TeX
592 lines
27 KiB
TeX
\documentclass[10pt]{article}
|
|
\usepackage{a4}
|
|
\usepackage{epsfig}
|
|
\usepackage{listings}
|
|
\lstset{language=Delphi}%
|
|
\lstset{basicstyle=\sffamily\small}%
|
|
\lstset{commentstyle=\itshape}%
|
|
\lstset{keywordstyle=\bfseries}%
|
|
\lstset{blankstring=true}%
|
|
\newif\ifpdf
|
|
\ifx\pdfoutput\undefined
|
|
\pdffalse
|
|
\else
|
|
\pdfoutput=1
|
|
\pdftrue
|
|
\fi
|
|
\begin{document}
|
|
\title{Programming GTK in Free Pascal}
|
|
\author{Florian Kl\"ampfl\\and\\Micha\"el Van Canneyt}
|
|
\date{July 2000}
|
|
\maketitle
|
|
\section{Introduction}
|
|
The GTK library is a popular widget library for the X-Windows system.
|
|
It is used as the basis for the GIMP graphical manipulation program
|
|
and for the GNOME application framework. With its ports to Microsoft
|
|
Windows and BeOS, it allows to program a graphical interface for any
|
|
application in a platform independent way.
|
|
|
|
GTK is implemented in C, but it is possible to access its functionality from
|
|
Free Pascal. For this, its headers have been translated to Pascal,
|
|
so a program written in Free Pascal can make use of the functionality
|
|
offered by GTK and its companion libraries GDK and GLIB. In fact, there is
|
|
an open source project (Lazarus) which makes use of GTK in order to build
|
|
an open-source alternative to the visual classes offered by Inprise's
|
|
Delphi.
|
|
|
|
This article intends to present an introduction to programming GTK in
|
|
Free Pascal. It by no means covers all of the functionality that GTK
|
|
offers, as this would probably require a complete manual.
|
|
|
|
The first section gives some general considerations on the GTK toolkit.
|
|
|
|
\section{GTK is a C library}
|
|
Since GTK is an external library, some import units describing the calls in
|
|
the libraries are needed. Three libraries make up the GTK programming kit:
|
|
\begin{description}
|
|
\item[glib] this library contains some general programming tools, and
|
|
defines platform independent types, which are used throughout the other
|
|
libraries. To use this library, it is sufficient to include the
|
|
\lstinline|glib| unit in your \lstinline|uses| clause.
|
|
\item[gdk] encapsulates the Windowing system (X or Windows) underlying GTK.
|
|
It contains routines to draw on the screen, and react to various mouse or
|
|
keyboard events. To use these
|
|
routines, the \lstinline|gdk| unit must be included in the \lstinline|uses|
|
|
clause of a unit or program.
|
|
\item[gtk] contains the widget library. This is a series of controls such
|
|
as edit boxes, drop-down lists and many more, which are organised in an OOP
|
|
structure. Since the library is written in C, there is no programming
|
|
language support for this structure.
|
|
|
|
All definitions of the gtk library are contained in the gtk unit, which
|
|
must be included in the \lstinline|uses| clause of any program or unit that needs their
|
|
functionality.
|
|
\end{description}
|
|
|
|
The GTK toolkit was programmed in C. This has some consequences for the
|
|
Pascal interface, since some C constructs do not port easily to Pascal.
|
|
When using the Pascal translation of the C headers, the following must be
|
|
kept in mind:
|
|
|
|
\begin{enumerate}
|
|
\item Reserved words: Pascal reserved words in types, record element names
|
|
etc. have been prepended with the word 'the'. For example \lstinline|label|
|
|
becomes \lstinline|thelabel|.
|
|
\item Functions and procedures have been kept with the same names.
|
|
\item Types have been prepended with T, that is, the C type
|
|
\lstinline|GtkWidget| has become \lstinline|TGtkWidget|.
|
|
\item Pointers to types have been defined as the type name, prepended with a
|
|
P. \lstinline|GtkWidget *| becomes \lstinline|PGtkWidget|.
|
|
\item Records with bit-size elements: C allows to store parts of a record in
|
|
individual bits; whereas in Pascal, the minimum size of an element in a
|
|
record is a byte. To accommodate this, functions were defined to retrieve
|
|
or set single bits from a record. The functions to retrieve a bit
|
|
have the name of the record field. The procedure to set a bit has
|
|
the name of the field prepended with 'set\_'.
|
|
For example
|
|
\begin{lstlisting}[language=c]{cpackedstruct}
|
|
struct SomeStruct
|
|
{
|
|
gchar *title;
|
|
guint visible : 1;
|
|
guint resizeable : 1;
|
|
};
|
|
\end{lstlisting}
|
|
translates to
|
|
\begin{lstlisting}{ppackedstruct}
|
|
TSomeStruct = record
|
|
title : Pgchar;
|
|
flag0 : word;
|
|
end;
|
|
function visible(var a: TGtkCListColumn): guint;
|
|
procedure set_visible(var a: TGtkCListColumn; __visible: guint);
|
|
function resizeable(var a: TGtkCListColumn): guint;
|
|
procedure set_resizeable(var a: TGtkCListColumn;__resizeable: guint);
|
|
\end{lstlisting}
|
|
\item Macros. Many C macros have not been translated. The typecasting
|
|
macros have been dropped, since they're useless under Pascal.
|
|
Macros to access record members have been translated, but they are to be
|
|
considered as {read-only}. So they can be used to retrieve a value, but
|
|
not to store one. e.g.
|
|
\begin{lstlisting}{macro}
|
|
function GTK_WIDGET_FLAGS(wid : pgtkwidget) : longint;
|
|
\end{lstlisting}
|
|
can be used to retrieve the widget flags, but not to set them. so things like
|
|
\begin{lstlisting}{invaliduseofmacro}
|
|
GTK_WIDGET_FLAGS(wid):=GTK_WIDGET_FLAGS(wid) and someflag;
|
|
\end{lstlisting}
|
|
will not work, since this is a function, and NOT a macro as in C.
|
|
\item Calling conventions: A C compiler uses another calling convention
|
|
than the Free Pascal compiler. Since many GTK functions need a callback,
|
|
these callback must use the C calling convention. This means that every
|
|
function that is called by GTK code, should have the \lstinline|cdecl|
|
|
modifier as a part of its declaration.
|
|
\end{enumerate}
|
|
|
|
Compiling a GTK application is no different than compiling any other Free
|
|
Pascal application. The only thing that needs to be done is to tell the free
|
|
Pascal compiler where the gtk, gdk and glib libraries are located on your
|
|
system. This can be done with the \verb|-Fl| command-line switch. For
|
|
example, supposing the gtk library is located in \verb|/usr/X11/lib|, the
|
|
following command-line could be used to compile your application:
|
|
\begin{verbatim}
|
|
ppc386 -Fl/usr/X11/lib mygtkapp.pp
|
|
\end{verbatim}
|
|
This example supposes that the gtk unit is be in your unit search path. If
|
|
it is not, you can add it with the \verb|-Fu| switch.
|
|
|
|
\section{The bricks of a GTK application}
|
|
The building-blocks of a a GTK application are the {\em widgets}.
|
|
Widgets are the equivalent of Delphi's controls. And although GTK
|
|
is not an object oriented library, the library defines a record
|
|
\lstinline|TGtkWidget| which contains all settings common to all
|
|
widgets; all widgets start with this record, and add their own
|
|
specific data to it. This creates a tree-like structure with all
|
|
the widgets present in the GTK library, to which your own widgets
|
|
can be added.
|
|
|
|
All functions that create a particular widget return a pointer
|
|
to a \lstinline|TGtkWidget| record. It is not recommended to
|
|
manipulate the contents of the widget record directly; GTK offers
|
|
many functions to manipulate the members of the record, e.g.
|
|
\lstinline|gtk_widget_set_parent| or \lstinline|gtk_widget_get_name|.
|
|
To this set of functions, each new widget adds a few functions that are
|
|
specific to this particular widget.
|
|
|
|
Each widget has a specific function and a specific look; there are many
|
|
widgets to choose from. A complete list of widgets is outside the scope of
|
|
this article; the GTK reference manual offers an overview of available
|
|
widgets. In general it can be said that most widgets one would expect
|
|
are present in the GTK library: Edit fields, buttons, check-boxes, various
|
|
lists, menus, combo-boxes, tree views, and some pre-defined dialogs.
|
|
|
|
Any of these widgets is created with a \lstinline|gtk_WIDGET NAME_new| call.
|
|
This call can accept arguments; The number and type of arguments depend
|
|
on the widget.
|
|
For example, to create a button that displays a text, the call is defined
|
|
as follows:
|
|
\begin{lstlisting}{}
|
|
gtk_button_new_with_label(ALAbel : PChar)
|
|
\end{lstlisting}
|
|
All widgets can be destroyed with the \lstinline|gtk_widget_destroy| call,
|
|
irrespective of their type.
|
|
|
|
\section{Showing things on the screen}
|
|
To show things on the screen, it is necessary to create a window. A window
|
|
is created with the the \lstinline|gtk_window_new| call. This call accepts
|
|
as an argument the type of window to be created.
|
|
|
|
Creating a window creates it's structure in memory, but doesn't show it on
|
|
screen. To show this window on the screen,a call to the
|
|
\lstinline|gtk_widget_show| function is needed, as can been seen in
|
|
example 1.
|
|
\lstinputlisting{gtkex/ex1.pp}
|
|
If the window contains widgets, the \lstinline|gtk_widget_show| function
|
|
must be called for each widget.
|
|
|
|
Looking at example 1, one notices 2 special calls: \lstinline|gtk_init| and
|
|
\lstinline|gtk_main|. These calls should be present in any program that uses
|
|
the GTK library.
|
|
|
|
The first call initialises the GTK library. Among other things, it reads
|
|
the command-line to see e.g. which display should be used.
|
|
|
|
The second call is the heart of the GTK widget library: It starts the
|
|
message loop of GTK. This call will not return, unless somewhere else
|
|
in the program \lstinline|gtk_main_quit| is called. As long as the call
|
|
doesn't return, GTK will wait for events such as mouse clicks, key-presses
|
|
and so on. It will handle these events, but it will not notify you of any
|
|
of these events except if you specifically ask for it.
|
|
|
|
A window by itself is of course not very interesting. To make it more
|
|
interesting, some elements should be added.
|
|
|
|
Adding a widget to a parent is done with the \lstinline|gtk_container_add|
|
|
call. This call places a widget in a container. A container is a widget
|
|
which can contain other widgets; not all widgets are containers, however.
|
|
|
|
Example 2 shows how to add a widget (a button) to a container (the window
|
|
in this case). It also shows that the container has some specific
|
|
properties, which can be manipulated as well (in this case, the border
|
|
width). Since not each widget is a container, the window pointer must be
|
|
typecasted to \lstinline|GTK_CONTAINER| in order to be accepted by the
|
|
container handling calls.
|
|
|
|
\lstinputlisting{gtkex/ex2.pp}
|
|
|
|
Adding more than 1 widget to a container is not trivial in GTK. The reason
|
|
for this is that GTK has not been designed to set widgets at a specific
|
|
location in their parent widget. Instead, GTK asks that you 'pack' your
|
|
objects in their parent widget. This means that if the parent widget is
|
|
resized, it's child widgets are resized as well, depending on the packing
|
|
options that were set.
|
|
|
|
One of the reasons that the GTK library was set up this way, is that the
|
|
size of a widget is not well-defined. For instance, the size of a button
|
|
depends on whether it is the default widget of the window or not. Given
|
|
that this is so, the placement of such a button is not well-defined either.
|
|
|
|
The most common ways of packing widgets in a parent widget are the
|
|
following:
|
|
\begin{enumerate}
|
|
\item using a vertical box.
|
|
\item using a horizontal box.
|
|
\item using a table.
|
|
\end{enumerate}
|
|
We'll discuss these ways in the subsequent. There are other ways, but these
|
|
are probably the most important ones.
|
|
|
|
\subsection{Using boxes}
|
|
|
|
A horizontal or vertical box can be used to contain a row or column of
|
|
widgets. Various options can be set to modify the spacing between the
|
|
widgets, the alignment of the widgets in the box, or the behaviour of
|
|
the box when the user resizes the parent widget. Boxes work only in
|
|
one direction. The widgets inside a horizontal box always have the height of
|
|
the box, and widgets in a vertical box always have the width of the vertical
|
|
box.
|
|
|
|
You can create a horizontal box with the \lstinline|gtk_hbox_new| call.
|
|
It accepts 2 arguments: The first one is a boolean. It tells GTK whether the
|
|
children should have the same space in the box. The second one is an
|
|
integer, which tells GTK how much space to leave between the widgets in the
|
|
box. Likewise, a vertical box can be created with the
|
|
\lstinline|gtk_vbox_new| call. This call accepts the same arguments as the
|
|
first box.
|
|
|
|
Adding widgets to a box happens with the \lstinline|gtk_box_pack_start| or
|
|
\lstinline|gtk_box_pack_end| calls. The former adds a widget at the start
|
|
of the box, the latter adds a widget at the end of the box. Both functions
|
|
accept the same arguments:
|
|
\begin{lstlisting}{boxarguments}
|
|
(Box : PGtkBox; Widget: PGtkWidget;
|
|
expand gboolean; fill : gboolean;padding : guint);
|
|
\end{lstlisting}
|
|
The \lstinline|expand| argument tells the box whether it should take the
|
|
size of it's parent widget, or whether it should resize itself so that it is
|
|
just large enough to fit the widgets. The latter allows to justify the
|
|
widgets in the box (but only if the box is {\em not} homogeneous.
|
|
If the box should keep the size of it's parent, then the \lstinline|fill|
|
|
argument decides what is done with the extra space available.
|
|
|
|
If \lstinline|fill| is \lstinline|True| then the extra space is
|
|
divided over the widgets. If \lstinline|fill| is \lstinline|False| then the
|
|
extra space is put in between the widgets.
|
|
|
|
The \lstinline|padding| adding tells the box to add extra space for this
|
|
particular widget.
|
|
|
|
The following program shows the use of a box:
|
|
\lstinputlisting{gtkex/ex3.pp}
|
|
What the program does is the following: It creates a window, which it splits
|
|
up in two halves by means of the \lstinline|totalbox| widget. This is a
|
|
vertical box, which will contain two other boxes: a vertical box and a
|
|
horizontal box. Each of these two boxes is filled with buttons.
|
|
The behaviour of the boxes can be seen when the window is resized.
|
|
|
|
The effect of the various arguments to the pack calls can be seen by
|
|
changing the arguments and recompiling the example.
|
|
|
|
\subsection{Using tables}
|
|
A table is used to set widgets in a grid inside the parent widget. It acts
|
|
like a grid with cells, in which you can 'hang' your widgets. If the user
|
|
resizes the parent widget, then the size of the grid cells changes, and
|
|
the widgets will change their location and size, based on the size of the
|
|
new grid cells.
|
|
|
|
To create a table to manage a window's layout, the \lstinline|gtk_table_new|
|
|
call is used. It accepts 3 arguments: the number of rows, the number of
|
|
columns and a boolean which tells GTK whether the cells should always have
|
|
the same size or not.
|
|
|
|
To add a widget to a table, the \lstinline|gtk_table_attach| call is used.
|
|
It is declared as
|
|
\begin{lstlisting}{}
|
|
gtk_table_attach(Table: PGtkTable;Widget: PGtkWidget;
|
|
Left, right, top, bottom : guint;
|
|
Xoptions,Yoptions : TGtkAttachOptions;
|
|
Xpadding,Ypadding : Guint);
|
|
\end{lstlisting}
|
|
The first two options of this call are self-explanatory. The next four
|
|
options, however, need some explanation. Contrary to what the name 'table'
|
|
suggests, these do {\em not} specify the coordinates of cells; instead, they
|
|
specify the grid lines that delimit the cells.
|
|
|
|
\begin{figure}
|
|
\caption{Placing widgets in a table.\label{fig:table}}
|
|
\begin{center}
|
|
\ifpdf
|
|
\epsfig{file=table.pdf}
|
|
\else
|
|
\epsfig{file=table.eps}
|
|
\fi
|
|
\end{center}
|
|
\end{figure}
|
|
|
|
Figure \ref{fig:table} represents a table with 5 rows and 5 columns, with
|
|
cells of the same size. The call to create this table could be:
|
|
\begin{lstlisting}{}
|
|
maintable:=gtk_table_new(5,5,TRUE);
|
|
\end{lstlisting}
|
|
To hang a widget in this table, so it starts in cell (2,1) and ends in cell
|
|
(3,2), where the cells are counted starting with 0, it is necessary to tell
|
|
GTK that the widget starts at horizontal grid line 2, and ends at horizontal
|
|
grid line 4. Vertically, it starts at grid line 1, and ends at grid line 3.
|
|
This means that the following call would place the widget at its correct
|
|
location:
|
|
\begin{lstlisting}{}
|
|
gtk_table_attach(maintable,mybutton,
|
|
2,4,1,3,
|
|
GTK_EXPAND OR GTK_FILL,GTK_EXPAND OR GTK_FILL,
|
|
0,0);
|
|
\end{lstlisting}
|
|
GTK delivers a shorter form of this call:
|
|
\begin{lstlisting}{}
|
|
gtk_table_attach_defaults(maintable,mybutton,2,4,1,3);
|
|
\end{lstlisting}
|
|
The parameter \lstinline|GTK_EXPAND or GTK_FILL| tells GTK that the widget
|
|
should always take up the full space assigned to it.
|
|
|
|
The following example program illustrates the use of a table in a gtk
|
|
application:
|
|
\lstinputlisting{gtkex/ex4.pp}
|
|
The example creates a table with 6 rows and 6 columns. It places 3 buttons,
|
|
each at a different location in the table, with different sizes. The first
|
|
button has a width and height of 1 cell and is located at cell (1,1). The
|
|
second has a width and height of two cells, and is located at cell (3,1).
|
|
The last button is 4 cells wide and has a height of 1 cell, and is located
|
|
at cell (1,4). When the window is resized, the cells are resized as well,
|
|
and the buttons follow the size of the cells.
|
|
|
|
{\em Remark:} because the table has homogeneous cells, the minimum width
|
|
and height of the cells is determined by the first button (in this case).
|
|
Since all cells must have the same size, this means that the minimum size
|
|
of the window is 6 times the size of the first button (plus a border).
|
|
|
|
\section{Reacting to user input}
|
|
So far, the example programs did not react to button clicks or any other user
|
|
action other than closing the window. To make a window respond to user
|
|
actions, it is necessary to install signal callbacks or event handlers.
|
|
|
|
The difference between signals and events is that signals come from the GTK
|
|
toolkit. Events come from the underlying window system (X or Windows).
|
|
For example, 'button\_pressed' is an event that is generated by the window
|
|
system when the user clicks with his mouse. It is possible to react to
|
|
this event.
|
|
|
|
On the other hand, a button widget defines a signal 'clicked'.
|
|
The 'clicked' event will occur when the user clicks on the button.
|
|
So, many signals that are defined by GTK widgets are just a translation
|
|
of events to something specific for that widget.
|
|
|
|
Since calls to connect to a signal or to an event are the same, in what
|
|
follows the discussion will be restricted to signals, but all that is
|
|
said is also true for events.
|
|
|
|
GTK has essentially 2 ways to install signal callbacks. The only difference
|
|
between these calls is the arguments that the callback function accepts.
|
|
|
|
The first way to install a callback is using the
|
|
\lstinline|gtk_signal_connect| function. This function is declared as
|
|
follows:
|
|
\begin{lstlisting}{}
|
|
TGtkSignalFunc = procedure ;cdecl;
|
|
Function gtk_signal_connect(TheObject:PGtkObject;Name:Pgchar;
|
|
Func:TGtkSignalFunc;Data:gpointer):guint;cdecl;
|
|
\end{lstlisting}
|
|
|
|
The first argument of this call (\lstinline|TheObject|) is the object
|
|
(usually a widget) to which you want to assign an event handler. The second
|
|
parameter, \lstinline|Name|, is the event you wish to catch with this
|
|
callback (an example could be 'clicked' for a button). The third argument
|
|
(\lstinline|Func|) is the function that should be called when the event occurs.
|
|
The \lstinline|Data| argument is a pointer to arbitrary data. This pointer
|
|
will be passed on to the callback function \lstinline|func| when the event
|
|
occurs.
|
|
|
|
The \lstinline|gtk_signal_connect| function returns an integer. This integer
|
|
is the number of the callback for this event. It is possible to attach more
|
|
than one callback to an event in GTK. When the event occurs, the callbacks
|
|
will be executed in the order that they have been attached to the widget.
|
|
|
|
The declaration of the \lstinline|TGtkSignalFunc| type requires that every
|
|
callback function that is passed to GTK must be typecast. Since GTK defines
|
|
only one function to set a signal handler, this is necessary, since
|
|
callbacks can have different forms. This mechanism is error-prone, since
|
|
in this manner it is possible to pass a function to GTK which has the wrong
|
|
number of arguments.
|
|
|
|
However, most callbacks must be of the form:
|
|
\begin{lstlisting}{}
|
|
Function (Widget : PGtkWidget; Data : GPointer) : guint;cdecl;
|
|
\end{lstlisting}
|
|
Such a callback function accepts 2 arguments: the first argument
|
|
(\lstinline|Widget|) is the widget which caused the event
|
|
(for example, the button which was clicked). The second argument is a
|
|
pointer to arbitrary data. This is the pointer that was passed as
|
|
\lstinline|Data| when the callback was installed.
|
|
|
|
Signals are identified by their name. The GTK reference guide contains a
|
|
complete list of supported signals.
|
|
|
|
The first example shows how a handler for the 'destroy' signal of the
|
|
window is installed. When the window-manager kills the window, this
|
|
signal is sent. The \lstinline|gtk_main_quit| instructs GTK that it
|
|
should stop processing X events and exit the \lstinline|gtk_main| call.
|
|
|
|
A second method to connect a callback to a signal is using the
|
|
\lstinline|gtk_signal_connect_object| call. This call is defined as
|
|
follows:
|
|
\begin{lstlisting}{}
|
|
Function gtk_signal_connect_object(theobject:PGtkObject;
|
|
name:Pgchar;
|
|
func:TGtkSignalFunc;
|
|
slot_object:PGtkObject):guint;cdecl
|
|
\end{lstlisting}
|
|
It is similar in function to the \lstinline|gtk_signal_connect| function,
|
|
only it doesn't allow to pass arbitrary data to the signal handler. Instead,
|
|
the handler must {\em always} be of the following form:
|
|
|
|
\begin{lstlisting}{}
|
|
Function (AnObject : PGtkObject);
|
|
\end{lstlisting}
|
|
The \lstinline|slot_object| pointer that was provided in the call to
|
|
\lstinline|gtk_signal_connect_object| will be passed as the
|
|
\lstinline|AnObject| argument to this function. Many GTK functions have the
|
|
above form; this makes it possible to attach a GTK internal function to a
|
|
signal.
|
|
|
|
To illustrate this, the second example is modified so that clicking the button
|
|
will also close the window:
|
|
\lstinputlisting{gtkex/ex5.pp}
|
|
In the example, the call to \lstinline|gtk_signal_connect_object| will
|
|
connect the 'clicked' signal of the button to the
|
|
\lstinline|gtk_widget_destroy| function of GTK, and passes the pointer to
|
|
the window widget to it. When the user clicks the button, the
|
|
\lstinline|gtk_widget_destroy| function will be called with as argument the
|
|
pointer of the window object. As a result, the window widget will be
|
|
destroyed, it's 'destroy' signal will be activated, and the
|
|
\lstinline|gtk_main_quit| function will be called through the program's
|
|
'destroy' handler for the window.
|
|
|
|
Since the signal handler connect call returns an integer by which it can
|
|
be identified, it is possible to manipulate or even remove the handler
|
|
once it has been installed.
|
|
|
|
For instance it is possible to temporarily disable a signal handler with the
|
|
\lstinline|gtk_signal_handler_block| call, and to enable it again with the
|
|
\lstinline|gtk_signal_handler_unblock| call. An example of how to do this
|
|
can be found in the following example:
|
|
\lstinputlisting{gtkex/ex6.pp}
|
|
There are other things that one can do with signal handlers, but a complete
|
|
discussion of all possibilities is outside the scope of this article.
|
|
|
|
Some widgets do not have their own window; i.e. they do not receive events
|
|
from the underlying windowing system. An example of such a widget is a
|
|
label. A label just draws it's text on it's parent widget, and nothing else.
|
|
|
|
To be able to respond to certain events, an event-box must be used, and the
|
|
window-less widget must be placed in it. An event-box can be created with
|
|
the \lstinline|gtk_event_box_new| call. This call accepts no arguments.
|
|
|
|
To this event-box, a window-less widget can be added. The event-bow will
|
|
then capture events for the window-less widget. The following example shows
|
|
how to use an event-box to detect when the mouse pointer is moved over a
|
|
label:
|
|
\lstinputlisting{gtkex/ex7.pp}
|
|
If the mouse pointer is moved over the first label, the text of the second
|
|
label is adapted accordingly. The example also shows the use of
|
|
\lstinline|gtk_widget_show_all|, which shows a widget and all widgets
|
|
contained in it.
|
|
|
|
\section{A touch of style}
|
|
The look of a GTK application is controlled through the use of styles. A
|
|
style controls the colors in which a widget is drawn, in various states.
|
|
As an example: a widget may be drawn differently depending on whether it
|
|
has focus or not. How to draw the widget in each of this states is described
|
|
in the style of the widget.
|
|
|
|
GTK recognizes the following states of a widget:
|
|
\begin{description}
|
|
\item[NORMAL] The normal state of a widget. No mouse over it.
|
|
\item[PRELIGHT] Is the state of a widget when the mouse is over it.
|
|
\item[ACTIVE] Is the state of a widget when it is pressed or
|
|
clicked.
|
|
\item[INSENSITIVE] if the widgets is disabled ('grayed').
|
|
\item[SELECTED] When the object is selected.
|
|
The GTK unit has a constant for each of these states; it is the above name
|
|
with \lstinline|GTK_STATE_| prefixed, so e.g. \lstinline|GTK_STATE_NORMAL|
|
|
for the normal state.
|
|
|
|
\end{description}
|
|
|
|
|
|
Each widget class has a default style in which it is drawn. If you wish to
|
|
change the way all these widgets look, you should change the default style
|
|
of this class. If you want to change the way one particular widget looks,
|
|
you should make a new style, and apply it to that particular widget. It is
|
|
possible to make a copy of an existing style and modify the copy before
|
|
applying it.
|
|
|
|
It is also possible to change the default style of widgets. Changing the
|
|
default style of widgets will have effect on all widgets created after the
|
|
new style was set. Widgets created before that will be unaffected.
|
|
|
|
The following example shows how to set the color of a label. It takes a
|
|
copy of the standard label style, and modifies it so the foreground color
|
|
becomes red. It then applies the modified style to the first label.
|
|
The second label is unaffected by this change
|
|
\lstinputlisting{gtkex/ex8.pp}
|
|
|
|
The last example shows how to change the color of a button when the mouse
|
|
moves over it.
|
|
\lstinputlisting{gtkex/ex9.pp}
|
|
|
|
\section{Carrying on}
|
|
In the previous sections, some basic concepts of GTK have been introduced.
|
|
However, GTK is a big toolkit and much more can be said about it. It is
|
|
outside of the scope of the current article to describe all Widgets in the
|
|
GTK library. The range of offered widgets is broad, and there probably is a
|
|
widget for each conceivable task. If there is a missing widget, it is always
|
|
possible to write a new widget.
|
|
|
|
In principle, therefore, GTK is suitable to write large applications, also
|
|
when writing in Free Pascal. However, the fact that it is written in C and
|
|
it's interface is C oriented, justifies the writing of a small Pascal Object
|
|
Oriented wrapper around it.
|
|
The following arguments show the need for such a wrapper:
|
|
\begin{enumerate}
|
|
\item C has no object-oriented language constructs. This makes it necessary
|
|
to do a lot of typecasts in GTK calls. Using Classes or Objects, this is no
|
|
longer necessary, and will improve code readability substantially.
|
|
\item C uses null-terminated strings. It can be useful to wrap calls that
|
|
need a null-terminated string into one that accepts a normal string as an
|
|
argument. Using ansistrings will make the conversion to null-terminated
|
|
strings easier.
|
|
\item The signal mechanism of GTK destroys the strong type checking of
|
|
Pascal. When compiling the statement
|
|
\begin{lstlisting}{}
|
|
Button.OnClick:=@MyForm.OnButtonClick
|
|
\end{lstlisting}
|
|
The compiler checks that the \lstinline|OnButtonClick| method can be
|
|
assigned to the \lstinline|OnClick| event. Under GTK, it is possible
|
|
to pass any function as a handler for a signal, as in
|
|
\begin{lstlisting}{}
|
|
gtk_signal_connect (PGTKOBJECT (window), 'destroy',
|
|
GTK_SIGNAL_FUNC (@destroy), NULL);
|
|
\end{lstlisting}
|
|
This can lead to errors if the \lstinline|destroy| procedure accepts a
|
|
different number of arguments, or other arguments than the ones that
|
|
GTK provides. Therefore it would be a good idea to implement methods that
|
|
would force type checking, e.g:
|
|
\begin{lstlisting}{}
|
|
Button.AddOnClick(@MyForm.OnButtonClick);
|
|
\end{lstlisting}
|
|
Such a statement would only compile if the \lstinline|OnButtonClick| would
|
|
be of the right type.
|
|
\end{enumerate}
|
|
Additional benefits of making such a wrapper would be simpler code, and
|
|
hence better readability. Both improve the maintainability of the code as well,
|
|
which are all important factors when writing a large application.
|
|
\end{document}
|