Leveraging shadow maps in Qt, without using QOpenGLFrameBufferObject

A few years ago I was attempting to use shadow maps in Qt/OpenGL but was simply unable to use QOpenGLFrameBufferObject as a shadow-buffer. I had even posted a question about it on stack-overflow here. It turns out, you cannot use QOpenGLFrameBufferObject for creating shadow buffers. So, I had to resort to native OpenGL calls. Recently someone reached to me on LinkedIn asking me if I could share the code.

So here it is: https://github.com/pnudupa/codesamples/tree/master/bike_shadows

When the code at this repository is compiled and executed, we can see a window with two bikes, with their shadows on the ground and on each other.

You can press any key to rotate the whole scene and watch the shadows move.

QObjectFactory

Factory Design Pattern is a well known pattern. Its a way to organise code so that class-instances can be created at run time against a developer defined key. Factories specialise in create objects of a specific type. For example (yes, this example is inspired from the example on the Wikipedia page about Factory Design Patterns)

#define interface class
#define implements : public

interface IPerson
{
public:
    virtual std::string getType() = 0;
};

class VillagePerson implements IPerson
{
public:
    virtual std::string getType() { return "VillagePerson"; }
};

class TownPerson implements IPerson
{
public:
    virtual std::string getType() { return "TownPerson"; }
};

class CityPerson implements IPerson
{
public:
    virtual std::string getType() { return "CityPerson"; }
};

class PersonFactory
{
public:
    IPerson *createPerson(const std::string &type) {
        if(type == "VillagePerson")
            return new VillagePerson;
        if(type == "TownPerson")
            return new TownPerson;
        if(type == "CityPerson")
            return new CityPerson;
        return nullptr;
    }
};

So, we can now create instances of IPerson using the PersonFactory class. For example:

PersonFactory factory;
IPerson *person = factory.createPerson("CityPerson");

Now person will point to an instance of CityPerson class. Thats the whole point of a factory, to create object instances based on a key. In this case the key was type name as string.

Qt uses Factory Design Pattern in several places. For example the QItemEditorFactory class helps create editor widgets for items in a QAbstractItemView. Over there the type is integer. There is also a QStyleFactory that creates instances of QStyle based on style-name as key.

Almost every software project that I have been a part of makes use of the factory design pattern. Almost all of the factory classes I have written create QObject subclasses based on a QString or QByteArray key. Typically the key is name of the QObject subclass, but its not always the case. After one writes enough number of factories, it becomes boring to write the same thing over and over again. So I came up with a simple solution.

QObjectFactory class

Lets take the same example as before. Suppose we wanted to create a Person factory class. Lets also suppose that we can make peace with the fact that objects created by our factory will be QObject subclasses. So the person interface and subclasses would look like this

class AbstractPerson : public QObject
{
    Q_OBJECT

public:
    AbstractPerson(QObject *parent=nullptr)
        : QObject(parent) { }
    ~AbstractPerson() { }

    virtual QByteArray getType() const = 0;
};

class VillagePerson : public AbstractPerson
{
    Q_OBJECT

public:
    Q_INVOKABLE VillagePerson(QObject *parent=nullptr)
        : AbstractPerson(parent) { }
    ~VillagePerson() { }

    virtual QByteArray getType() const {
        return "VillagePerson";
    }
};

class TownPerson : public AbstractPerson
{
    Q_OBJECT

public:
    Q_INVOKABLE TownPerson(QObject *parent=nullptr)
        : AbstractPerson(parent) { }
    ~TownPerson() { }

    virtual QByteArray getType() const {
        return "VillagePerson";
    }
};

Notice the following

  • AbstractPerson is subclassed from QObject
  • AbstractPerson and all its subclasses have Q_OBJECT macro declared in them
  • AbstractPerson and all its subclasses have a constructor that accepts a parent pointer.
  • Subclasses of AbstractPerson have their constructor marked as Q_INVOKABLE

Now, lets register subclasses of AbstractPerson in a factory.

QObjectFactory factory;
factory.addClass<VillagePerson>();
factory.addClass<TownPerson>();
factory.addClass<CityPerson>();

That’s it. Creation of instances from the factory would be as simple as

AbstractPerson *person = factory.create<AbstractPerson>("CityPerson");

Seems magical right? Let’s look at how the QObjectFactory class is written.

class QObjectFactory
{
public:
    QObjectFactory() { }
    ~QObjectFactory() { }

    void add(const QMetaObject *mo) { m_metaObjects += mo; }
    void remove(const QMetaObject *mo) { m_metaObjects -= mo; }

    template <class T>
    void addClass() { this->add( &T::staticMetaObject ); }

    template <class T>
    void removeClass() { this->remove( &T::staticMetaObject ); }

    const QMetaObject *find(const QByteArray &className) const {
        Q_FOREACH(const QMetaObject *mo, m_metaObjects) {
            if( !qstrcmp(className.data(), mo->className()) )
                return mo;
        }
        return nullptr;
    }

    QObject *create(const QByteArray &className, QObject *parent=nullptr) const {
        const QMetaObject *mo = this->find(className);
        if(mo == nullptr)
            return nullptr;
        QObject *obj = mo->newInstance(Q_ARG(QObject*,parent));
        return obj;
    }

    template <class T>
    T *create(const QByteArray &className, QObject *parent=nullptr) const {
        QObject *obj = this->create(className, parent);
        return qobject_cast<T*>(obj);
    }

private:
    QSet<const QMetaObject*> m_metaObjects;
};

Qt’s QMetaObject class provides a newInstance() method that helps us call the constructor of its QObject class using a transparent API. By using some really simple template constructs, we make the QObjectFactory class look magical.

Can we use QObjectFactory for creating widgets?

QWidget and subclasses accept a QWidget* as parent pointer, instead of QObject*. So, the QObjectFactory class in its current incarnation cannot be used as a QWidget factory. We will need to write a QWidgetFactory class, that does pretty much the same thing, just that the parent pointer will be of type QWidget*. Or, we can do something smarter.

template <class T>
class QtFactory
{
public:
    QtFactory() { }
    ~QtFactory() { }

    void add(const QMetaObject *mo) { m_metaObjects += mo; }
    void remove(const QMetaObject *mo) { m_metaObjects -= mo; }

    template <class Class>
    void addClass() { this->add( &Class::staticMetaObject ); }

    template <class Class>
    void removeClass() { this->remove( &Class::staticMetaObject ); }

    const QMetaObject *find(const QByteArray &className) const {
        Q_FOREACH(const QMetaObject *mo, m_metaObjects) {
            if( !qstrcmp(className.data(), mo->className()) )
                return mo;
        }
        return nullptr;
    }

    T *create(const QByteArray &className, T *parent=nullptr) const {
        const QMetaObject *mo = this->find(className);
        if(mo == nullptr)
            return nullptr;

        const char *t = this->type();
        QObject *obj = qobject_cast<T*>(mo->newInstance( QArgument<T*>(t,parent) ));
        return qobject_cast<T*>(obj);
    }

    template <class Class>
    Class *create(const QByteArray &className, T *parent=nullptr) const {
        T *obj = this->create(className, parent);
        return qobject_cast<Class*>(obj);
    }

private:
    const char *type() const {
        static const char *qobjectstar = "QObject*";
        static const char *qwidgetstar = "QWidget*";
        if( typeid(T) == typeid(QWidget) )
            return qwidgetstar;
        return qobjectstar;
    }

private:
    QSet<const QMetaObject*> m_metaObjects;
};

typedef QtFactory<QObject> QObjectFactory;
typedef QtFactory<QWidget> QWidgetFactory;

Notice how QtFactory is now a generic template class and we create QObjectFactory and QWidgetFactory from it. Now we can use QWidgetFactory as follows

class LineEdit : public QLineEdit
{
    Q_OBJECT

public:
    Q_INVOKABLE LineEdit(QWidget *parent=nullptr)
        : QLineEdit(parent) { }
    ~LineEdit() { }
};

class PushButton : public QPushButton
{
    Q_OBJECT

public:
    Q_INVOKABLE PushButton(QWidget *parent=nullptr)
        : QPushButton(parent) {
        this->setText("Button");
    }
    ~PushButton() { }
};

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    QWidgetFactory factory;
    factory.addClass<LineEdit>();
    factory.addClass<PushButton>();

    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);

    layout->addWidget(factory.create("LineEdit", &window));
    layout->addWidget(factory.create("PushButton", &window));

    window.show();

    return a.exec();
}

How about key as something other than class name?

So far we are using class-name as the lookup key in the factory class. What if we wanted to use as key something other class-names? For example, we may want a widget-factory that helps us create widgets based on the type of data we want to edit using it. We can make use of Q_CLASSINFO() to add additional data about the widget class into its QMetaObject as follows.

class LineEdit : public QLineEdit
{
    Q_OBJECT
    Q_CLASSINFO("DataTypes", "QString;QUrl")

public:
    Q_INVOKABLE LineEdit(QWidget *parent=nullptr)
        : QLineEdit(parent) { }
    ~LineEdit() { }
};

class CheckBox : public QCheckBox
{
    Q_OBJECT
    Q_CLASSINFO("DataTypes", "bool")

public:
    Q_INVOKABLE CheckBox(QWidget *parent=nullptr)
        : QCheckBox(parent) {
        this->setText("Yes/No");
    }
    ~CheckBox() { }
};

Now, we update the QtFactory class to lookup based on the semi-colon separated list in value to DataTypes Q_CLASSINFO.

template <class T>
class QtFactory
{
public:
    QtFactory(const QByteArray &key=QByteArray()) : m_classInfoKey(key) { }
    ~QtFactory() { }

    QByteArray classInfoKey() const { return m_classInfoKey; }

    void add(const QMetaObject *mo) {
        QList<QByteArray> keys;
        const int ciIndex = mo->indexOfClassInfo(m_classInfoKey.constData());
        if(ciIndex >= 0) {
            const QMetaClassInfo ci = mo->classInfo(ciIndex);
            keys = QByteArray(ci.value()).split(';');
        }
        keys.append( QByteArray(mo->className()) );
        m_metaObjects.insert(mo, keys);
        Q_FOREACH(QByteArray key, keys)
            m_keyMap[key].append(mo);
    }

    void remove(const QMetaObject *mo) {
        const QList<QByteArray> keys = m_metaObjects.take(mo);
        Q_FOREACH(QByteArray key, keys) {
            QList<const QMetaObject *> mos = m_keyMap.take(key);
            mos.removeOne(mo);
            if(mos.isEmpty())
                continue;
            m_keyMap[key] = mos;
        }
    }

    // ...

    QList<QByteArray> keys() const { return m_keyMap.keys(); }

    const QMetaObject *find(const QByteArray &val) const {
        const QList<const QMetaObject *> mos = m_keyMap.value(val);
        return mos.isEmpty() ? nullptr : mos.last();
    }

    // ...

private:
    QMap<const QMetaObject*, QList<QByteArray> > m_metaObjects;
    QMap<QByteArray, QList<const QMetaObject *> > m_keyMap;
    QByteArray m_classInfoKey;
};

Notice how the factory class now uses both class-names and Q_CLASSINFO data to lookup which class to create an instance of.

Now we can use the QWidgetFactory class as follows.

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    QWidgetFactory factory("DataTypes");
    factory.addClass<LineEdit>();
    factory.addClass<CheckBox>();

    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);

    layout->addWidget(factory.create("QString", &window));
    layout->addWidget(factory.create("QUrl", &window));
    layout->addWidget(factory.create("bool", &window));

    window.show();

    return a.exec();
}

The entire widget factory is just a header file. You can expand this block to view/copy the entire code.

QtFactory Source Code
/****************************************************************************
**
** Copyright (C) Prashanth Udupa, Bengaluru
** Email: prashanth.udupa@gmail.com
**
** This code is distributed under LGPL v3. Complete text of the license
** can be found here: https://www.gnu.org/licenses/lgpl-3.0.en.html
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#ifndef QOBJECTFACTORY_H
#define QOBJECTFACTORY_H

#include <QSet>
#include <QMap>
#include <QWidget>
#include <QObject>
#include <QMetaObject>
#include <QMetaClassInfo>

template <class T>
class QtFactory
{
public:
    QtFactory(const QByteArray &key=QByteArray()) : m_classInfoKey(key) { }
    ~QtFactory() { }

    QByteArray classInfoKey() const { return m_classInfoKey; }

    void add(const QMetaObject *mo) {
        QList<QByteArray> keys;
        const int ciIndex = mo->indexOfClassInfo(m_classInfoKey.constData());
        if(ciIndex >= 0) {
            const QMetaClassInfo ci = mo->classInfo(ciIndex);
            keys = QByteArray(ci.value()).split(';');
        }
        keys.append( QByteArray(mo->className()) );
        m_metaObjects.insert(mo, keys);
        Q_FOREACH(QByteArray key, keys)
            m_keyMap[key].append(mo);
    }
    void remove(const QMetaObject *mo) {
        const QList<QByteArray> keys = m_metaObjects.take(mo);
        Q_FOREACH(QByteArray key, keys) {
            QList<const QMetaObject *> mos = m_keyMap.take(key);
            mos.removeOne(mo);
            if(mos.isEmpty())
                continue;
            m_keyMap[key] = mos;
        }
    }

    template <class Class>
    void addClass() { this->add( &Class::staticMetaObject ); }

    template <class Class>
    void removeClass() { this->remove( &Class::staticMetaObject ); }

    QList<QByteArray> keys() const { return m_keyMap.keys(); }

    const QMetaObject *find(const QByteArray &val) const {
        const QList<const QMetaObject *> mos = m_keyMap.value(val);
        return mos.isEmpty() ? nullptr : mos.last();
    }

    T *create(const QByteArray &className, T *parent=nullptr) const {
        const QMetaObject *mo = this->find(className);
        if(mo == nullptr)
            return nullptr;
        const char *t = this->type();
        QObject *obj = qobject_cast<T*>(mo->newInstance( QArgument<T*>(t,parent) ));
        return qobject_cast<T*>(obj);
    }

    template <class Class>
    Class *create(const QByteArray &className, T *parent=nullptr) const {
        T *obj = this->create(className, parent);
        return qobject_cast<Class*>(obj);
    }

private:
    const char *type() const {
        static const char *qobjectstar = "QObject*";
        static const char *qwidgetstar = "QWidget*";
        if( typeid(T) == typeid(QWidget) )
            return qwidgetstar;
        return qobjectstar;
    }

private:
    QMap<const QMetaObject*, QList<QByteArray> > m_metaObjects;
    QMap<QByteArray, QList<const QMetaObject *> > m_keyMap;
    QByteArray m_classInfoKey;
};

typedef QtFactory<QObject> QObjectFactory;
typedef QtFactory<QWidget> QWidgetFactory;

#endif // QOBJECTFACTORY_H

Anchor Layout for Widgets

I love QML. It’s a software developer’s ticket to build truly amazing user interfaces while at the same time not compromising on software architecture. Its simply fantastic!

One of the features of QML UI that I love the most is Anchor Layouts. Coming from the widgets world I knew QVBoxLayout, QHBoxLayout, QGridLayout and QFormLayout. These layouts help place and resize contents of a widget in a predefined fashion. They were the perfect set of layouting tools until I discovered Anchor Layouts. Now, whenever I have to go back to writing widgets UI; I feel that lack of Anchor Layouts in Widgets world more than ever. I have gotten used to placing one UI element to the right-of, left-of, above, below of another UI element and so on. I like being able to say that this UI element should be as wide as the other element, but placed on top. You can’t do that with widgets as straight-forwardly as you can do it in QML. So, I decided to build an AnchorLayout class for use with QtWidgets. This blog post is all about that.

For the uninformed: what is an Anchor Layout?

Qt’s layout classes QBoxLayout, QGridLayout and QFormLayout distributes space available in a container widget among its child widgets using some logic. Anchor Layouts on the other hand think of a container and its contents as a UI elements with each of them having invisible anchor lines as follows

We then setup UI elements such that one UI element’s anchor-line is “anchored” to another UI element’s anchor-line. Of-course, this works as long as the UI elements in question are either siblings OR share a container-contained relationship.

In addition to being able to “anchor” lines on each other, we can also establish a space between them, by specifying a margin. When we anchor one item on another, we can in addition to that also say that we need a margin. That way we can introduce space between UI elements.

For example: we could say, anchor the left of blue UI element to the right of a red UI element.

We could further say that while we want the left of the blue UI element to stand to the right of the red UI element, we want a gap of 10 pixels between them.

The way we think anchor-layouts is distinct from the way we think QLayouts. When we think “anchor-layouts” we think pretty much like how we would think when we arrange objects on our desk. Place one item to the right of the other, top of the other OR place this item to the extreme right of the table, that one to the extreme left and so on.

Anchor Layouts are more natural to think than traditional QLayouts. That is not to say that traditional layouts are bad. Having an option to use both is better.

I have implemented an AnchorLayout class that offers this functionality. If you just want to download and use the code, git-pull a copy of the code from here: https://github.com/pnudupa/anchorlayout

Simple Example

First let’s look at the results. Suppose I wanted to place a QFrame within a QWidget such that it would occupy the entire container, by leaving behind some margin.

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    QWidget container;

    QFrame *frame = new QFrame(&container);
    frame->setFrameStyle(QFrame::Box);

    AnchorLayout *containerLayout = AnchorLayout::get(&container);
    AnchorLayout *frameLayout = AnchorLayout::get(frame);
    frameLayout->fill(containerLayout)->setMargins(50);

    container.resize(400, 400);
    container.show();

    return a.exec();
}

When executed, we will get an output like this.

Surely this can be accomplished using a QVBoxLayout or QHBoxLayout as well. We are not competing with existing layouts. Only trying to bring the power of Anchor Layouts in QML to the QtWidgets world.

Show the frame in the bottom right quadrant

Suppose we wanted to show the frame in the bottom right quadrant of the container leaving a 20 pixel margin around, like this.

This is of course possible with QGridLayout, but somehow getting the placement right will be an issue. We will have to play around with sizeHint(), sizePolicy() of the frame effectively to achieve this result. You see, we want the frame to be in the bottom right quadrant, leaving a 20 pixel margin around even when the widget is resized.

With the AnchorLayout class, it is as simple as this. Notice how we borrow the anchor-lines concept from QML into this.

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    QWidget container;

    QFrame *frame = new QFrame(&container);
    frame->setFrameStyle(QFrame::Box);

    AnchorLayout *containerLayout = AnchorLayout::get(&container);
    AnchorLayout *frameLayout = AnchorLayout::get(frame);

    frameLayout->left()
               ->anchorTo(containerLayout->horizontalCenter())
               ->setMargin(20);
    frameLayout->top()
               ->anchorTo(containerLayout->verticalCenter())
               ->setMargin(20);
    frameLayout->right()
               ->anchorTo(containerLayout->right())
               ->setMargin(20);
    frameLayout->bottom()
               ->anchorTo(containerLayout->bottom())
               ->setMargin(20);

    container.resize(400, 400);
    container.show();

    return a.exec();
}

Each AnchorLayout instance has left(), right(), top(), bottom(), horizontalCenter() and verticalCenter() AnchorLines. These lines can be made to coincide with AnchorLines of other layouts.

Placing widgets in a container is simply a matter of anchoring their anchor-lines of their anchor-layouts with each other. As the anchor lines move, they take the edges of the widgets they represent with them. This way the pull-and-push of anchor lines effectively places and resizes the widgets.

Place two frames in the center, separated by a space

Suppose wanted to place two frames next to each other, in the center of a container such that

  • The width of each frame is 35% of the width of the container
  • The height of each frame is 50% of the height of the container

Like this:

Once again, the idea is to ensure that width and height ratios vis-a-vis the container must remain consistent when the container is resized.

Here is how we would use the AnchorLayout class to make this happen.

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    QWidget container;

    QLabel *frame1 = new QLabel(&container);
    frame1->setFrameStyle(QFrame::Box);
    frame1->setText("One");
    frame1->setAlignment(Qt::AlignCenter);

    QLabel *frame2 = new QLabel(&container);
    frame2->setFrameStyle(QFrame::Box);
    frame2->setText("Two");
    frame2->setAlignment(Qt::AlignCenter);

    AnchorLayout *containerLayout = AnchorLayout::get(&container);

    AnchorLayout *frame1Layout = AnchorLayout::get(frame1);
    frame1Layout->left()
                ->anchorTo(containerLayout->customLine(Qt::Vertical,0.15))
                ->setMargin(-5);
    frame1Layout->right()
                ->anchorTo(containerLayout->horizontalCenter())
                ->setMargin(5);
    frame1Layout->top()
                ->anchorTo(containerLayout->customLine(Qt::Horizontal,0.25));
    frame1Layout->bottom()
                ->anchorTo(containerLayout->customLine(Qt::Horizontal,-0.25));

    AnchorLayout *frame2Layout = AnchorLayout::get(frame2);
    frame2Layout->right()
                ->anchorTo(containerLayout->customLine(Qt::Vertical,-0.15))
                ->setMargin(-5);
    frame2Layout->left()
                ->anchorTo(containerLayout->horizontalCenter())
                ->setMargin(5);
    frame2Layout->top()
                ->anchorTo(containerLayout->customLine(Qt::Horizontal,0.25));
    frame2Layout->bottom()
                ->anchorTo(containerLayout->customLine(Qt::Horizontal,-0.25));

    container.resize(600, 400);
    container.show();

    return a.exec();
}

Notice the use of customLine() method. In addition to left(), top(), right(), bottom(), horizontalCenter() and verticalCenter() anchor lines, we can create custom anchor lines. Anchor Lines are either horizontal or vertical lines. They show up at a particular distance from the layout’s edge. For example

  • A Qt::HorizontalLine at 0.25 distance is a horizontal line that is 25% of the width from the left of the layout.
  • A Qt::HorizontalLine at -0.25 distance is a horizontal line that is 25% of the width from the right of the layout

Like all AnchorLines, custom AnchorLines can be used to anchor another another anchor-line on it as long as the orientations match. Also note how we are able to easily introduce space in-between the frames using margins.

We want one one of the frames to stick to the top, the other to the bottom.

Suppose want to achieve this.

Notice that “one” and “two” are taller than before. They are actually 75% as tall as the container. While frame one is aligned to the top, frame two is aligned to the bottom. But note also the margin and spacing. The code for accomplishing this result is almost similar to what we have written before. I have highlighted the changes.

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    QWidget container;

    QLabel *frame1 = new QLabel(&container);
    // ....

    QLabel *frame2 = new QLabel(&container);
    // ....

    AnchorLayout *containerLayout = AnchorLayout::get(&container);

    AnchorLayout *frame1Layout = AnchorLayout::get(frame1);
    // ....    
    frame1Layout->top()
                ->anchorTo(containerLayout->top())
                ->setMargin(10);
    frame1Layout->bottom()
                ->anchorTo(containerLayout->customLine(Qt::Horizontal,-0.25))
                ->setMargin(-10);

    AnchorLayout *frame2Layout = AnchorLayout::get(frame2);
    // ....
    frame2Layout->top()
                ->anchorTo(containerLayout->customLine(Qt::Horizontal,0.25))
                ->setMargin(-10);
    frame2Layout->bottom()
                ->anchorTo(containerLayout->bottom())
                ->setMargin(10);

    // ....
}

Accomplishing something like this with traditional layouts, though possible, would be quite tricky. You have to play around with stretch factors, alignments, size hints and policies. Anchor Layout just makes all this very straight forward.

Typical Application UI

A typical application UI consists of a central document area, surrounded by other widgets.

Although a main-window does indeed provide us with this construct, lets look at how we can construct such a layout by ourselves using AnchorsLayout.

Firstly, let us spell out the ratio, proportions and fixed sizes of each area

  • The menu-bar area is always along the top, occupying the full width of the window. It has a fixed height of 30 pixels.
  • The status-bar-area is always along the bottom, occupying the full width of the window. It also has a fixed height of 30 pixels.
  • The tool-box and config-box areas are to the left and right; occupying roughly 20% of the window width.
  • The remaining space is occupied by the document-area.
int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    auto createFrame = [](const QString &text, QWidget *parent) {
        QLabel *label = new QLabel(parent);
        label->setFrameStyle(QFrame::Box);
        label->setText(text);
        label->setAlignment(Qt::AlignCenter);
        return label;
    };

    QWidget container;
    QWidget *documentArea = createFrame("document-area", &container);
    QWidget *menuBarArea = createFrame("menu-bar-area", &container);
    QWidget *toolBoxArea = createFrame("tool-box-area", &container);
    QWidget *configBoxArea = createFrame("config-box-area", &container);
    QWidget *statusBarArea = createFrame("status-bar-area", &container);

    AnchorLayout *containerLayout = AnchorLayout::get(&container);

    AnchorLayout *menuBarLayout = AnchorLayout::get(menuBarArea);
    menuBarLayout->left()
                ->anchorTo(containerLayout->left());
    menuBarLayout->right()
                ->anchorTo(containerLayout->right());
    menuBarLayout->top()
                ->anchorTo(containerLayout->top());
    menuBarArea->setFixedHeight(30);

    AnchorLayout *statusBarLayout = AnchorLayout::get(statusBarArea);
    statusBarLayout->left()
                ->anchorTo(containerLayout->left());
    statusBarLayout->right()
                ->anchorTo(containerLayout->right());
    statusBarLayout->bottom()
                ->anchorTo(containerLayout->bottom());
    statusBarArea->setFixedHeight(30);

    AnchorLayout *configBoxLayout = AnchorLayout::get(configBoxArea);
    configBoxLayout->right()
                ->anchorTo(containerLayout->right());
    configBoxLayout->top()
                ->anchorTo(menuBarLayout->bottom())
                ->setMargin(2);
    configBoxLayout->bottom()
                ->anchorTo(statusBarLayout->top())
                ->setMargin(2);
    configBoxLayout->left()
                ->anchorTo(containerLayout->customLine(Qt::Vertical,-0.2));

    AnchorLayout *toolBoxLayout = AnchorLayout::get(toolBoxArea);
    toolBoxLayout->left()
                ->anchorTo(containerLayout->left());
    toolBoxLayout->top()
                ->anchorTo(menuBarLayout->bottom())
                ->setMargin(2);
    toolBoxLayout->bottom()
                ->anchorTo(statusBarLayout->top())
                ->setMargin(2);
    toolBoxLayout->right()
                ->anchorTo(containerLayout->customLine(Qt::Vertical,0.2));

    AnchorLayout *documentAreaLayout = AnchorLayout::get(documentArea);
    documentAreaLayout->left()
                ->anchorTo(toolBoxLayout->right())
                ->setMargin(2);
    documentAreaLayout->top()
                ->anchorTo(toolBoxLayout->top());
    documentAreaLayout->right()
                ->anchorTo(configBoxLayout->left())
                ->setMargin(2);
    documentAreaLayout->bottom()
                ->anchorTo(configBoxLayout->bottom());

    container.resize(600, 400);
    container.show();

    return a.exec();
}

Notice how we are able to easily place areas relative to each other. Notice also how we are able to use setFixedHeight() to specify some hardcoded sizes. Once we have sorted out the placement of areas, we can now go ahead and place widgets within them using traditional QLayouts or using AnchorLayouts.

AnchorLayout does not inherit QLayout

The AnchorLayout class I have implemented does not inherit QLayout. It makes use of event-filters to look up resize events and recalculates placement of anchor-lines. Each anchor-line then cascades its placement to other anchor-lines connected to it. Recursively the entire widget UI is placed and sized. This also means that we can use an AnchorLayout on top of existing layouts. This is especially useful when you want to stack a widget on top of another widget, which may already have a layout, but still want to have control over its placement.

Example: Let’s take Qt’s QMainWindow sample application.

Now, lets say we want to hover a calendar widget on top of the main-window such that it comes exactly above the status-bar area, but horizontally centered. Like this:

Existing main-window layouts do not provide us any facility to accommodate temporary widgets like this. But with AnchorLayout, we can do something like this

MainWindow::MainWindow(const CustomSizeHintMap &customSizeHints,
                       QWidget *parent, Qt::WindowFlags flags)
    : QMainWindow(parent, flags)
{
    // ......

    AnchorLayout *mainWindowLayout = AnchorLayout::get(this);
    AnchorLayout *statusBarLayout = AnchorLayout::get(this->statusBar());

    QCalendarWidget *calendarWidget = new QCalendarWidget(this);
    calendarWidget->setGridVisible(true);

    AnchorLayout *calendarLayout = AnchorLayout::get(calendarWidget);

    calendarLayout->bottom()
            ->anchorTo(statusBarLayout->top())
            ->setMargin(1);
    calendarLayout->horizontalCenter()
            ->anchorTo(mainWindowLayout->horizontalCenter());
    calendarWidget->setFixedSize(350, 200);
}

What if we wanted for the calendar widget to slightly overlap the status bar area, such that the bottom edge of the calendar goes all the way to 50% of the status-bar height?

We just need to make a really small code change.

    calendarLayout->bottom()
            ->anchorTo(statusBarLayout->verticalCenter());

In this example, we are showing a calendar widget. But in a real world application, it is perfectly possible that whenever we select a file to open, we want to show a temporary widget near the status bar like this which shows load-progress. We want for this temporary widget to be visible within the boundary of the main-window; but placed appropriately.

Surely there are plenty of real-world use cases for using an Anchor Layout in Widgets UI. If you want to take the Anchor Layout for a spin, please pull a copy of the sample code from here: https://github.com/pnudupa/anchorlayout.git