Added massive parser documentation.

Some parser changes.

svn-id: r32726
This commit is contained in:
Vicent Marti 2008-06-16 23:38:21 +00:00
parent cb6cb1361b
commit 6932c836cf
5 changed files with 343 additions and 61 deletions

View file

@ -54,19 +54,19 @@ VectorRenderer *createRenderer(int mode) {
void VectorRenderer::drawStep(const Common::Rect &area, const DrawStep &step) { void VectorRenderer::drawStep(const Common::Rect &area, const DrawStep &step) {
if (step.flags & DrawStep::kStepCallbackOnly) { if (step.flags & DrawStep::kStepCallbackOnly) {
(this->*(step.drawing_call))(area, step); (this->*(step.drawingCall))(area, step);
return; return;
} }
if (step.flags & DrawStep::kStepSetBG) if (step.flags & DrawStep::kStepSetBG)
setBgColor(step.color2.r, step.color2.g, step.color2.b); setBgColor(step.bgColor.r, step.bgColor.g, step.bgColor.b);
if (step.flags & DrawStep::kStepSetFG) if (step.flags & DrawStep::kStepSetFG)
setFgColor(step.color1.r, step.color1.g, step.color1.b); setFgColor(step.fgColor.r, step.fgColor.g, step.fgColor.b);
if (step.flags & DrawStep::kStepSetGradient) if (step.flags & DrawStep::kStepSetGradient)
setGradientColors(step.color1.r, step.color1.g, step.color1.b, setGradientColors(step.gradColor1.r, step.gradColor1.g, step.gradColor1.b,
step.color2.r, step.color2.g, step.color2.b); step.gradColor2.r, step.gradColor2.g, step.gradColor2.b);
if (step.flags & DrawStep::kStepSetShadow) if (step.flags & DrawStep::kStepSetShadow)
shadowEnable(step.shadow); shadowEnable(step.shadow);
@ -78,12 +78,12 @@ void VectorRenderer::drawStep(const Common::Rect &area, const DrawStep &step) {
setStrokeWidth(step.stroke); setStrokeWidth(step.stroke);
if (step.flags & DrawStep::kStepSetFillMode) if (step.flags & DrawStep::kStepSetFillMode)
setFillMode((FillMode)step.fill_mode); setFillMode((FillMode)step.fillMode);
if (step.flags & DrawStep::kStepSettingsOnly) if (step.flags & DrawStep::kStepSettingsOnly)
return; return;
(this->*(step.drawing_call))(area, step); (this->*(step.drawingCall))(area, step);
} }
/******************************************************************** /********************************************************************

View file

@ -45,10 +45,12 @@ struct DrawStep {
struct { struct {
uint8 r, g, b; uint8 r, g, b;
} }
color1, /** Foreground color/gradient start */ fgColor, /** Foreground color */
color2; /** Background color/gradient end */ bgColor, /** backgroudn color */
gradColor1, /** gradient start*/
gradColor2; /** gradient end */
bool fill_area; /** If enabled, the draw step occupies the whole drawing area */ bool fillArea; /** If enabled, the draw step occupies the whole drawing area */
struct { struct {
uint16 pos; uint16 pos;
@ -60,12 +62,12 @@ struct DrawStep {
uint8 shadow, stroke, factor, radius; /** Misc options... */ uint8 shadow, stroke, factor, radius; /** Misc options... */
uint8 fill_mode; /** active fill mode */ uint8 fillMode; /** active fill mode */
uint8 extra_data; /** Generic parameter for extra options (orientation/bevel) */ uint8 extraData; /** Generic parameter for extra options (orientation/bevel) */
uint32 scale; /** scale of all the coordinates in FIXED POINT with 16 bits mantissa */ uint32 scale; /** scale of all the coordinates in FIXED POINT with 16 bits mantissa */
void (VectorRenderer::*drawing_call)(const Common::Rect &, const DrawStep &); /** Pointer to drawing function */ void (VectorRenderer::*drawingCall)(const Common::Rect &, const DrawStep &); /** Pointer to drawing function */
enum DrawStepFlags { enum DrawStepFlags {
kStepCallbackOnly = (1 << 0), kStepCallbackOnly = (1 << 0),
@ -330,7 +332,7 @@ public:
} }
void stepGetPositions(const DrawStep &step, const Common::Rect &area, uint16 &in_x, uint16 &in_y, uint16 &in_w, uint16 &in_h) { void stepGetPositions(const DrawStep &step, const Common::Rect &area, uint16 &in_x, uint16 &in_y, uint16 &in_w, uint16 &in_h) {
if (step.fill_area) { if (step.fillArea) {
in_x = area.left; in_x = area.left;
in_y = area.top; in_y = area.top;
in_w = area.width(); in_w = area.width();
@ -392,13 +394,13 @@ public:
void drawCallback_TRIANGLE(const Common::Rect &area, const DrawStep &step) { void drawCallback_TRIANGLE(const Common::Rect &area, const DrawStep &step) {
uint16 x, y, w, h; uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h); stepGetPositions(step, area, x, y, w, h);
drawTriangle(x, y, w, h, (TriangleOrientation)step.extra_data); drawTriangle(x, y, w, h, (TriangleOrientation)step.extraData);
} }
void drawCallback_BEVELSQ(const Common::Rect &area, const DrawStep &step) { void drawCallback_BEVELSQ(const Common::Rect &area, const DrawStep &step) {
uint16 x, y, w, h; uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h); stepGetPositions(step, area, x, y, w, h);
drawBeveledSquare(x, y, w, h, step.extra_data); drawBeveledSquare(x, y, w, h, step.extraData);
} }
/** /**

View file

@ -163,43 +163,43 @@ int InterfaceManager::runGUI() {
Graphics::DrawStep *steps = new Graphics::DrawStep[5]; Graphics::DrawStep *steps = new Graphics::DrawStep[5];
steps[0].color1.r = 214; steps[0].gradColor1.r = 214;
steps[0].color1.g = 113; steps[0].gradColor1.g = 113;
steps[0].color1.b = 8; steps[0].gradColor1.b = 8;
steps[0].color2.r = 240; steps[0].gradColor2.r = 240;
steps[0].color2.g = 200; steps[0].gradColor2.g = 200;
steps[0].color2.b = 25; steps[0].gradColor2.b = 25;
steps[0].fill_mode = VectorRenderer::kFillGradient; steps[0].fillMode = VectorRenderer::kFillGradient;
steps[0].drawing_call = &VectorRenderer::drawCallback_FILLSURFACE; steps[0].drawingCall = &VectorRenderer::drawCallback_FILLSURFACE;
steps[0].flags = DrawStep::kStepSetGradient | DrawStep::kStepSetFillMode; steps[0].flags = DrawStep::kStepSetGradient | DrawStep::kStepSetFillMode;
steps[1].color1.r = 206; steps[1].gradColor1.r = 206;
steps[1].color1.g = 121; steps[1].gradColor1.g = 121;
steps[1].color1.b = 99; steps[1].gradColor1.b = 99;
steps[1].color2.r = 173; steps[1].gradColor2.r = 173;
steps[1].color2.g = 40; steps[1].gradColor2.g = 40;
steps[1].color2.b = 8; steps[1].gradColor2.b = 8;
steps[1].radius = 8; // radius steps[1].radius = 8; // radius
steps[1].fill_area = true; steps[1].fillArea = true;
steps[1].drawing_call = &VectorRenderer::drawCallback_ROUNDSQ; steps[1].drawingCall = &VectorRenderer::drawCallback_ROUNDSQ;
steps[1].flags = DrawStep::kStepSetGradient; steps[1].flags = DrawStep::kStepSetGradient;
steps[1].scale = (1 << 16); steps[1].scale = (1 << 16);
steps[2].radius = 8; // radius steps[2].radius = 8; // radius
steps[2].fill_area = false; steps[2].fillArea = false;
steps[2].x.relative = true; steps[2].x.relative = true;
steps[2].x.pos = 32; steps[2].x.pos = 32;
steps[2].y.relative = false; steps[2].y.relative = false;
steps[2].y.pos = 32; steps[2].y.pos = 32;
steps[2].w = 128; steps[2].w = 128;
steps[2].h = 32; steps[2].h = 32;
steps[2].drawing_call = &VectorRenderer::drawCallback_ROUNDSQ; steps[2].drawingCall = &VectorRenderer::drawCallback_ROUNDSQ;
steps[2].flags = DrawStep::kStepCallbackOnly; steps[2].flags = DrawStep::kStepCallbackOnly;
steps[2].scale = (1 << 16); steps[2].scale = (1 << 16);
steps[3].color1.r = 255; steps[3].fgColor.r = 255;
steps[3].color1.g = 255; steps[3].fgColor.g = 255;
steps[3].color1.b = 255; steps[3].fgColor.b = 255;
steps[3].flags = DrawStep::kStepSettingsOnly | DrawStep::kStepSetFG; steps[3].flags = DrawStep::kStepSettingsOnly | DrawStep::kStepSetFG;
Common::Rect area = Common::Rect(32, 32, 256, 256); Common::Rect area = Common::Rect(32, 32, 256, 256);

View file

@ -44,10 +44,10 @@ namespace GUI {
void ThemeParser::debug_testEval() { void ThemeParser::debug_testEval() {
static const char *debugConfigText = static const char *debugConfigText =
"</* lol this is just a moronic test */drawdata id = \"background_default\" cache = true>" "</* lol this is just a moronic test */drawdata id = \"background_default\" cache = true>\n"
"<draw func = \"roundedsq\" /*/fill = \"gradient\" gradient_start = \"255, 255, 128\" gradient_end = \"128, 128, 128\" size = \"auto\"/>" "<drawstep func = \"roundedsq\" fill = \"gradient\" gradient_start = \"255, 255, 128\" gradient_end = \"128, 128, 128\" size = \"auto\"/>\n"
"<draw func = \"roundedsq\" fill = \"none\" color = /*\"0, 0, 0\"*/\"0, 1, 2\" size = \"auto\"/>" "//<drawstep func = \"roundedsq\" fill = \"none\" color = /*\"0, 0, 0\"*/\"0, 1, 2\" size = \"auto\"/>\n"
"</ drawdata>/* lol this is just a simple test*/"; "</ drawdata>/* lol this is just a simple test*/\n";
_text = strdup(debugConfigText); _text = strdup(debugConfigText);
parse(); parse();
@ -59,7 +59,7 @@ void ThemeParser::parserError(const char *error_string) {
printf("PARSER ERROR: %s\n", error_string); printf("PARSER ERROR: %s\n", error_string);
} }
void ThemeParser::parserCallback_DRAW() { void ThemeParser::parserCallback_DRAWSTEP() {
printf("Draw callback!\n"); printf("Draw callback!\n");
} }
@ -68,29 +68,28 @@ void ThemeParser::parserCallback_DRAWDATA() {
} }
void ThemeParser::parseActiveKey(bool closed) { void ThemeParser::parseActiveKey(bool closed) {
printf("Parsed key %s.\n", _activeKey.top().c_str()); printf("Parsed key %s.\n", _activeKey.top()->name.c_str());
if (!_callbacks.contains(_activeKey.top())) { if (!_callbacks.contains(_activeKey.top()->name)) {
parserError("Unhandled value inside key."); parserError("Unhandled value inside key.");
return; return;
} }
// Don't you just love C++ syntax? Water clear. // Don't you just love C++ syntax? Water clear.
(this->*(_callbacks[_activeKey.top()]))(); (this->*(_callbacks[_activeKey.top()->name]))();
for (Common::StringMap::const_iterator t = _keyValues.top().begin(); t != _keyValues.top().end(); ++t) for (Common::StringMap::const_iterator t = _activeKey.top()->values.begin(); t != _activeKey.top()->values.end(); ++t)
printf(" Key %s = %s\n", t->_key.c_str(), t->_value.c_str()); printf(" Key %s = %s\n", t->_key.c_str(), t->_value.c_str());
if (closed) { if (closed) {
_keyValues.pop(); delete _activeKey.pop();
_activeKey.pop();
} }
} }
bool ThemeParser::parseKeyValue(Common::String keyName) { bool ThemeParser::parseKeyValue(Common::String keyName) {
assert(_keyValues.empty() == false); assert(_activeKey.empty() == false);
if (_keyValues.top().contains(keyName)) if (_activeKey.top()->values.contains(keyName))
return false; return false;
_token.clear(); _token.clear();
@ -109,7 +108,7 @@ bool ThemeParser::parseKeyValue(Common::String keyName) {
return false; return false;
} }
_keyValues.top()[keyName] = _token; _activeKey.top()->values[keyName] = _token;
return true; return true;
} }
@ -120,7 +119,6 @@ bool ThemeParser::parse() {
_state = kParserNeedKey; _state = kParserNeedKey;
_pos = 0; _pos = 0;
_keyValues.clear();
_activeKey.clear(); _activeKey.clear();
while (_text[_pos]) { while (_text[_pos]) {
@ -160,11 +158,12 @@ bool ThemeParser::parse() {
} }
if (activeClosure) { if (activeClosure) {
if (_activeKey.empty() || _token != _activeKey.top()) if (_activeKey.empty() || _token != _activeKey.top()->name)
parserError("Unexpected closure."); parserError("Unexpected closure.");
} else { } else {
_keyValues.push(Common::StringMap()); ParserNode *node = new ParserNode;
_activeKey.push(_token); node->name = _token;
_activeKey.push(node);
} }
_state = kParserNeedPropertyName; _state = kParserNeedPropertyName;
@ -173,8 +172,7 @@ bool ThemeParser::parse() {
case kParserNeedPropertyName: case kParserNeedPropertyName:
if (activeClosure) { if (activeClosure) {
activeClosure = false; activeClosure = false;
_activeKey.pop(); delete _activeKey.pop();
_keyValues.pop();
if (_text[_pos++] != '>') if (_text[_pos++] != '>')
parserError("Invalid syntax in key closure."); parserError("Invalid syntax in key closure.");
@ -215,6 +213,9 @@ bool ThemeParser::parse() {
_state = kParserNeedPropertyName; _state = kParserNeedPropertyName;
break; break;
default:
break;
} }
} }

View file

@ -34,6 +34,272 @@
#include "common/hash-str.h" #include "common/hash-str.h"
#include "common/stack.h" #include "common/stack.h"
/**
*********************************************
** Theme Description File format overview. **
*********************************************
This document is a work in progress.
A more complete version will be posted on the wiki soon.
In the new version of the Graphical User Interface for ScummVM, almost
all properties regarding looks, design and positioning of the UI
elements are defined in a set of external files.
The chosen syntax for theme description is a basic subset of XML.
The process of theme description is divided in two main parts: Drawing
specifications for the vector renderer and theme design/layout
information for the actual theme engine.
These two core sections of a theme's description may be placed in a
single file or split for convenience across several files.
_DRAWING SPECIFICATIONS_
The process of rendering a widget on the screen is discretized into
several phases called "drawing steps". A set of such steps, which
generate a basic widget shape on screen is called a Draw Data set. The
GUI Engine loads all the different data sets for a given
widget and takes care of rendering it into the screen based on its
current state.
For example, the basic Button widget may be composed of several sets
of data: Drawing data for the button's idle state, drawing data for
when the button is hovered and drawing data for when the button is
pressed.
The functionality of each set of Drawing Data is hard-coded into the
Graphical User Interface; the most up to date version of all the
drawing sets may be found extensively commented in the
"gui/InterfaceManager.h" file, in the DrawData enumeration inside the
InterfaceManager class.
In order to successfully parse and load a custom theme definition, the
whole list of Draw Data sets must be specified.
_THEME LAYOUT SPECIFICATIONS_
#######
_SYNTAX OVERVIEW AND PARAMETERS_
As stated before, all the theme description is done through a XML-like
syntax. The files are parsed left-to-right, ignoring extra whitespaces
and newlines. Parser data is interpreted during the parsing. As a
general guideline, theme files are composed of keys which may or not
contain specific values for the key and which may parent several
subkeys; independently of this, all keys must be properly closed with
the '/' operator.
<parent_key value1 = "sample">
<child_key1>
<self_closed_child value2 = 124 />
<external_closed_child value3 = 245>
</external_closed_child>
</child_key1>
<child_key2 with_value = "sample">
<subchild/>
</child_key2>
</parent_key>
Note how keys which contain no children may be closed by themselves
or with an external closure.
- Comments
The parser supports the same comment syntax as the C++ programming
language. Comment blocks may be specified by surrounding them with the
'/ *' and '* /' operators, while whole lines may be commented out by
preceding them with the // operator.
Block comments are parsed in a non-hungry manner, i.e. the first
comment closure is understood to close the whole commenting block, so
syntax like
/ * hey look this comment finishes here * / or maybe here?? * /
is invalid.
- Section keys.
The section key is the root level of a theme description file. Each
file may contain one or more of these keys, which specifies the nature
of all their children, namely if the children keys specify drawing or
layout information. Its syntax is as follows:
<layout_info platform = "NDS" resolution = "320x240">
// ...
</layout_info>
<render_info platform = "NDS" resolution = "320x240">
// ...
</render_info>
The "layout_info" key specifies that all children keys contain
information regarding the layout of the theme, while the "render_info"
key specifies that all children keys contain information regarding the
looks of the theme.
Both keys support the two optional parameters "platform" and
"resolution", in order to make a certain layout apply to a single
resolution or to a single platform. To make a key apply for more than
one specific platform or resolution at the same time, you may separate
their names with commas.
<render_info platform = "nds, palmos, pocketpc">
- Render Info keys:
The children of a "render_info" key are expected to be one of these
kind:
-- DrawData key:
DrawData keys are the core of the rendering engine. They specifiy
via their own children the looks of all the UI elements. Here's
their syntax:
<drawdata id = "button_idle" cache = true platform = "NDS"
resolution = "320x240">
</drawdata>
All drawdata keys must contain an "id" value, specifying which set
of drawing data they implement. Here's a list of all possible ids.
#########
Remember that all these ids must me implemented in order for the
parsing to be considered succesful.
DrawData keys may also contain an optional boolean value "cache",
which states if the set of DrawingSteps may be cached into the
memory so it can be blit into the Overlay each frame or if the set
of Drawing Steps should be performed individually each frame. If
omitted, the "cache" value defaults to false.
Also, just like the <render_info> key, DrawData keys may also
contain optional "platform" and "resolution" values, making such
draw steps specific for a single or several platforms or
resolutions. In order to specify several platforms or resolutions,
they must be separated by commas inside the key's value.
<drawdata id = "background_default" cache = true platform = "nds, palmos">
</drawdata>
When making a set of Drawing Data for a widget specific to a
single platform or resolution, remember that the set must be also
implemented later generically for other platforms, or the
rendering of the theme will fail in such platforms.
Lastly, each DrawData key must contain at least a children
"drawstep" subkey, with the necessary info for the
VectorRenderer.
- The DrawStep key
The DrawStep key is probably the most complex definition of
a ThemeDescription file. It contains enough information to
allow the Vector Renderer to draw a basic or complex shape
into the screen.
DrawStep keys are supposed to have no children, so they must
be either self-closed or closed externally.
Their basic syntax is as follows:
<drawstep func = "roundedsq">
</drawstep>
The only required value is the function "func" which states
the drawing function that will be used, and it must be
accompanied by a set of parameters specific to each drawing
step. Here's a list of such parameters:
Common parameters for all functions:
fill = "none|foreground|background|gradient"
Specifies the fill mode for the drawn shape.
Possible values:
"none": Disables filling so only the stroke is shown.
"foreground" (default): Fills the whole shape with the active foreground
color.
"background": Fills the whole shape with the active background
color.
"gradient": Fills the whole shape with the active gradient.
gradient_start = "R, G, B" | "color name"
gradient_end = "R, G, B" | "color name"
fg_color = "R, G, B" | "color name"
bg_color = "R, G, B" | "color name"
Sets the active gradient, foreground or backgroud colors. Colors
may be specified with their three components (red, green, blue)
ranging each from 0 to 255, or via a color name which has
previously been defined in the palette section.
These colours have no default values.
stroke = width (integer)
Sets the active stroke width; strokes may be disabled by setting
this value to 0. All shapes are automatically stroked with the
given width and the active foreground color. Defaults to 1.
shadow = offset (integer)
Sets the shadow offset. In the rendering engines that support it,
drawn shapes will have a soft shadow offseted the given amount on
their bottom-right corner. Defaults to 0 (disabled).
factor = amount (integer)
The factor value specifies the displacement of the active
gradient, i.e. its zoom level. It is only taken into account if
the active fill mode is set to gradient. Defaults to 1.
Standard primitive drawing functions:
func = "circle"
Draws a primitive circle. Requires the additional parameter
"radius", with an integer defining the radius of the circle or
the "auto" value.
func = "square"
Draws a primitive square/rectangle. Requires no additional parameters.
func = "roundedsq"
Draws a square/rectangle with rounded corners. Requires the
additional parameter "radius" defining the radius of the rounded
corners.
func = "bevelsq"
Draws a square/rectangle with beveled borders. This square
ignores the active fill mode, as it is never filled. Requires the
additional parameter "bevel" with the amount of bevel.
func = "line"
Draws a line. If the "size" parameter is specified, the line will
be drawn ranging from the bottom-left corner to the top-right
corner of the defined box. Optionally, you may define the ending
point of the line with the "end" parameter.
func = "triangle"
Draws a triangle. Triangles are always isosceles, meaning they
are drawn inside the square defined by the position and size
values, with the given width as the base of the triangle and the
given height as the height of the triangle.
The optional parameter
orientation = "top|left|right|bottom"
may be specified to define the way in which the triangle is
pointing. Defaults to top.
func = "fill"
This call ignores position and size parameters, as it completely
fills the active drawing surface taken into account the active
fill mode and colors.
*/
namespace GUI { namespace GUI {
class ThemeParser { class ThemeParser {
@ -44,7 +310,7 @@ class ThemeParser {
public: public:
ThemeParser() { ThemeParser() {
_callbacks["drawdata"] = &ThemeParser::parserCallback_DRAWDATA; _callbacks["drawdata"] = &ThemeParser::parserCallback_DRAWDATA;
_callbacks["draw"] = &ThemeParser::parserCallback_DRAW; _callbacks["drawstep"] = &ThemeParser::parserCallback_DRAWSTEP;
} }
~ThemeParser() {} ~ThemeParser() {}
@ -64,7 +330,7 @@ public:
void debug_testEval(); void debug_testEval();
protected: protected:
void parserCallback_DRAW(); void parserCallback_DRAWSTEP();
void parserCallback_DRAWDATA(); void parserCallback_DRAWDATA();
bool parseKeyValue(Common::String keyName); bool parseKeyValue(Common::String keyName);
@ -87,9 +353,19 @@ protected:
while (_text[_pos++]) { while (_text[_pos++]) {
if (_text[_pos - 2] == '*' && _text[_pos - 1] == '/') if (_text[_pos - 2] == '*' && _text[_pos - 1] == '/')
break; break;
if (_text[_pos] == 0)
parserError("Comment has no closure.");
} }
return true; return true;
} }
if (_text[_pos] == '/' && _text[_pos + 1] == '/') {
_pos += 2;
while (_text[_pos] && _text[_pos] != '\n')
_pos++;
return true;
}
return false; return false;
} }
@ -113,9 +389,12 @@ protected:
Common::String _error; Common::String _error;
Common::String _token; Common::String _token;
Common::FixedStack<Common::String, kParserMaxDepth> _activeKey; struct ParserNode {
Common::FixedStack<Common::StringMap, kParserMaxDepth> _keyValues; Common::String name;
Common::StringMap values;
};
Common::FixedStack<ParserNode*, kParserMaxDepth> _activeKey;
Common::HashMap<Common::String, ParserCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _callbacks; Common::HashMap<Common::String, ParserCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _callbacks;
}; };