-
Notifications
You must be signed in to change notification settings - Fork 31
Css language syntax
NUI provides a very flexible way to handle the look of your application in the form of a simple stylesheet language. This langage, while very similar to the CSS language used for web development has been adapted to the needs of a GUI toolkit. This document describes the syntax as well as the features offered.
The CSS is a simple text file containing three different kind of directives: preprocessing, object declarators and widget matching.
You can use #include “xxx.css” to include external CSS files in your main CSS delcaration. This permits to split your CSS code to make it clearer. For example you can have one file for the font declarations and another for the actual widget matching.
Simple line comments works like in C++: anything beyond a double slash is ignored by the parser.
You can describe a widget directly in the CSS:
+nuiLabel MySpecialLabel
{
Text: "Some label text";
}
And then create this widget from C++:
nuiWidget* pWidget = nuiBuilder::Create(_T("MySpecialWidget"));
// ... you can now use pWidget as any other widget.
Of course you are not limited to creating a single widget, you can create a complete widget tree:
+nuiFixed SomeWidgetTree
{
+nuiLabel
{
UserPos: {10,10};
Text: "Some label text";
}
+nuiLabel MySpecialLabel
{
UserPos: {10,50};
Text: "Another Label";
}
}
We also have some special syntax to add cells to the nuiVBox, nuiHBox and nuiGrid:
+nuiGrid OneBigGrid
{
+[0,0] nuiLabel // This is the equivalent of pGrid->SetCell(0,0, new nuiLabel(...));
{
Text: "Some label text";
}
+nuiVBox SomeVerticalBox
{
+nuiLabel { Text: Bleh; } // This is the equivalent of pBox->AddCell(..)
+nuiLabel[0] { Text: SomeOtherLabel; } // SetCell(0, new nuiLabel(...));
}
}
One other thing that you can do is change some parts of these widget creators at run time in C++:
std::map<nglString, nglString> dictionnary;
dictionnary.push_back(std::make_pair(_T("OneBigGrid"), _T("The actual text I want in the label")));
nuiWidget* pWidget = nuiBuilder::Create(_T("MySpecialWidget"), dictionnary);
In this case the widget builder will replace every occurence of “Bleh” with the associated text: “The actual text I want in the label”. This feature works for the values as demonstrated here, but also for the widget types (for exemple you can replace nuiLabel with nuiEditLine) and for the attributes and properties names (associate “Text” with “Comment” for example.
The declatators permit to create named resources in the CSS. These resources can be referenced by the CSS as well as the application code directly. For example, if you create a color with the name “MyPrettyColor”, you can reference it as a color in the CSS or look it up from the simple C++ code @ nuiColor(_T(“MyPrettyColor”)) @.
All declarators start with the @
char, followed by the type of the object (color, font, nuiFrame, etc.) and its name. The names can be enclosed in double quotes if it has to contain spaces: "My pretty color"
.
You can create a global variable that will be stored as nuiObject::SetGlobalProperty(Name, Value) in two ways:
@var Name = Value;
// or the equivalent:
$Name = Value;
// You can also have quoted values:
$MyStuff = "orange, pear, apple";
To retreive these global properties in your code you must use nuiObject::GetGlobalProperty(_T(“Name”));
Here are some typical color declarations:
@color PrettyBlue = rgb(0,0,215);
@color TransparentRed = rgb(255, 0, 0, 128);
@color SomeWebColor = aquamarine;
@color AnotherWebColor = #ff00ff;
@color WebColorWithAlpha = #80ff00ff;
- Line 1 shows a declaration with the keyword rgb followed by the red, green and blue components. The values for the components can go from 0 to 255.
- Line 2 is almost the same as line 1 except it also declares the alpha as a 4th optional component. In this case the alpha is 128.
- Line 3 uses a standard web color name. It can be nice if you know your standard color names.
- Line 4 shows how to use the hexadecimal html color declaration as three pairs of hexadecimal digits. Each pair represents one byte in hexadecimal for each color component.
- Line 5 is almost the same as previous line except it add an alpha component. Beware that in the hexa description, the optional Alpha is the first hexa pair. In this case 80.
Every color declaration must end with a semi colon.
First a simple example:
@Font TinyBoldFont
{
Name: "Arial";
Size: 10;
Bold: true;
Italic: false;
}
The class keyword for creating fonts can be nuiFont or font (the check is not case sensitive).
The first line declares the font TinyBoldFont and is has to be followed by a block enclosed in curly braces, containing the description of the font request.
The description contains a list of requests. Each request has the form of a request type and a value separated by a colon character: Name : "Arial"
. Requests are separated by one @
character per line.
The possible requests are:
- Name : the name of the font.
- GenericName: a generic name. Possible names are sans-serif, serif, cursive, fantasy, monospace. The best matching font will be selected depending on the other requests.
- Style: a font style string. Font styles depends on the font files. Typical ones include italic, bold, etc.
- Face: this is just the index of the face in the font file, in case you want to have a very accurate request for one particular face in one particular font file.
- Size: the display size in pixels.
- Italic: a boolean (true or false).
- Bold: a boolean (true or false).
- Proportionnal: a boolean (true or false).
- Monospace: a boolean (true or false).
- Scalable: a boolean (true or false).
The font request will find the best match for all your requests.
Decorations are a very powerful way to easily decorate widgets. Decorations can be as simple as solid colors applied to the background or foreground, or a very complex setup of dynamic frames, and every thing in between. While every widget can be decorated, you can also set up your widgets to use private decorations to make some of its part controlable from the CSS.
A color decoration is a simple way to decorate a widget by drawing a rectangle over or under it. You can choose one color for the outline and another one for the inside. You can decide to draw the ouline, the inside or both. Finaly you can change the outline size in pixels.
Here are the color decoration attributes:
- ClientRect : the rectangle that defines the inside of the stretchable rect.
- StrokeSize: the size in pixel of the outline.
- FillColor: the color used to draw the inside of the rectangle.
- StrokeColor: the color used to draw the outline of the rectangle.
- ShapeMode: the rendering shape mode. Possible values are: Stroke (outline only), Fill (inside only), StrokeAndFill (inside + outline).
A Border decoration renders a border around the decorated widget. You can change the stroke size, the stretch rectangle, the part of the border you want to draw as well as the rendering style.
Border attributes:
- ClientRect : the rectangle that defines the inside of the stretchable rect.
- StrokeSize: the size in pixel of the outline.
- StrokeColor: the color used to draw the outline of the rectangle. This changes left, top, right and bottom stroke colors at the same time.
- StrokeLeftColor: the color used to draw the outline of the left border.
- StrokeRightColor: the color used to draw the outline of the right border.
- StrokeTopColor: the color used to draw the outline of the top border.
- StrokeBottomColor: the color used to draw the outline of the bottom border.
- Border: select which side of the border to draw, one of: All, Left, Right, Top, Bottom, Horizontal, Vertical, None.
- BorderMode: select the rendering style among Normal, 3D and 3DOut.
TODO
TODO
TODO
The matchers are dynamic descriptions of the widgets we want to apply CSS styling to.
A matcher is described by a list of test, followed by a list of actions enclosed in curly braces. The actions are only applied to the widget if it passes all the tests in the matcher.
The tests start from the end of the line, going back to the begining of the test list as long as the curently tested widget passes the test.
For example consider the following matcher: nuiBox.nuiLabel
. It contains three tests, nuiBox
, .
and nuiLabel
. Matching will proceed as follows:
- If the widget being test is of the nuiLabel class, continue, otherwise abort this matching operation.
- Go to the parent of the currently tested widget. (the
.
test is not really a test per se. It never fails as long as the widget has a non NULL parent). - If the parent of the widget is of class nuiBox then the test and the matcher pass. Otherwise it fails and it is aborted.
If one of the test in a matcher fail, the whole matcher is aborted and we try the next matcher agains the widget.
There are many test you can implement:
A simple symbol name without quotes will test if the widget is of a particular class (set in the C++ code with SetObjectClass).. For exemple, test for all the labels:
nuiLabel
{
// Apply some changes to all labels here
}
A quoted string will test if the widget has the given object name (set in the C++ code with SetObjectName).
"my special label"
{
// Apply some changes to the widgets here
}
Every nuiObject hold a dictionnary that can be used to hold pairs of string. These pairs have no special significance and they can be used to hold any kind of data for the application. The matchers can test if a property contains a particular value with the following syntax:
[ClientName = "meeloo"]
{
// Apply some changes to the widgets here
}
Only the widget containing a property named ClientName with an associated value of “meeloo” will match this test.
You can set properties in the C++ with
nuiObject::SetProperty(...)
. (for example pWidget->SetProperty(_T("ClientName"), _T("meeloo"));
).
Attributes are somewhat similar to properties. Their main diference is that attributes are not just string pairs but a way to access the nuiObject accessor methods without really knowing an object. Attributes have a name and you can get and set them, which will call some particular method to actually change the object. For example, all widgets have an attribute “Alpha” that permits to retreive and change the alpha component of the widget. Here is an example of matching an attribute:
[Hover : true]
{
// Apply some changes to the widgets here
}
This example will only match widgets that are being hovered by the mouse. You can easily see what attributes are stored on each widget by having a look at the DebuggingTools.
Note: you can test multiple attribute at once in the same [ ] block: [Alpha:1 Hover:true Selected:false]. You can even mix attribute tests with property tests: [Selected:true ClientName="meeloo]. Note that the only syntactic difference in between properties and attributes tests is the use of the equal sign =
or the colon :
.
TODO
Just end the matcher line with a !
character and this matcher will only be tested on the widget once, at init time. This permits to ease the work of the CSS engine as it reduce the number of tests it has to do each time the widget tree changes.
Example:
nuiLabel!
{
// Apply some changes to the widgets here
}
When a widget passes all the tests in a matcher the CSS engine applies to it the actions defined in the curly braces following the matcher.
Every action stands on its own line and ends with a semi colon (“;
”).
There are currently two kinds of actions: changing a property and changing an attribute. They both lookalike and only the assignment sign changes:
- properties uses the equal sign (“
=
”). - attributes uses the colon sign (“
:
”).
So here is an example of a set of changes:
SomeWidgetClass
{
ClientName = "some name";
Alpha : 0.5;
Decoration : MyPreDefinedDecoration;
}
In this example for any widget that matches SomeWidgetClass
will see the propery ClientName assigned the string “some name”, the Alpha attribute will be changed to 0.5 and the Decoration will be changed to MyPreDefinedDecoration.
This really is very simple stuff but it quickly becomes very powerful as soon as you start adding attributes to your classes.