mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-26 12:43:54 +00:00
1043 lines
45 KiB
TeX
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}
|
|
|