All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
CSS

Cascading Style Sheets, CSS for short, is a style sheet language which uses simple semantics to describe the appearance of content.

It is commonly used to define the appearance of web pages with HTML and JavaScript.

CSS in Cornerstone

CSS in Cornerstone can be used to define the values of Attributes. Any attribute value can be defined via CSS and not just attributes related to the appearance of widgets. The collection of CSS rules, usually defined in a CSS file, is called the style of a widget.

Cornerstone implements its own CSS parser and CSS engine:

  • Based on CSS 2.1 specification but supports many CSS 3 features
  • Fully supported with both C++ and JavaScript applications
  • Can be used with any kind of widget (standard, derived, and plugin)
  • Can use units (em, px, deg, etc) and relative units (%) as value

CSS-related classes can be found in Stylish namespace, but it is typically not necessary to use these classes directly. Most of the logic is already integrated into the MultiWidgets::Widget class, so usually it is enough to just define some extra CSS selectors for widgets when needed.

We recommend the practice of defining your application structure with C++/JavaScript and then using CSS to define their attributes. This approach has several benefits over defining the attribute values in source code:

  • Flexibility: With an external style sheet file there is no need to rebuild your application every time you want to make a simple change. E.g. change the background color of a widget.
  • Fast prototyping: Style changes are applied on-the-fly when the CSS file is updated. Applications can be re-styled while running. There is no need to restart them to re-apply the style.
  • Extensive: Any attribute can be defined through CSS just like with C++/JavaScript code
  • Reusability: CSS files can be re-used to define the appearance of applications
  • Less code: It is easy to define the attributes for multiple widgets at once. With plain C++ code, we would have to set each of the attributes individually to each widget (or at least create factory methods for doing so).

Prerequisites

A prerequisite for using CSS in Cornerstone is a basic understanding of CSS and its syntax. Many excellent tutorials can be found online, e.g. www.w3schools.com

You can get a basic idea just by reading this document and looking at our examples, but understanding the following basic principles in CSS will be beneficial:

  • CSS syntax
  • CSS selectors (#id, .class, :pseudo-class)
  • Basic declarations (property:value pairs)
  • CSS box model

Applying a Style to an Application

There are two ways to apply a style defined in a CSS file to an application:

  • From code: Application::setStyleFilename("myFile.css")
  • As a command line argument: –css "myFile.css"

Cornerstone CSS engine supports @import tag to split your CSS content into many files if necessary. This might be handy for creating simple reusable style files, e.g. one file for text effects, one file for your widget plugins, etc.

Styling Widgets

All widgets in Cornerstone have the CSS type selector that matches the class name without namespace. For example, MultiWidgets::Widget has a type selector Widget, MultiWidgets::ImageWidget has a type selector ImageWidget, etc.

Below is a simple example of basic style for a widget. It also demonstrates the CSS Box Model that Cornerstone implements.

CSS-box-model-screenshot.png
Styled TextWidget With Borders and Colors

The image above is the result of a single instance of MultiWidgets::TextWidget:

//C++
auto w = MultiWidgets::create<MultiWidgets::TextWidget>("Box Model");

and some CSS applied to it:

//CSS
TextWidget {
background-color: red; /* color as plain text */
color: #000000FF; /* color as html color code */
border-style: solid; /* change style from none to visible */
border-color: 1 1 1 1; /* color as rgba */
border-width: 10px; /* borders extend outside the area defined in size */
padding: 15px; /* paddings can also be defined in all directions, e.g. padding: 5px 5px 10px 15px; */
margin: 10px; /* margin has no visual representation, it describes how the widget should behave in relation to other widgets */
}

In the example above, the style is applied to all TextWidgets in the application.

It is also possible to change any widget attributes using CSS. We can modify the previous example to change some of the TextWidget's attributes using an id selector:

CSS-widget-attributes-screenshot.png
Same Widget With Modified Attributes

In the picture above, we first apply a CSS id to our TextWidget instance in C++ code:

//C++
auto w = MultiWidgets::create<MultiWidgets::TextWidget>("Box Model"); //create widget with initial text
w->setCSSId("MyAttributes"); //attach object with id-selector "MyAttributes"

Then we apply a new set of CSS rules in addition to the previous rules, but this time so that they match only the MyAttributes CSS id:

//CSS
#MyAttributes { /*id selector */
rotation: 25deg;
size: 100px 50px;
input-motion: false;
text: "Rotated Widget" !important; /* use !important-tag to override value set in C++ */
}

Here the rotation, size, input-motion and text are not standard CSS attributes anymore. Instead they reference internal attributes of the MultiWidgets::TextWidget class. The same result could have been done using C++ functions like setRotation(float) etc.

Note that in the last case the !important flag is required, because the value set in C++ code is above the CSS-style file in the Attributes priority layer (See Attribute Layers). This is one of the reasons why it is convienient to keep as much of the attribute initializations in the CSS, instead of hard-coding it into the application.

The !important flag is also required after user has interacted with the widget. User interaction causes the attribute values to be modified in the User layer by C++ code.

CSS Selectors

All widgets in Cornerstone have the CSS type selector that matches the class name without namespace. For example, MultiWidgets::Widget has a type selector Widget, MultiWidgets::ImageWidget has a type selector ImageWidget, etc.

Widget can be a part of many classes but it can have only one id and type. Cornerstone does not enforce ids to be unique, so multiple widgets can have the same id, but this can be considered as a misuse of the selector. In these cases you should use the class selector instead.

Here is a short example of these basic selectors:

TextWidget { /* Type selector affects all TextWidgets */
background-color: red;
}
.MyClass { /* Class selector affects all Widgets in MyClass-class */
background-color: blue;
}
#MyId { /* Id selector affects the Widget with id MyId */
background-color: green;
}

Other CSS 2.1 selectors supported by Cornerstone:

  • Child of selectors (>)
  • Descendant selectors ( )
  • User-defined pseudo-classes (e.g. :active, :loading)
  • Universal selector (*)

Child of selector can be used to reference widgets that have been added to another widget using the Widget::addChild method.

/* selects all TextWidgets that have an ImageWidget as parent */
ImageWidget > TextWidget {
border-color: blue;
}
/* selects all Widgets that belong to a class "WhiteBackground" and are children of a Widget that has id MyWidget */
#MyWidget > .WhiteBackground {
background-color: white;
}

Child of selector only matches widget's direct children, if you want to match to all descendants, including children, grandchildren etc, you can use Descendant selector:

/* Hides all images that are descendants of widget with CSS class 'plain-text' */
.plain-text ImageWidget {
display: none;
}

Pseudo-class selectors can be used to react to some state-changes inside widgets. You can define your own pseudo classes with Stylish::Styleable::setPseudoClass.

/* hides all ImageWidgets while loading */
ImageWidget:loading {
display: none;
}
/* changes border color of MyClass widgets when user is interacting with them */
.MyClass:active {
border-color: yellow;
}

Universal selector is the simplest selector, and mainly used for debugging. Universal selector will match all widgets.

/* Add one pixel red border to all widgets, overriding any normal border rules */
*{
border: 1px solid red !important;
}

Different kind of selectors can be combined:

ImageWidget#HelpIcon:enabled > TextWidget.menu.fixed:active { ... }

Built-in Pseudo-classes

The following CSS pseudo classes are built-in to Cornerstone.

MultiWidgets::Widget

MultiWidgets::BaseMediaWidget (applies to MultiWidgets::ImageWidget, MultiWidgets::VideoWidget, MultiWidgets::ImageMovieWidget):

  • ready Enabled when the widget has the correct size and can render itself
  • header-ready Enabled when the widget has set its size, but can not render itself yet.
  • loading Enabled while the widget is loading media. Disabled after widget is in ready or error state
  • error End-state indicating an error while loading. The widget can not recover from this state.

MultiWidgets::ImageWidget

  • reloading Enabled when the image is being changed. reloading is true while the old image is being displayed and the new image is still being loaded.

MultiWidgets::BrowserWidget

  • error Enabled when there is a problem with the browser backend process. The widget can not recover from this.

Style Updates

Cornerstone automatically re-applies styles when:

  • The stylesheet file is changed
  • Widget hierarchy is changed
  • CSS selector attributes change (type, id, class, pseudo-class)

Cornerstone tracks dependencies between relative attributes using attribute listeners. When an attribute value is changed, any attributes that depend on it are updated.

Examples

You can learn more by checking these following examples: