wesnoth/doc/design/gui2.tex
2010-04-30 14:21:02 +00:00

1043 lines
45 KiB
TeX

\documentclass[a4paper,notitlepage]{report}
\usepackage[english]{babel}
\usepackage{listings}
\lstset{
numbers = left
, showstringspaces = false
, tabsize = 4
, frame = single
, xrightmargin = -7em
, xleftmargin = -7em
}
\lstdefinestyle{C++}{
language = C++
, escapeinside = {/*@}{@*/}}
\lstdefinelanguage{WML}{}
\lstdefinestyle{WML}{
language = WML
, escapeinside = {\#*@}{@*\#}}
\usepackage{verbatim}
\usepackage {hyperref}
\author{M.~de Wever}
\title{Gui2 design document}
\begin{document}
\maketitle
\tableofcontents
\begin{abstract}
Late 2007 I started to rewrite the gui engine of Wesnoth, this project is called
the gui2 project. A lot of code and documentation has been written since. The
documentation is available at:
\begin{itemize}
\item \url{http://wiki.wesnoth.org/GUIToolkit} The general information
regarding the project and documentation regarding the WML used.
\item \url{http://devdocs.wesnoth.org} The general doxygen information.
\item \url{http://devdocs.wesnoth.org/pages.html} Contains some links to more
gui2 specific information.
\end{itemize}
This documentation describes a lot of the details and the wiki describes some of
the design from the WML side. Doxygen contains some information regarding the
design of the C++ side but it misses the overall design. This paper tries to
fill that gap, trying to explain more of the overall design and some of the
design choices.
\end{abstract}
\chapter{Introduction}
This paper describes the general design of the gui2 project. The paper is
divided in several chapters:
\begin{description}
\item[Chapter 1] Explains why yet another document is added instead of using the
doxygen and a short history of the gui2 project.
\item[Chapter 2] Goes into the general design and structure of the gui2 project.
\item[Chapter 3] Dives deeper into certain parts of the design of certain
classes and algorithms.
\end{description}
A omission in this paper is the simple story of how to add your own widgets in
the C++ code\footnote{The WML part is described in the wiki.}. I have a slightly
outdated paper on the subject, but decided not to add it; the paper needs
polishing and updates, but I've some design changes planned for the 1.9
development series. These changes will also change the way of adding new
widgets, so that paper needs to be rewritten and added after the rewrite is
complete.
\section{Why why why?}
Why is this document written in \LaTeX{} instead of doxygen? The reason is
simple; during the development of the iterator class I felt I had to write a
lot. Writing much documentation in doxygen feels awkward. A lot of time is spend
on formatting, the number of subsections is limited (too low in my opinion).
Also it's not possible to document why certain functions aren't implemented, a
solution would be to make the function private and declared only, but that feels
like an ugly hack.
Why is the document needed? In my opinion most manuals written in doxygen are
great as reference manuals but fail to explain how to do certain things with the
library and fail to explain why certain things are implemented in the way it is.
Writing a separate document separated from the code has the disadvantage of
getting earlier out of sync when an implementation details differs, but this can
be used as an advantage as well. Since it's hard to keep the details in sync,
it's a good reason not to dive into these details. This makes a natural
separation between which part should be in doxygen and which part in this
manual.
Why after the above, are some parts of gui2 documented in separate pages in
doxygen? This indeed seems a contradiction, but it's not. Remember the first
paragraph stating it's annoying to write this kind of documentation in doxygen?
These parts are where I realized it's annoying to do it in doxygen and haven't
been incorporated in this document\footnote{These parts are still mentioned in
this paper, but they refer to the doxygen documentation.}.
\section{History}
The gui2 project was started late in 2007 to fix several problems I had with the
current gui at that time. The problems I had were:
\begin{enumerate}
\item When using small resolutions the gui started to look bad, since widgets
simply left their container and were drawn outside it.
\item Most of the gui was hard-coded in the C++ code and thus not configurable
for normal users.
\item ThemeWML, the part that allows the user to configure the layout, is not
well understood by the current developers.
\end{enumerate}
This let to the following design goals:
\begin{enumerate}
\item The gui should look well at every possible resolution and automatically
fit well.
\item Everything, or at least as much as possible should be configurable by WML.
\item The project needed more documentation as the current ThemeWML, and explain
how to create your own guis.
\end{enumerate}
At the time of this writing the project is still work in progress\footnote{It
was never expected to be a short during project.} and will still take while to
finish. This initial goals were pretty clear, and still are. On the other hand
how to implement certain parts provided to be less clear.
The library is the first time I designed a gui toolkit and like with all larger
projects, you need to learn from mistakes; preferably of others, but your doomed
to make your own as well. The design process is an iterative one, causing some
parts of the design to be changed several times. At the time of
writing\footnote{Shortly before the 1.8 release.} a lot of areas feel stable,
but others are still planned to be rewritten. Mainly the handling of scrollbar
widgets feels awkward and hacky and are slated for a redesign.
Other parts of the gui are still in the planning stage and not implemented yet,
but the project moves along at a steady pace and more features will be added in
the future.
\paragraph{}
The history of this document also goes back a long time, it's mostly build from
various scraps of notes I have on my system. The plan was to change these notes
to doxygen format some day put it in the source. The problem was to find a nice
place in the source to do so.
This lead to the fact the notes just stayed on my system and the number of
notes accumulated. During travelling to work I started to design some things for
the 1.9 development series and for the iterator class I needed a new kind of
document. I decided that was the final straw and started to work on writing this
document while travelling.
Since you read this document it's uploaded in the Wesnoth source tree, this
doesn't mean the document is complete. In fact it more means the opposite, the
draft is uploaded and I can start to work on finishing the document by adding
the missing information.
\chapter{Overall design}
This chapter takes a deeper look into the overall design of the library. Before
we can explain how things work, we'll have a look at the features; both
implemented and planned for the future. Once we know what the gui should be able
to do I'll explain how these goals are achieved. The chapter ends with a short
explanation of the directory structure so you'll be able to find the parts of
the library.
\section{Features}
Wesnoth runs on a large amount of devices, with an even larger amount of display
resolutions, from $320\times 240$ on hand-held devices to $2560\times 1600$ on
large 30" screens. The gui should look appealing on all these devices. Therefore
there are three resolution groups:
\begin{description}
\item[hand-held devices] For these devices the code needs to be compiled with
\textbf{TINY\_GUI}. This forces all images to be scaled down by a factor two.
The typical resolutions on these devices are $320\times 240$ -- $640\times
480$.
\item[PC] For the usage on PC's Wesnoth offers the resolutions $800\times 600$
-- $2560\times 1600$\footnote{And larger once screens that size become
available.}.
\item[Netbooks] When the first netbooks were introduced the normal resolution
was $800\times 480$, which is slightly smaller as the minimum PC resolution.
Therefore a start option \textsc{--smallgui} was added making minimal
modifications to the layout\footnote{Actually this is gui1 only.}.
\end{description}
In order to facilitate this range of resolutions the gui2 code allows several
definitions of a window, tuned for a specific resolution. The reason is twofold;
first to make a difference between the hand-held devices and PCs, second allow
different views for different resolutions.
The main example of this feature\footnote{Not yet implemented, but one of the
main reasons to add this feature.} is the attack dialogue. The dialogue has a
button to show the damage calculation, which shows a new dialogue with the
calculation overview. From an UI point of view I consider that rather ugly and
rather have tabs to switch between the view. But when I have a larger screen the
dialogue only fills a small part and I need to switch between tabs to see the
info, in that case I rather have one dialogue without tabs, which directly shows
all information. When the user changes the resolution the dialogue should switch
between these two views, depending on the current resolution.
\paragraph{}
The gui should be able to adapt to the size actually needed, the current gui
uses fixed sizes at some places. This leads to problems that when the screen
resolution is reduced widgets end up outside the dialogue or get truncated. The
same for some translated texts, where the translation is much longer as the
English original. Gui2 solves this problem by dynamically determining the size
of a widget and adjust the layout until it fits. This has the disadvantage that
dialogues with a lot of dynamic content, resize at random times and changing the
layout of the dialogue slightly. Another disadvantage is that when a dialogue
doesn't fit Wesnoth terminates. This problem can be fixed by adding scrollbars
to every window. When the dialogue doesn't fit the scrollbars are used and the
dialogue fits again, might be a bit ugly but at least everything fits again.
\paragraph{}
It must be possible for WML designers to change the entire gui of Wesnoth with
their own version. For example Spacenoth\footnote{That project is dead, but
that doesn't matter for this example.} is Wesnoth is a space setting, so the
project might want to use a more fitting user interface.
Adding a new gui is a lot of work and can't be done in one fell swoop, so the
code needs to support a gradual conversion. Therefore when you select your own
theme\footnote{Support for this selection and the entire fall-back haven't been
added yet. Obviously I want to add support, but it remains low priority until
somebody really wants to add their own gui.} the engine first searches in the
new theme for items\footnote{A widget or window.}. When an item is not found it
uses that item from the default theme. Therefore it's mandatory that every item
is defined for the default gui, which the engine validates at
startup\footnote{That causes the problem that Wesnoth sometimes refused to start
when the source and data are out-of-sync.}.
\section{The big picture}
First we dive into the components that define the gui.
\subsection{Widget}
A widget is the basic user interface element, like a label, a button or a text
entry. Every widget has its own behaviour, which sometimes can be influenced by
certain settings, but the main behaviour is fixed per widget. Next to behaviour
a widget also has a visual representation so the user can see and interact with
the widget.
Before the widget is shown to the user it needs to be created first, the
creation happens in the window builder class. This class reads the definition of
a widget and fills in the blanks et voil\`a the widget is there. Let's go over
this part in more slow-motion.
\paragraph{The definition}
The definition determines how a widget looks and some basic properties, these
properties are the same for all instances of that widget. For example a button
has a minimum size so the decoration can be drawn. It's possible to make
multiple definitions of a button. These different definitions, can look
different have different minimum sizes and other properties.
These definitions are written in WML and a small loader class loads and
validates the definition. The definition is then added to the list of known
widgets of this type.
The definition of the widget depends on the resolution, this is for example used
in the button. It has a minimum size depending on the decoration used, in
\textbf{TINY\_GUI} mode the decoration is scaled down, so the minimum size can
also be reduced.
\paragraph{The builder}
The builder is started from the C++ code, started while building a window. This
window definition contains a list of widget to build with more instance specific
values. For example a label builder has the text to show to the user as
parameter.
These builder ``scripts'' are also written in WML and loaded by as small loader
class that does the validation and build the needed widget.
\paragraph{Widget}
The widget itself is written in a larger C++ class and it defines the behaviour
and provide various hooks to modify the properties of that widget. These hooks
are used by the builder, but can also be modified later by the engine.
Other hooks provide bindings to react to events. The bindings are now rather
static, but with the new event handling added late in the 1.8 release series
more things are possible. The plan is to enhance this part during the 1.9
release series and deprecate and remove the current interface.
Of course the question ``why wait until 1.9'' raises. The reason is simple it
was added late in the 1.8 cycle to fix certain issues with the MP lobby at that
time I had no time to convert the rest of the code, since I was working on the
MP lobby instead.
\subsection{Window}
A window can mean two things, the window widget and the window definition. In
this section the window definition is meant. Already discussed before, but a bit
more verbose this time.
The window definition defines a window and which widgets are placed in the
window. This window definition also depends on the resolution. This allows the
window to look different depending on the resolution. The changes can be small
or the window can look completely different.
\subsection{Dialogue}
A dialogue is a pure C++ thing. A dialogue shows a window, but is not a window.
So what's the difference\footnote{These are the definitions used in the gui2
code and not the definition of other window toolkits.}? A window is a dumb
combination of widgets created depending on the definitions in a WML file. After
the window is created it often needs extra content and react to certain events.
For example the language dialogue after building has an empty list of languages.
This is where the dialogue comes into play. The user asks for the language
selection dialogue. The code creates and shows a dialogue. The dialogue code
builds a window, then fills the language list with the available languages and
selects the current language and then shows it to the user.
In other dialogues the code also needs to wire in event handlers or build other
structures. It searches the wanted widget by id, in some cases it doesn't even
care what kind of widget is used. In other cases it needs to be of a certain
class or ``concept''. This allows a flexible design and let the user select the
kind of widget used in some cases.
So the dialogue is the sugar between the WML window and an interactive dialogue
shown in the game.
\section{Directory structure}
This section describes the directories available, instead of listing them in
alphabetic order I list them in a order that makes explaining them more natural.
\begin{description}
\item[src/gui] The general source code directory with all parts used for the gui.
\begin{description}
\item[dialogs] This directory contains all dialogues used in Wesnoth, in
general every file contains one dialogue, the name of the file being the
logical name of what the dialogue does. Some dialogues have a helper
dialogue, which is sometimes embedded in the same file.
\item[widgets] This directory contains all widgets used in the library. Every
file contains one widget. Also base classes or concepts of widgets are
stored in this directory. During the development it also accumulated some
helper files, which don't fit in the aforementioned descriptions, this lead
to the creation of the auxiliary directory. Some files haven't been moved to
this new directory yet.
\item[auxiliary] This directory contains items auxiliary classes. Some helper
parts are so large that they got their own subdirectory.
\begin{description}
\item[event] Contains all event handling code, the translation from SDL events
to the internal events and their dispatching.
\item[widget\_definition] Contains the code to serialize the WML to an internal
data structure, needed to define that kind of widget. The names of the files
match the widget names. (Most files are rather small, but I prefer small
single tasked files over huge files controlling a lot of code\footnote{Some
might remember I started with a single file, which did exactly that, but the
file got too large to maintain efficiently.}.
\item[window\_builder] Contains the code to create a widget object, from the
widget definition and the data supplied in the window definition. Again the
name of the file matches the name of widget they build. (These files are
also mostly small.)
\end{description}
\end{description}
\item[data/gui] The general data directory with all parts used for the gui. All
guis shipped with Wesnoth should be in a sub-directory of this one, with a
config file with the name of the directory as main entry point and include
the sub-directory. At the time of writing only one gui is shipped, the
default one.
\begin{description}
\item[default] This directory is the bases for the default gui.
\begin{description}
\item[macro] This directory contains some helper macros, for default font sizes.
\item[widget] This directory contains the definitions of widgets. Since there
can be multiple definitions of a widget their name is the name of the widget
with a suffix. The suffix for the default widgets is, \textsc{default} for
the others an appropriate name is picked.
\item[window] Contains the definitions of windows, the name of the files is the
name of the dialogue they represent.
\end{description}
\end{description}
\end{description}
\chapter{Design details}
Now that the big picture regarding the library is known, I dive into parts of
the code that can use more explanation. A part not explained here, doesn't mean
the code is obvious or simple, lack of time to document it properly is more
likely the excuse.
\section{Boot strapping the library}
Before the library can be started the available widgets and windows need to
be registered. This registering is done before \textsc{main()} is started.
The registering is done by some small static classes that do the registering
of the item\footnote{Widget or Window.} in its constructor. Since this
involves some redundant typing there are REGISTER\_XXX macros written.
After all items are register the library can be started, which is simply
done by \textsc{gui2::init()}. This function does all required steps to get
the library up and running.
\section{Layout algorithm}
An important part of the gui engine is to properly layout the widgets in the
available space. The documentation of that algorithm is written in
doxygen\footnote{\url{http://devdocs.wesnoth.org/layout\_algorihm.html}}.
\section{Event handling and dispatching}
\label{event_handling}
The event handling translates the ``raw'' SDL events to an event structure
specific to gui2, effectively decoupling the interface. This also allows adding
frameworks to fake events. The documentation of that code is written in doxygen
doxygen\footnote{\url{http://devdocs.wesnoth.org/event_dispatching.html}}
\section{Iterator}
The iterator class is written\footnote{The code hasn't been written yet only
designed how it should look. Still I feel the design is rather finished and I
can update this paper if details change too much.} to alleviate certain
problems. The scrollbar containers have their own grid and a grid for it's
content. The implantation makes the looping over all children tricky. This has
been solved, but the design of the solution is rather awkward. Obviously fixing
the design is the right thing to do, but that breaks the iteration.
By first writing an iteration class the interface for iteration can be kept
cleaner and the classes can easily be refactored.
\subsection{Design}
There are two basic kind of iterators in the design, the simple ones that can
only travel themselves and their direct children. This type will be referred to
as basic iterator hereafter.
This basic iterator is a superclass for several specific subclasses. The
superclass is a concrete class, which can be instantiated. This class acts as a
sentinel iterator, signalling the end of a list.
Every widget has it's own creation function returning a pointer to a subclass
object, allowing the main iterator to keep pointers to basic iterators, which it
uses for travelling.
\paragraph{}
The other kind is called the main iterator. This iterator is the type the user
normally creates and uses. The class is a template class, where a policy designs
how the travelling should go\footnote{For now only one policy is planned, but I
can think of more kinds}.
When the main iterator is created it's possible to add a predicate to the
constructor. The predicate determines what the travelling routine does with a
candidate widget.
Now that we know the players in the game look further about the implementation
details and the decisions made.
\subsubsection{Travelling}
A widget can have several ``layers'' namely:
\begin{description}
\item[self] The widget itself.
\item[grid] Container widget have a grid which is another layer, note that for a
grid widget, its grid and self layer are the same.
\item[content] Scrollbar containers have a grid, containing their scrollbars and
a dummy content spacer. Their real content is stored in a separate internal
node, which is used as content layer.
\end{description}
The travel policy decides in which order these layers are visited, and when a
node from a ``grid'' or ``content'' is returned, which direction to travel?
Travel over them first and then into their children or children first? These
decisions are coded in the travelling policy.
While travelling, the policy finds a candidate widget to travel to. This widget is
offered for evaluation to the predicate, which returns one of the following
values:
\begin{description}
\item[return] The widget referred by the iterator is accepted and the algorithm
found its target.
\item[continue] The widget is skipped and the next candidate is sought.
\item[break] The widget is not allowed and the travelling at this layer stops.
This doesn't mean the algorithm gives up. This layer is cancelled but the
travelling path might have more options, which are used. At the moment there's
no way to tell that the candidate has failed and that the searching should stop
altogether, a ``exit'' result might be added for that case.
\end{description}
Obviously these names are inspired by the C++ keywords.
\subsubsection{Copying}
The basic iterators are copyable since their state can easily be copied.
\paragraph{}
The main iterator can't be copied, it would involve copying the state
and copying the iterator is deemed not to be needed.
\subsubsection{Operator++(int) (postfix increment)}
The postfix increment operator hasn't been added to either widget type. For the
basic iterators, there's the problem that the type iterator returned after
iteration might not be the original type. This works properly on the prefix
increment operator since it's a virtual function, using the covariant return
type.
The postfix version needs to return an object and that would involve slicing the
returned object instead of binding it to a reference.
\paragraph{}
The main iterator has no postfix increment operator since it's not copyable.
This of course is a bit the chicken and the egg problem, since I wanted to
prevent the postfix increment operator I made the class not copyable.
For most (standard) iterator classes the overhead of copying isn't too high
since those iterators carry little state. This iterator carries a lot of state,
so you don't want to copy it. The cost of copying is $O(n)$ where $n$ is the
depth in the widget tree the iterator is\footnote{This might depend on the
travelling policy, but it won't get cheap}. So in the same spirit that the
standard library doesn't add operator[] for std::list I omitted the postfix
increment operator.
\section{Callbacks}
\S~\ref{event_handling} describes the generic event handling for the widgets but
in some cases a widget wants to notify other widgets of a state change. Parts of
gui2 use simple C-style callbacks for that purpose, but using boost::function
makes better replacement. Therefore the code was analysed closer and another
problem should be rectified; The callback causes a binding between two objects,
but they are not notified of the deletion of the object leaving a hole for
calling a destroyed object.
In order to fix the problem two classes are defined:
\begin{description}
\item[tnotifier] The class sending the wanted notification.
\item[tnotifiee] The class to manage the lifetime of the connection.
\end{description}
The tnotifiee is a small class that holds a pointer to the receiver its
connected to, upon destruction it uses this pointer to deregister itself by the
receiver. There after the callback function will no longer be called.
The class should be used as member of a class so it can manage the lifetime of
the connection with the tnotifier.
\paragraph{}
The tnotifier is the main class, when a callback is registered it stores the
callbacks in an internal list and updates the pointer in the tnotifiee to
itself.
Upon destruction it clears the pointer in all tnotifiees that point to use, that
way upon destruction of the tnotifiee it won't try to deregister itself with
this destroyed object.
Subclasses of the tnotifier should add an notification function so the notifier
can call all callbacks in the list. The notifier should take care of this
calling.
\chapter{Creating new widgets and dialogues}
This chapter dives into the area of creating your own widgets or dialogues. For
this example we use a real widget and dialogue under development. The widget
being developed is the progress bar and the screen the initial load screen.
First we make the widget then the screen in order to test the widget, this
happens often when you need to add a new widget that you need it for a new
dialogue so you need to create both for testing. Another option is to add the
widget as dummy item to another existing dialogue. When adding a dialogue for
which all widgets already exist this doesn't matter and you can directly dive
into generating the dialogue.
\section{Creating the widget}
\label{sec:creating_the_widget}
The widget is normally split in 7 different files. Three .cpp/.hpp files and one
WML file. Usually I start to create the new files by simply copy pasting the old
files and pick a rather small class like timage or something. Then I start to
update the build system files, followed by creating the code for the
classes\footnote{Obviously I already thought about the design of the class
before that, but that's of less importance in this tutorial}.
Now we start to explain what the various files do, first a general overview of
the files after which I dive more into the implementation of the file.
\begin{description}
\item[src/gui/auxiliary/widget\_definition/progress\_bar.*]
These files contain the definition of the progress bar. This mainly involves
the fixed fields for the widget. This widget has no extra fields so the copy
pasted version suffices. This file contains the \emph{static} properties for
a certain definition of a progress bar.
\begin{description}
\item[hpp] Listing \ref{widget_definition.hpp} contains the sample code.
\begin{description}
\item[Line \ref{widget_definition.hpp:control}] Normally a
widget definition inherits from tcontrol\_definition which defines the
basic mandatory fields for a widget definition and a templated load
function for the resolution. Most widgets don't add more members to this
class.
\item[Line \ref{widget_definition.hpp:resolution}] Normally a resolution
definition inherits from tresolution\_definition\_ which define the
basic mandatory fields for a resolution definition. Not all widgets use
these members, but most do. Most widgets don't add more members to this
class, except most container classes that add a grid builder
definition. Another example is are the scrollbars which define some
minimum sizes for parts of their control.
\end{description}
\item[cpp] Listing \ref{widget_definition.cpp} contains the sample code.
\begin{description}
\item[Line \ref{widget_definition.cpp:textdomain}] Every file in the library
is inside the ``wesnoth-lib'' text domain.
\item[Line \ref{widget_definition.cpp:constructor}] The constructor used is
pretty typical for all widgets.
\item[Line \ref{widget_definition.cpp:resolution_constructor}] The
constructor used is semi-typical. Resolutions that have their own
members initialize them in the constructor, but also validate mandatory
fields for their existence.
Also the progress bar only has one state, most widgets have more and
thus add more states.
And maybe the most important part of this constructor is the wiki
comment. This comment is used to generate wiki pages about the widget.
These wiki pages are used by content creators to know how to create a
proper WML file for the class.
\end{description}
\end{description}
\item[data/gui/default/widget/progress\_bar\_default.cfg]
Now that the widget is defined, we can decide how it should look. Which is
done in this file. Note that in the formula we use ``percentage'' which is
the percentage of the bar to be filled.
\begin{description}
\item[Line \ref{progress_bar.cfg:textdomain}] Every file in the library
is inside the ``wesnoth-lib'' text domain.
\item[Line \ref{progress_bar.cfg:class}]
Beginning of the progress bar, this part is much better documented in
the wiki %TODO ref
since this part is aimed at WML authors, duplicating that effort here
makes no sense so just visit the wiki.
\end{description}
\item[src/gui/auxiliary/window\_builder/progress\_bar.*]
These files contain the code to build a widget from the definition described
above and a config object as defined in the window. This file contains the
\emph{dynamic} properties of the progress bar. Since the progress bar has no
dynamic content this file is also rather short.
\begin{description}
\item[hpp] Listing \ref{window_builder.hpp} contains the sample code.
\begin{description}
\item[Line \ref{window_builder.hpp:control}] Normally a
window builder inherits from tbuilder\_control which defines the
basic mandatory fields for a window builder.
The function also declares the build function.
Widget define their own member if they want to have some runtime
settings, e.g. the spacer defines its fixed size if needed.
\end{description}
\item[cpp] Listing \ref{window_builder.cpp} contains the sample code.
\begin{description}
\item[Line \ref{window_builder.cpp:textdomain}] Every file in the library
is inside the ``wesnoth-lib'' text domain.
\item[Line \ref{window_builder.cpp:constructor}] The constructor used is
pretty typical for all widgets.
If the constructor initialize custom members it may add some validation.
This is also true when the widget has a grid, like the listbox or
multi\_page.
\item[Line \ref{window_builder.cpp:build}]
This build() is semi-typical, it creates the widget initializes the
default fields and returns the created object.
Widgets with their own members initialize them after initializing the
default members.
\item[Line \ref{window_builder.cpp:wiki}]
At the end of the file it contains two wiki comment sections:
The first defines a macro with a short description of the widget.
The second describes the extra fields for the instance of the widget.
\end{description}
\end{description}
\item[src/gui/widgets/progress\_bar.*]
These files contain the interaction the widget has with the outer world and
how it reacts to events. Again the progress bar is rather boring. The
interesting part is the setter of the percentage, here the value of the
percentage use in the drawing routines is set\footnote{Other classes have a
separate update canvas, but that's because the canvas in those classes is
invalidated in several functions.}
\begin{description}
\item[hpp] Listing \ref{progress_bar.hpp} contains the sample code.
\begin{description}
\item[Line \ref{progress_bar.hpp:class}]
Most simple widgets derive from tcontrol, which is the base class of the
visual widgets. Another common base is tcontainer or
tscrollbar\_container.
\item[Line \ref{progress_bar.hpp:constructor}]
The class calls the control constructor with the number of states, which
is the number of canvases used. Other then that it does the
initialization of its members. Some constructors do a bit more
processing but where a lot needs to be done, it's often deferred to a
finalize function.
\item[Line \ref{progress_bar.hpp:inherited}]
This section implements or overrides member functions of the base class.
Some functions are pure virtual and need to be overridden others to
change behaviour.\footnote{Of course a list of which functions are
common the add here would be nice, however the library is still too
volatile to do so, no need to create a soon out of date list.}
\item[Line \ref{progress_bar.hpp:settersgetters}]
This section contains the setters and getters for the class. The
list below shows the common convention where T is the type of the
setter/getter and ``foo'' the name of the member without trailing
underscore.
\begin{description}
\item{setter}
Signature void set\_foo(const T foo); The type T can be by reference
if a larger class.
\item{getter}
Signature T get\_foo() const { return foo\_; } The type T is always
returned by value.
\item{reference}
Signature T\& foo() { return foo\_; } This version returns the value
by reference, and it should also have a const version. These
functions or at least the non-const version are often not public.
\item{pointer}
Signature T* foo() { return foo\_; } Almost the same as the
reference version and is used for members that are pointers. The
text of the reference also applies here.
Furthermore this version often has no normal getter but may have a
setter.
\end{description}
\item[Line \ref{progress_bar.hpp:state}]
The state has the list of states available and normally has COUNT as
last value, which in turn is used in the constructor to initialize the
base class.
\end{description}
\item[cpp] Listing \ref{progress_bar.cpp} contains the sample code.
\begin{description}
\item[Line \ref{progress_bar.cpp:textdomain}] Every file in the library
is inside the ``wesnoth-lib'' text domain.
\item[Line \ref{progress_bar.cpp:logheader}]
The common headers used for logging, this makes sure the output of the
items always have the same format. Used by
LOG\_FOO \textless\textless{} LOG\_HEADER \textless\textless{} "foo.\textbackslash{}n"; or
log\_scope(foo, LOG\_SCOPE); % check function signature
\item[Line \ref{progress_bar.cpp:register}]
Registers a widget, this part is really volatile and still has some
hoops and rings to jump through. Normally it works if not too bad and
look through the other widgets to see how to fix it.
\textbf{Caveat emptor} just when all compiles fine you get a runtime
error about your new nice class, the builder claims your widget doesn't
exist. \emph{Don't panic} probably you did nothing wrong grep TRY in
src/gui/auxiliary/window\_builder.cpp (at the moment of writing line 96)
and read the comment above, dully add your class to the list and be
happy.
\item[Line \ref{progress_bar.cpp:get_control_type}]
Simple helper to get the human readable name of the class. The first
version was inlined in the header, but that seems to be an ODR
violation. At least at some point a compiler or linker complained, can't
really remember which one. % TODO git pick-axe
\end{description}
\end{description}
This completes the writing of the files for the new classes, some files need to
be modified to get things up and running. The build system files need the three
new .cpp files listed, these are:
\begin{itemize}
\item src/Makefile.am
\item src/SConstruct
\item src/CmakeLists.txt
\end{itemize}
The next step has become optional now that most build systems can glob the files
needed for translation, but I still add them manually as well. The file is
po/wesnoth-lib/POTFILES.IN which holds the files for the wesnoth-lib text
domain.
The last file to be edited is src/gui/widgets/settings.cpp here a wiki comment
for the new class needs to be added, this might change. The reason is that
before automatically registering widgets this file needed more modification so
it was done in one fell swoop.
\end{description}
This completes the simple progress bar widget. Of course we haven't tested it
yet since there's no dialogue to test it in. So let's implement a dialogue.
\section{Creating the window}
\label{sec:creating_the_window}
The window is normally split in 3 files. One .cpp/.hpp file and one WML file.
The dialogue used is the unit attack dialogue, at least an initial version. This
version barely does what it needs to do, and the in game version will be more
polished. This simple version is nice as example here since it's not too large.
Now we start to explain what the various files do, first a general overview of
the files after which I dive more into the implementation of the file.
\begin{description}
\item[src/gui/dialogs/unit\_attack.*pp]
These files contain the code of the dialogue. This code `binds' the WML
code and allows the C++ code to show the dialogue show the right data at the
right place. In several cases this also allows the designer of the WML file
to decide which widget s/he picks.
\begin{description}
\item[hpp] Listing \ref{unit_attack.hpp} contains the sample code.
\begin{description}
\item[Line \ref{unit_attack.hpp:class}]
Defines the class itself, which normally inherits from tdialog.
Sometimes a class inherits from another class, but that class then
derives from tdialog, for example the wml\_dialogs have a common base and
separate classes for left and right.
\item[Line \ref{unit_attack.hpp:constructor}]
Dialogues are often created and then shown once before being destroyed,
therefore the constructor often takes all parameters needed. Since the show
function shouldn't be overloaded the only other alternative would be member
functions to set the members. Note when keeping `references' to widgets these
member must be pointers and set to NULL in the constructor, this due to the
simple fact that the widgets aren't created at this point. Also keep in mind
when
the dialogue is shown twice (using the \emph{same} object) the widgets won't be
the same. The window owning the widgets is destroyed after being shown and
a new one created upon the next call to show.
\item[Line \ref{unit_attack.hpp:settersgetters}]
As said before there are often no setters, since all is set in the
contructor. Most members also remain hidden since the caller has the
information. Only members that change are `exported'. The most common example in
a class with a listbox, the last selected item is made public, since this is
often used as choice the user made.
\item[Line \ref{unit_attack.hpp:window_id}]
This function is part of the window registration and only needs to be
declared in the header, the definition will be provided by a macro.
\item[Line \ref{unit_attack.hpp:pre_show}]
This function is called after the window is created but before being
shown. This is the point to set the members that point to a widget. As said
before every time the dialogue is shown a newly created window is shown, so
setting the pointers can be unconditional. This is also the place to fill the
widgets with the proper content, if needed. Eg filling the languages in the
language selection dialogue.
\item[Line \ref{unit_attack.hpp:post_show}]
Called after the dialogue is shown, this allows you to update certain
statuses. E.g. if the dialogue is closed with the OK button set the selected
language to the newly selected language. You can also update it unconditionally
and let the caller test the status.
\item[Line \ref{unit_attack.hpp:member}]
At this point the list of private members start.
\end{description}
\item[cpp] Listing \ref{unit_attack.cpp} contains the sample code. Note some
parts are already described more verbose in the header and thus not mentioned
here.
\begin{description}
\item[Line \ref{unit_attack.cpp:textdomain}]
Like written for the widget, every file is inside the `wesnoth-lib' text
domain.
\item[Line \ref{unit_attack.cpp:wiki}]
Directly after the includes (inside the gui2 namespace) the wiki
documentation starts.
\item[Line \ref{unit_attack.cpp:register}]
After the wiki documentation the registration code follows, it defines
the window\_id function and does some other registration parts.
\item[Line \ref{unit_attack.cpp:pre_show}]
The pre\_show is already mentioned in the header file and it the one
used here uses several static helper functions to do it's job.
\end{description}
\end{description}
\item[src/gui/dialogs/unit\_attack.cfg]
This file contains the WML that defines the widget. It should honour the
restrains set in the wiki comment like \ref{unit_attack.cpp:wiki}. These
constrains set the minimum set of needed widgets and their types, and mentions
the optional widgets. How the widgets are placed and whether only the optional
ones are set is up to the designer of the dialog. The gui toolkit wiki has more
information on the subject. Still some parts are worth mentioning.
\begin{description}
\item[Line \ref{unit_attack.cfg:textdomain}]
Every file in the library is inside the ``wesnoth-lib'' text domain.
\item[Line \ref{unit_attack.cfg:defines}]
With larger dialogs I often design them on paper and then divide the
dialog in several sections. These sections often are turned into defines in
order to keep the main dialog rather simple. WML tends to be verbose and deeply
nested, and the GUI's also tend to have a lot of nested items, these powers
combined result in deeply nested structure, where one can easily lose the way.
Also not that all local macros are prefixed with \_GUI, this to avoid clashes
with other macros. The BIG in the names are just because I want to make a
different definition of high resolution screens (this will be the first screen
using that feature, but that will be added later).
\item[Line \ref{unit_attack.cfg:window}]
Not much to tell about this part, since it's all documented in the wiki.
But do note that due to the defines above the grid in the window itself is
rather simple.
\item[Line \ref{unit_attack.cfg:undefines}]
The local macros are no longer needed so undefine them, I normally
undefine them in the oposite order of the definitions, I'm quite sure that's not
actually needed, but it feels the right way\texttrademark.
\end{description}
\end{description}
\appendix
\chapter{Files for creating the widget}
This chapter contains the files created in \S~\ref{sec:creating_the_widget}, these
files aren't the real files added, but a slightly modified version; The
copyright headers are stripped to avoid taking up useless space. Some extra
comments are added to make referencing possible. This also means the files here
will get out of date with the real files when fixes are committed, that's also
fine since it doesn't change how the files are structured. The label still
mentions the original filename, but might over time differ from the listings
below.
\lstinputlisting[style=C++
, caption={src/gui/auxiliary/widget\_definition/progress\_bar.hpp}
, label=widget_definition.hpp]
{gui2/widget_definition.hpp}
\pagebreak
\lstinputlisting[style=C++
, caption={src/gui/auxiliary/widget\_definition/progress\_bar.cpp}
, label=widget_definition.cpp]
{gui2/widget_definition.cpp}
\pagebreak
\lstinputlisting[style=WML
, caption={data/gui/default/widget/progress\_bar\_default.cfg}
, label=progress_bar.cfg]
{gui2/progress_bar.cfg}
\pagebreak
\lstinputlisting[style=C++
, caption={src/gui/auxiliary/window\_builder/progress\_bar.hpp}
, label=window_builder.hpp]
{gui2/window_builder.hpp}
\lstinputlisting[style=C++
, caption={src/gui/auxiliary/window\_builder/progress\_bar.cpp}
, label=window_builder.cpp]
{gui2/window_builder.cpp}
\lstinputlisting[style=C++
, caption={src/gui/widgets/progress\_bar.hpp}
, label=progress_bar.hpp]
{gui2/progress_bar.hpp}
\pagebreak
\lstinputlisting[style=C++
, caption={src/gui/widgets/progress\_bar.cpp}
, label=progress_bar.cpp]
{gui2/progress_bar.cpp}
\chapter{Files for creating the window}
This chapter like the previous one contains listings of files, this time for
\S~\ref{sec:creating_the_window}. About the same is true for these files, except
we know the originals will change in the future.
\lstinputlisting[style=C++
, caption={src/gui/dialogs/unit\_attack.hpp}
, label=unit_attack.hpp]
{gui2/unit_attack.hpp}
\lstinputlisting[style=C++
, caption={src/gui/dialogs/unit\_attack.cpp}
, label=unit_attack.cpp]
{gui2/unit_attack.cpp}
\lstinputlisting[style=WML
, caption={src/gui/dialogs/unit\_attack.cfg}
, label=unit_attack.cfg]
{gui2/unit_attack.cfg}
\end{document}