Newer
Older
Import / research / MVC / ModelViewController.cpp
@John Ryland John Ryland on 30 Dec 2020 3 KB add mvc sample app
/*

  MVC base classes
  Qt Based Implementation
  Copyright (C) 2020, John Ryland

*/
#include "ModelViewController.hpp"
#include <QtUiTools>


namespace
{
    // Based on: QMetaObject::connectSlotsByName
    // Searches rootObject and all it's descendants for signals, and connects them to any slots in receiverObject where the signatures match
    // the pattern  on_<senderName>_<signalName>
    void connectSlotsByName(QObject* rootObject, const QObject* receiverObject)
    {
        const QMetaObject* slotInfo = receiverObject->metaObject();
        if (slotInfo && rootObject)
        {
            const QObjectList list = rootObject->findChildren<QObject*>(QString()) << rootObject; // look for signals in rootObject and all it's descendants 
            // for each method/slot of mo ...
            for (int i = 0; i < slotInfo->methodCount(); ++i)
            {
                const QByteArray slotSignature = slotInfo->method(i).methodSignature();
                const char* slot = slotSignature.constData();
                // ...that starts with "on_", ...
                if (slot[0] == 'o' && slot[1] == 'n' && slot[2] == '_')
                {
                    // ...we check each object in our list, ...
                    for (int j = 0; j < list.count(); ++j)
                    {
                        const QObject* signalObject = list.at(j);
                        const QByteArray coName = signalObject->objectName().toLatin1();
                        // ...discarding those whose objectName is not fitting the pattern "on_<objectName>_...", ...
                        if (coName.isEmpty() || qstrncmp(slot + 3, coName.constData(), coName.size()) || slot[coName.size() + 3] != '_')
                            continue;
                        std::string signal = slot + coName.size() + 4;
                        // we attempt to connect it...
                        rootObject->connect(signalObject, ("2" + signal).c_str(), receiverObject, (std::string("1") + slot).c_str());
                    }
                }
            }
        }
    }

    class LayoutWrapper : public QVBoxLayout
    {
    public:
        LayoutWrapper(QWidget* parent, QWidget* view)
            : QVBoxLayout(parent)
        {
            addWidget(view);              // view is now a child of parent
            parent->resize(view->size()); // resize parent to the size of view
        }
    };
}


ModelBase::ModelBase()
{
    setObjectName("model");
}


ViewBase::ViewBase(const char* uiFileName)
{
    setObjectName("view");
    new LayoutWrapper(this, QUiLoader{}.load(&QFile(uiFileName)));
}


ControllerBase::ControllerBase(ModelBase* model, ViewBase* view)
    : _model(model)
    , _view(view)
    , _controller(this)
{
    setObjectName("controller");
    new LayoutWrapper(this, view);    // view is now a child of controller
    view->layout()->addWidget(model); // model is now a child of view
}


void ControllerBase::start()
{
    ::connectSlotsByName(this, _model);
    ::connectSlotsByName(this, _view);
    ::connectSlotsByName(this, _controller);
    show();
}