Update design document.

This commit is contained in:
Mark de Wever 2010-04-05 09:51:00 +00:00
parent 0d29a6d216
commit dcfd8bea24
8 changed files with 555 additions and 1 deletions

View file

@ -1,6 +1,27 @@
\documentclass[a4paper,notitlepage,twocolumn,draft]{report}
\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}
@ -551,5 +572,214 @@ 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{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 fiets 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.
% TODO add the description of the files
\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.}
% TODO add the description of the files
\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.
\begin{comment}
FIXME add to list in settings.
FIXME add a caveat emptor regarding broken linker
FIXME add the builder files, at least mention them
\section{Creating the window}
The window is normally split in 3 files. One .cpp/.hpp file and one WML file.
The dialogue used it the load progress dialogue, this is not the greatest
example dialogue, so this section may later use another dialogue as example.
\end{comment}
\appendix
\chapter{Files for creating the widget}
This chapter contains the files created in \S~\ref{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}
\end{document}

View file

@ -0,0 +1,84 @@
#textdomain wesnoth-lib
###
### Definition of an progress bar, which has the same height on normal and tiny
### gui.
###
[progress_bar_definition]
id = "default"
description = "A progress_bar."
[resolution]
min_width = 14
min_height = 41
default_width = 54
default_height = 41
max_width = 0
max_height = 41
[state_enabled]
[draw]
# Outer "golden" border.
[rectangle]
x = 0
y = 0
w = "(width)"
h = 41
border_thickness = 1
border_colour = "188, 176, 136"
[/rectangle]
# Inner black border, with gray filling.
[rectangle]
x = 1
y = 1
w = "(width - 2)"
h = 39
border_thickness = 1
border_colour = "0, 0, 0"
fill_colour = "21, 22, 24"
[/rectangle]
# 3D effect at upper side.
[rectangle]
x = 2
y = 3
w = "(((width - 4) * precentage) / 100)"
h = 2
border_thickness = 0
fill_colour = "79, 103, 123"
[/rectangle]
# Main bar
[rectangle]
x = 2
y = 5
w = "(((width - 4) * precentage) / 100)"
h = 33
border_thickness = 0
fill_colour = "21, 53, 80"
[/rectangle]
# 3D effect at lower side.
[rectangle]
x = 2
y = 38
w = "(((width - 4) * precentage) / 100)"
h = 2
border_thickness = 0
fill_colour = "10, 26, 40"
[/rectangle]
[/draw]
[/state_enabled]
[/resolution]
[/progress_bar_definition]

View file

@ -0,0 +1,40 @@
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/widgets/progress_bar.hpp"
#include "gui/auxiliary/widget_definition/progress_bar.hpp"
#include "gui/auxiliary/log.hpp"
#include "gui/widgets/settings.hpp"
#include <boost/bind.hpp>
#define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
#define LOG_HEADER LOG_SCOPE_HEADER + ':'
namespace gui2 {
REGISTER_WIDGET(progress_bar)
void tprogress_bar::set_percentage(const unsigned percentage)
{
assert(percentage <= 100);
if(percentage_ != percentage) {
percentage_ = percentage;
foreach(tcanvas& c, canvas()) {
c.set_variable("percentage", variant(percentage));
}
set_dirty();
}
}
const std::string& tprogress_bar::get_control_type() const
{
static const std::string type = "progress_bar";
return type;
}
} // namespace gui2

View file

@ -0,0 +1,60 @@
#ifndef GUI_WIDGETS_PROGRESS_BAR_HPP_INCLUDED
#define GUI_WIDGETS_PROGRESS_BAR_HPP_INCLUDED
#include "gui/widgets/control.hpp"
namespace gui2 {
class tprogress_bar
: public tcontrol
{
public:
tprogress_bar()
: tcontrol(COUNT)
, percentage_(-1)
{
// Force canvas update
set_percentage(0);
}
/***** ***** ***** ***** Inherited ***** ***** ***** *****/
/** Inherited from tcontrol. */
void set_active(const bool /*active*/) {}
/** Inherited from tcontrol. */
bool get_active() const { return true; }
/** Inherited from tcontrol. */
unsigned get_state() const { return ENABLED; }
/** Inherited from tcontrol. */
bool disable_click_dismiss() const { return false; }
/***** ***** ***** setters / getters for members ***** ****** *****/
void set_percentage(const unsigned percentage);
unsigned get_percentage() const { return percentage_; }
private:
/**
* Possible states of the widget.
*
* Note the order of the states must be the same as defined in settings.hpp.
*/
enum tstate { ENABLED, COUNT };
/** The percentage done. */
unsigned percentage_;
/** Inherited from tcontrol. */
const std::string& get_control_type() const;
};
} // namespace gui2
#endif

View file

@ -0,0 +1,41 @@
#define GETTEXT_DOMAIN "wesnoth-lib" /*@ \label{widget_definition.cpp:textdomain} @*/
#include "gui/auxiliary/widget_definition/progress_bar.hpp"
#include "gui/auxiliary/log.hpp"
namespace gui2 {
tprogress_bar_definition::tprogress_bar_definition(const config& cfg) : /*@ \label{widget_definition.cpp:constructor} @*/
tcontrol_definition(cfg)
{
DBG_GUI_P << "Parsing progress bar " << id << '\n';
load_resolutions<tresolution>(cfg);
}
tprogress_bar_definition::tresolution::tresolution(const config& cfg) : /*@ \label{widget_definition.cpp:resolution_constructor} @*/
tresolution_definition_(cfg)
{
/*WIKI
* @page = GUIWidgetDefinitionWML
* @order = 1_progress_bar
*
* == Progress bar ==
*
* @macro = progress_bar_description
*
* The definition of a progress bar. This object shows the progress of a certain
* action, or the value state of a certain item.
*
* The following states exist:
* * state_enabled, the progress bar is enabled.
*
*/
// Note the order should be the same as the enum tstate is progress_bar.hpp.
state.push_back(tstate_definition(cfg.child("state_enabled")));
}
} // namespace gui2

View file

@ -0,0 +1,22 @@
#ifndef GUI_AUXILIARY_WIDGET_DEFINITION_PROGRESS_BAR_HPP_INCLUDED
#define GUI_AUXILIARY_WIDGET_DEFINITION_PROGRESS_BAR_HPP_INCLUDED
#include "gui/auxiliary/widget_definition.hpp"
namespace gui2 {
struct tprogress_bar_definition /*@ \label{widget_definition.hpp:control} @*/
: public tcontrol_definition
{
explicit tprogress_bar_definition(const config& cfg);
struct tresolution /*@ \label{widget_definition.hpp:resolution} @*/
: public tresolution_definition_
{
explicit tresolution(const config& cfg);
};
};
} // namespace gui2
#endif

View file

@ -0,0 +1,52 @@
#define GETTEXT_DOMAIN "wesnoth-lib" /*@ \label{window_builder.cpp:textdomain} @*/
#include "gui/auxiliary/window_builder/progress_bar.hpp"
#include "config.hpp"
#include "gui/auxiliary/log.hpp"
#include "gui/widgets/progress_bar.hpp"
namespace gui2 {
namespace implementation {
tbuilder_progress_bar::tbuilder_progress_bar(const config& cfg) /*@ \label{window_builder.cpp:constructor} @*/
: tbuilder_control(cfg)
{
}
twidget* tbuilder_progress_bar::build() const /*@ \label{window_builder.cpp:build} @*/
{
tprogress_bar* widget = new tprogress_bar();
init_control(widget);
DBG_GUI_G << "Window builder: placed progress bar '"
<< id << "' with defintion '"
<< definition << "'.\n";
return widget;
}
} // namespace implementation
} // namespace gui2
/*@ \label{window_builder.cpp:wiki} @*//*WIKI_MACRO
* @start_macro = progress_bar_description
*
* A progress bar shows the progress of a certain object.
* @end_macro
*/
/*WIKI
* @page = GUIWidgetInstanceWML
* @order = 2_progress_bar
*
* == Image ==
*
* @macro = progress_bar_description
*
* A progress bar has no extra fields.
*/

View file

@ -0,0 +1,25 @@
#ifndef GUI_AUXILIARY_WINDOW_BUILDER_PROGRESS_BAR_HPP_INCLUDED
#define GUI_AUXILIARY_WINDOW_BUILDER_PROGRESS_BAR_HPP_INCLUDED
#include "gui/auxiliary/window_builder/control.hpp"
namespace gui2 {
namespace implementation {
struct tbuilder_progress_bar /*@ \label{window_builder.hpp:control} @*/
: public tbuilder_control
{
tbuilder_progress_bar(const config& cfg);
twidget* build () const;
};
} // namespace implementation
} // namespace gui2
#endif