Skip to content
SamwiseFilmore edited this page Sep 17, 2017 · 17 revisions

Adding new theme to DlangUI application

Read following tutorials before this one:

Theme file

Theme is an .xml resource file of special structure. It's a bit similar to theme files in Android.

Theme ID is name of resource file w/o .xml suffix.

It's a good practice to use theme_ prefix in theme resource files.

Template for custom theme file - resource file theme_my_cool.xml:

<?xml version="1.0" encoding="utf-8"?>
<theme 
     id="theme_my_cool"
     parent="theme_default">

</theme>

element

<theme> is a main element of theme file. It's main attributes: id is theme id, must correspond to resource file name. parent is optional attribute, referencing to parent theme. If parent theme is specified, it will be loaded (included) before loading of this theme content. This behavior allows to override only a part of standard theme.

More attributes may be specified in <theme> element, e.g. default font size and face.

<theme> element may contain <style>, <state>, <drawable>, and <color> child elements.

theme and style elements are similar. theme is just a special case of style.

Difference:

  • Attribute values specified in theme are used as fallback values, if not specified in style.
  • parent attribute of style refers to base style, to use as fallback for attributes not specified in this element.
  • parent attribute of theme refers to parent theme resource, not to parent style.
  • theme may contain style elements, but style cannot contain nested style elements.

Following section is applicable to theme element as well.

style element

Style is a set of properties describing look&feel of widgets or widget parts.

Sample style with id STATIC_TEXT which uses TEXT style as base, and changes margins, padding, alignment and text color.

<style id="STATIC_TEXT"
    parent="TEXT"
    margins="0,0,1,0"
    padding="1,0,1,0"
    align="Left|VCenter"
    textColor="#FF0000"
    >
</style>

id mandatory attribute defines unique id of style. Used by application to assign this style to some widgets.

widget.styleId = "STATIC_TEXT";

parent attribute refers to parent style id. Parent style is used for getting properties which are not specified in this style.

Parent styles must be defined before child styles.

theme acts as a parent for all styles which don't have parent attribute specified.

style attributes

name type description example
id id string unique id for style id="MY_STYLE"
parent id string id of parent style to use as a fallback for properties not specified in current style parent="BUTTON"
backgroundImageId drawable resource id id drawable resource to draw as widget background (includes padding, excludes margins) backgroundImageId="button_bg_blue"
backgroundColor color color to draw as widget background (includes padding, excludes margins) backgroundColor="#FF00FF"
align alignment List of alignment flags (combined with |): Left, Right, Top, Bottom, HCenter, VCenter, Center, TopLeft align="Center"
textColor color color to draw text in widget textColor="#FF00FF"
margins dimensions margins from widget box to parent box (widget background is excluded from margins) margins="10px,5pt,3,3"
padding dimensions padding widget box to content (widget background will include padding) padding="3,4,1em,1.5em"
minWidth dimension minimal width of widget minWidth="300px"
maxWidth dimension maximal width of widget maxWidth="300pt"
minHeight dimension minimal height of widget minHeight="3em"
maxHeight dimension maximal height of widget maxHeight="100"
maxLines integer maximal number of text lines; applicable to some types of widgets maxLines="3"
fontFace font face list of font face to use for displaying text fontFace="Arial;DjVu Sans"
fontFamily font family font family to use for displaying text: SansSerif, Serif, Cursive, Fantasy, MonoSpace, Unspecified fontFamily="MonoSpace"
fontSize dimension size of font for displaying text in widget fontSize="12pt"
fontWeight font weight weight of font - either "bold" or "normal" fontWeight="bold"
layoutWidth layout dimension FILL_PARENT, WRAP_CONTENT or fixed dimension layoutWidth="fill"
layoutHeight layout dimension FILL_PARENT, WRAP_CONTENT or fixed dimension layoutHeight="wrap"
alpha dimension widget transparency (0==opaque, 100%==transparent) alpha="50%"
textFlags text flags text property flags - set of HotKeys, UnderlineHotKeys, UnderlineHotKeysWhenAltPressed, Underline textFlags="Underline"
focusRectColors focus rect colors defines focus rectangle properties focusRectColors="#000"

Style attribute types

id string

Unique id for style; string starting with a..z, may contain digits, hyphens and underscores.

Example: "my_style1"

dimension

Number with optional type suffix (similar to CSS)

sample value description
123px 123 pixels
123 integer 123 (usually same as px)
123pt 12 points (scaled for different DPI)
1.5em 1.5 of line height (scaled based on font size)
10% 10 % of base dimension

dimensions

Comma separated list of 4 dimensions. E.g. "1,2,3,4"

drawable resource id

(1, 2, 3, 4 mean any hex digit)

sample value description
button_background Drawable resource id, e.g. may correspond to file "button_background.9.png"
win_background.tiled Drawable resource id, e.g. may correspond to file "win_background.png" which will be drawn TILED
#123 solid color RGB value
#1234 solid color ARGB value
#112233 solid color RRGGBB value
#11223344 solid color AARRGGBB value
@null No drawable (transparent)
{......} Special inline drawable definition for Console mode

color

sample value description
#123 RGB value
#1234 ARGB value
#112233 RRGGBB value
#11223344 AARRGGBB value
@null transparent color
transparent transparent color
red standard color name: red, green, blue, white, black, gray, lightgray, silver

style child elements

  • state - override some properties for different states
  • color - define or override named color
  • drawable - define or override named drawable (e.g. image or color)
  • length - define or override named dimension value (custom numeric property)

state element

state element redefines part of parent style properties for particular combination of widget state flags.

State element is similar to usual style element, but instead of id and parent attributes it has a set of state selection attributes. These attributes are boolean - can be either "true" or "false".

When widget is looking for state aware properties, it tries to find matching state in current style, and uses property value from it. If property is not overridden in state, or no matching state is found, property value from style element or its parent will be used.

State selectors work like in Android. State attributes may be prefixed with "android:".

State selection attributes correspond to State enum flags.

attribute name description
state_pressed widget is pressed
state_focused widget is focused
state_default widget is default control (e.g. default button)
state_hovered mouse is over widget
state_selected widget is selected (e.g. item in list)
state_checkable widget check state can be changed
state_checked widget is checked (e.g. checkbox button or menu item)
state_enabled widget is enabled (can be focused, changed, pressed, selected, etc...)
state_activated
state_window_focused focused widget is in the same widget group as this widget (or in the same window)

color element

As a child of theme, style or state elements, color element defines or overrides custom named color value.

Samples of using custom colors in code.

// use global color from current theme
_window.backgroundColor = currentTheme.customColor("dialog_background");
// use custom color from widget style
_selectionColorFocused = style.customColor("editor_selection_focused");

drawable element

As a child of theme, style or state elements, drawable element defines or overrides custom named drawable resource.

Sample of getting custom drawable from current style in code:

DrawableRef gaugeDrawable = style.customDrawable("progress_bar_gauge");

length element

As a child of theme, style or state elements, length element defines or overrides custom named numeric or dimension value.

Sample of using custom length:

_buttonOverlap = edit.customLength("overlap", 0);

Adding simple custom theme

Let's create app with custom theme - changed buttons look & feel.

Sample app w/o custom theme

Simple DLangUI project directory structure:

themetest
    dub.json
    src
        app.d
        win_app.def
    views
        resources.list
        res
             theme_custom.xml

dub.json:

{
    "name": "themetest",
    "targetPath": "bin",
    "targetType": "executable",
    "sourceFiles-windows": ["$PACKAGE_DIR/src/win_app.def"],
    "dependencies": {
        "dlangui": "~master"
    }
}

win_app.def:

EXETYPE NT
SUBSYSTEM WINDOWS

app.d:

module app;
import dlangui;
mixin APP_ENTRY_POINT;
/// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) {
    // create window
    Window window = Platform.instance.createWindow("DlangUI example - ThemeTest", null);
    // create some widget to show in window
    window.mainWidget = parseML(q{
        VerticalLayout {
            margins: 10pt
            padding: 10pt
            layoutWidth: fill
            // red bold text with size = 150% of base style size and font face Arial
            TextWidget { text: "Theme test for DlangUI" }
            Button { text: "Sample button 1 (enabled)" }
            Button { text: "Sample button 2 (enabled)" }
            Button { text: "Sample button 3 (disabled)"; enabled: false }
        }
    });
    // show window
    window.show();
    // run message loop
    return Platform.instance.enterMessageLoop();
}

Adding custom theme - stub

Updated directory structure - adding resources in views/ directory:

themetest
    dub.json
    src
        app.d
        win_app.def
    views
        resources.list
        res
             theme_custom.xml

Update dub.json, add stringImportPaths section.

dub.json:

{
    "name": "themetest",
    "targetPath": "bin",
    "targetType": "executable",
    "stringImportPaths": ["views", "views/res"],
    "sourceFiles-windows": ["$PACKAGE_DIR/src/win_app.def"],
    "dependencies": {
        "dlangui": "~master"
    }
}

views/resources.list:

res/theme_custom.xml

Put empty theme stub to theme_custom.xml

Our new theme will have resource id theme_custom and will be based on default theme theme_default

views/res/theme_custom.xml:

<?xml version="1.0" encoding="utf-8"?>
<theme 
    id="theme_custom" 
    parent="theme_default"
    >
</theme>

In app.d, you need to set current theme to theme_custom and register custom application resources.

app.d:

...
/// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) {
    // embed and register app resources listed in file views/resources.list
    embeddedResourceList.addResources(embedResourcesFromList!("resources.list")());
    // load theme from file "theme_custom.xml"
    Platform.instance.uiTheme = "theme_custom";
    // create window
    ...

Redefine BUTTON style in custom theme

Let's redefine style of standard buttons. It will be applied to all widgets which are using BUTTON style.

Add new style element inside of theme element.

Let's describe simple button style with solid color background.

Properties of widget which we will set:

  • id="BUTTON" -- ID of our style; this style exists in parent theme, so it will be redefined (modified)
  • align="Center" -- Align button text at center of button
  • margins="5pt,5pt,5pt,5pt" -- big margins (space between widget background and other controls)
  • padding="5pt,5pt,5pt,5pt" -- big padding (space between widget content and background rect bounds)
  • fontSize="200%" -- font size is twice bigger than standard
  • fontWeight="bold" -- bold
  • backgroundColor="#8080FF" -- light blue background by default

If we need to change some properties depending on widget state, we can use state child elements. Alternative solution - if only background has to be changed - use state drawable as widget background.

We will set different properties for states

  • disabled button -- change text color to half transparent black
  • enabled pressed button -- change background color
  • enabled focused hovered button -- change background color and text color
  • enabled focused -- change background color
  • enabled hovered button -- change text color

Updated theme_custom.xml:

<?xml version="1.0" encoding="utf-8"?>
<theme 
    id="theme_custom" 
    parent="theme_default"
    >
    <style id="BUTTON"
        align="Center"
        margins="5pt,5pt,5pt,5pt"
        padding="5pt,5pt,5pt,5pt"
        fontSize="200%"
        fontWeight="bold"
        backgroundColor="#C0D8FF"
        >
        <state state_enabled="false" textColor="#C0000000"/>
        <state state_pressed="true" backgroundColor="#8080FF"/>
        <state state_focused="true" state_hovered="true" backgroundColor="#C0E0FF" textColor="#FF8000"/>
        <state state_focused="true" backgroundColor="#C0E0FF"/>
        <state state_focused="false" state_hovered="true" textColor="#FF8000"/>
    </style>
</theme>

New style

You can create new style instead of overriding existing.

...
<style id="CUSTOM_BUTTON"
...

And set this style ID to widgets in app.d (DML):

Button { text: "Sample button 1 (enabled)"; styleId: CUSTOM_BUTTON }
Button { text: "Sample button 2 (enabled)"; styleId: CUSTOM_BUTTON }
Button { text: "Sample button 3 (disabled)"; enabled: false; styleId: CUSTOM_BUTTON }

In code, style may be as well assigned using widget styleId property:

myButton.styleId = "CUSTOM_BUTTON";