Status & Roadmap
Authors & License
Funding U++
Search on this site

SourceForge.net Logo

SourceForge.net Logo

GitHub Logo

Discord Logo

Cameleon Rendering & Creating own chameleonized Ctrl's


Ultimate++ uses a key feature to yield almost identical look on target platforms. It is called 'Chameleon' and was meant to be only a slim layer to resemble target platform look, but passing time has evolved to a rich featured part of Ultimate++. Own custom Controls can be created quite fast, but often lack the proper support for Chameleon technology, rendering an ugly spot in your overall application, when it uses the other Ultimate++ standard controls, which are 'Chameleon aware'. So here is how you can extend / properly create a Ctrl derived Ultimate++ control with chameleon support.


1) Define what you consider Style for your control


In .h file


struct Style : ChStyle<Style> {

    Color paper;

    Color disabled;

    Color focus;

    Color invalid;

    Color text, textdisabled;

    Color selected, selectedtext;

    Value edge[4];

    bool  activeedge;

    int   vfm;



This is maybe the most important task to do. When you go ahead implementing your Ctrl, you should already use the respective properties, where possible, thats why this is important to do as soon as possible in development. Later porting can be pain. Parts considered to be Style of your Control are Colors of parts, think of states, your Ctrl can be in (active, inactive..), general sizes of  bars, margins, etc. Font should *not* be part of it. It should be specified via API (thus later including of your Ctrl in TheIDE can be done easier, provide SetFont() for it)


The ChStyle base enriches your own style with some handling with respect to static Style instances for all Ctrl instances of that type. so do not forget it.


Stick to the naming convention, Style...


2) Setup a default style


It is registered globally in Chameleon database for this special control (thats why the macro needs Ctrl class name, Style class name. It generates the function with name 'StyleDefault''). Use default System color descriptions, where possible, like SColorFace, SColorPaper, to remain consistent to global GUI design. They are derived from current target Platform settings.


In .cpp file

CH_STYLE(EditField, Style, StyleDefault)


    paper = SColorPaper();

    disabled = SColorFace();

    focus = paper;

    invalid = Blend(paper, Color(255, 0, 0), 32);

    text = SColorText();

    textdisabled = SColorDisabled();

    selected = SColorHighlight();

    selectedtext = SColorHighlightText();

    for(int i = 0; i < 4; i++)

        edge[i] = CtrlsImg::EFE();

    activeedge = false;

    vfm = 2;



3) Use a const Style *style to reference


It is the currently used style in your control code. Don't forget to initialize the pointer to your default style. Now, that you have a StyleDefault() function, you can use this one. provide means to override the Style  settings used in your Ctrl (SetStyle). Of course, the referenced Style needs to exist as long as the Ctrl uses it.


In .h file

    const Style *style;

    static const Style& StyleDefault();

    EditField&  SetStyle(const Style& s);


In .cpp file

    style = &StyleDefault(); //ctor


EditField& EditField::SetStyle(const Style& s)


    style = &s;



    return *this;



4) Use your style information to paint your control


Either use it directly, or provide Chameleon helper functions with some of your style info


void EditField::Paint(Draw& w)


    Size sz = GetSize();


    bool enabled = IsShowEnabled();

    Color paper = enabled && !IsReadOnly() ? (HasFocus() ? style->focus : style->paper) : style->disabled;


        paper = Null;

    Color ink = enabled ? style->text : style->textdisabled;




other controls use it like that (ScrollBar)


        if(i != 2 || thumbsize >= style->thumbmin)

            ChPaint(w, pr, l[i][p == i ? CTRL_PRESSED : light == i ? CTRL_HOT : CTRL_NORMAL]);

        if(i != 2)





    if(style->through) {

        ChPaint(w, sz, l[0][CTRL_DISABLED]);



    if(IsHorz()) {

            ChPaint(w, style->arrowsize, 0, sz.cx / 2, sz.cy, l[0][CTRL_DISABLED]);

            ChPaint(w, style->arrowsize + sz.cx / 2, 0, sz.cx - sz.cx / 2, sz.cy, l[1][CTRL_DISABLED]);


        else {

            ChPaint(w, 0, style->arrowsize, sz.cx, sz.cy / 2, l[0][CTRL_DISABLED]);

            ChPaint(w, 0, style->arrowsize + sz.cy / 2, sz.cx, sz.cy - sz.cy / 2, l[1][CTRL_DISABLED]);

...        }


Usage Hints:


Normally, the used Style struct is not alterable (thats why 'const Style *'), you can only replace ist as an entity at once (SetStyle) by a reference to another style. The referenced struct needs to exist as long as the control that's using it, exists as well (logical since it's no copy).


if you want to permanently alter the default Style for *all* controls of that type, you can disable the const lock, to edit the static global instance of your custom control's default style

(or even others if your control supports multiple global styles, see below). You can always make a preinitialisation to a Standard() style, which was defined one time as copy from the first global registered style (i.e. StyleDefault()) for your control. The Standard() preinit saves a lot of code when to alter only few properties. this is also how to restore an altered StyleDefault to its previous state.


    EditField::Style& es = EditField::StyleDefault().Write();


    es = es.Standard();

    es.paper = SColorPaper();

    es.disabled = SColorFace();

    es.focus = Blend(Green(), Black(), 192);


your main application window should update all instantiated controls after finishing updating all desired styles



Some Ctrl's may have more than one global style (i.e. Button). if your Ctrl's also needs more, just use another CH_STYLE macro to provide it. Do not forget to define it in .h just in the same way as StyleDefault.

Thus, your Ctrl can be constructed parametrized or use another default Style (Button::StyleNormal, Button::StyleOk..)


As of chameleonized helper functions, ChPaint is the only one you will likely need. The others are for internal use in the OS theming engine. The beauty and cornerstone of ChPaint and Chameleon is that ChPaint expects Value and can be extended w.r.t. Value types it is able to render. There are two basic types supported directly in Draw: Color and Image. Image has "hotspots" logic to define intelligent scaling, and color is self explaining. You can extend recognized types using ChLookFn. This way, e.g. XP chameleon registers its internal Value type "XpElement" and is able to use XP style rendering system to render Values from Styles. (thanks Mirek)


Use SColorFace for drawing background color, SColorText for text, SColorPaper for controls that really draw some data (graphs, text background in Editfields..) Here is a list of common SColor...derivates:

(i.e. the values used are for a dark look, BLENDIT = 180, should be setup at the beginning in aplication, but it is not used in all Ctrl's in upp.)


    SColorPaper_Write(Blend(White(), Black(), BLENDIT-20));









    SColorDisabled_Write(Blend(White(), Black(), BLENDIT/2));


    SColorFace_Write(Blend(White(), Black(), BLENDIT));










Play around with the Colors to see where they show up or browse the uppsrc :)


Chameleon is not that complicated, the problem is that the underlying code is complicated so it's difficult to work out documentation.



Do you want to contribute?