Writing your navigation in Qt

Hello everyone. I am an android developer with little experience. And today I would like to share the experience of developing an educational project in C ++ and Qt with simple navigation between screens. I would be glad to hear criticism or additions to my solution and I hope that it can help someone and make life easier.





Task

Before we start, let's define two terms so that in the end there is no confusion.

Window is the most common window in windows or any other os.

Screen - some kind of content that can be replaced by others inside the window.





, - Trello, , . ? , .





, , QStackedWidget . - .





. , , . .





, , , :





  1. .





  2. .





  3. .





  4. .





BaseFragment. , . , .





class BaseFragment: public QFrame {
    Q_OBJECT

signals:
    //      
 
public:
    BaseFragment();
    ~BaseFragment();

    //  -   
};
      
      



BaseFragment. , QStackedWidget .





class LoginFragment: public BaseFragment

class StartFragment: public BaseFragment

class RegistrationFragment : public BaseFragment
      
      



, , , . , , .





class BaseScreensFactory {
public:
    BaseScreensFactory();
    ~BaseScreensFactory();

    virtual BaseFragment* create(QString tag);
    virtual QString createStart();
};

      
      



:





  1. create(QString tag) — ( );





  2. createStart() — .





:





// screensfacrory.h  
namespace screens {
    static const QString SPLASH_TAG = "splash";
    static const QString START_TAG = "start";
    static const QString LOGIN_TAG = "login";
    static const QString REGISTRATION_TAG = "registration";
  	//   .....
};

class ScreensFactory: public BaseScreensFactory {
public:
    ScreensFactory();
    ~ScreensFactory();

    BaseFragment* create(QString tag) override;
    QString createStart() override;
};

// screensfacrory.cpp 
BaseFragment* ScreensFactory::create(QString tag) {
    qDebug("ScreensFactory create");
    if (tag == SPLASH_TAG) {
        return new SplashFragment;
    } else if (tag == START_TAG) {
        return new StartFragment;
    } else if (tag == LOGIN_TAG) {
        return new LoginFragment;
    } else if (tag == REGISTRATION_TAG) {
       //   .....
    }
}

QString ScreensFactory::createStart() {
    return SPLASH_TAG; //   .
}
      
      



, , , . QStackedWidget .





:





  • navigateTo(tag) — .





  • back() — .





  • replace(tag) — .





  • newRootScreen(tag) — .





  • navigateToWhithData(tag, data) — , navigateTo(tag), - .





, , , navigateTo(REGISTRATION_TAG), , . newRootScreen(MAIN_TAG).





, , BaseFragment , .





class BaseFragment: public QFrame {
    Q_OBJECT

signals:
  	// 
    void back();
    void navigateTo(QString tag);
    void newRootScreen(QString tag);
    void replace(QString tag);
    
    void navigateWhithData(QString tag, BaseModel* model);
 
public:
    BaseFragment();
    ~BaseFragment();

  	//    
    virtual void onPause();
    virtual void onResume();
    virtual void setData(BaseModel* model);
};
      
      



, , , onPause(), , onResume() , . setData() .





. QStackedWidget BaseScreensFactory .





navigator.h:





class Navigator: public QObject {
    Q_OBJECT
private:
    QStackedWidget *currentContainer;
    BaseScreensFactory *screensFactory;
    QLinkedList<BaseFragment*> stack;

    /**
     * @brief createAndConnect
     * @param tag   .
     *
     *      
     *   .
     *
     * @return     .
     */
    BaseFragment* createAndConnect(QString tag);

    /**
     * @brief connectFragment
     * @param fragment   
     *          .
     *
     *   
     *     
     *     .
     *
     */
    void connectFragment(BaseFragment *fragment);

    /**
     * @brief disconnectFragment
     * @param fragment
     *
     *    .
     */
    void disconnectFragment(BaseFragment *fragment);
public:
    Navigator(
            QStackedWidget *container,
            BaseScreensFactory *screensFactory
    );
    ~Navigator();
    BaseFragment* getStartScreen();

public slots:
    /**
     * @brief navigateTo
     * @param tag   .
     *
     *    .
     */
    void navigateTo(QString tag);

    /**
     * @brief back
     *
     *    .
     */
    void back();

    /**
     * @brief replace
     * @param tag    
     *         .
     *
     *     
     *  .
     */
    void replace(QString tag);

    /**
     * @brief newRootScreen
     * @param tag    
     *         .
     *
     *       
     *   .
     */
    void newRootScreen(QString tag);

    /**
     * @brief navigateWhithData
     * @param model
     *
     *   navigateTo   .
     */
    void navigateWhithData(QString tag, BaseModel* model);
};
      
      



. . . connectFragment , . disconnectFragment . createAndConnect . getStartScreen , , .





BaseFragment* Navigator::getStartScreen() {
    return createAndConnect(this->screensFactory->createStart());
}

void Navigator::connectFragment(BaseFragment *fragment) {
    connect(fragment, &BaseFragment::back, this, &Navigator::back);
    connect(fragment, &BaseFragment::replace, this, &Navigator::replace);
    connect(fragment, &BaseFragment::navigateTo, this, &Navigator::navigateTo);
    connect(fragment, &BaseFragment::newRootScreen, this, &Navigator::newRootScreen);
    connect(fragment, &BaseFragment::navigateWhithData, this, &Navigator::navigateWhithData);
}

void Navigator::disconnectFragment(BaseFragment *fragment) {
    disconnect(fragment, &BaseFragment::back, this, &Navigator::back);
    disconnect(fragment, &BaseFragment::replace, this, &Navigator::replace);
    disconnect(fragment, &BaseFragment::navigateTo, this, &Navigator::navigateTo);
    disconnect(fragment, &BaseFragment::newRootScreen, this, &Navigator::newRootScreen);
    disconnect(fragment, &BaseFragment::navigateWhithData, this, &Navigator::navigateWhithData);
}

BaseFragment* Navigator::createAndConnect(QString tag) {
    BaseFragment *fragment = this->screensFactory->create(tag);
    connectFragment(fragment);
    return fragment;
}
      
      



, .





Navigator::Navigator(
        QStackedWidget *container,
        BaseScreensFactory *screensFactory
) {
    this->screensFactory = screensFactory;
    this->currentContainer = container;
    BaseFragment* startFragment = getStartScreen();
    this->stack.append(startFragment);

    currentContainer->addWidget(stack.last());
    currentContainer->setCurrentIndex(0);
}
      
      



. : navigateTo. , . .





void Navigator::navigateTo(QString tag) {
    BaseFragment *newFragment = this->screensFactory->create(tag);
    stack.last()->onPause();
    disconnectFragment(stack.last());
    connectFragment(newFragment);
    stack.append(newFragment);
    currentContainer->addWidget(newFragment);
    currentContainer->setCurrentWidget(newFragment);
}
      
      



, . , . .





MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{   
    try {
        container = new QStackedWidget;

        this->factory = new ScreensFactory;
        this->navigator = new Navigator(
                    this->container,
                    this->factory
        );

        this->setCentralWidget(container);
    } catch (std::exception& e) {
        qDebug("%s", e.what());
    }
}
      
      



Now we have looked at a large number of pieces of code, at first it may seem that they are not very connected. After a lot of trying to figure it out, this feeling does not disappear, and this is actually a good thing. As a result, we were able to develop various screens of the application completely independently of the rest of the parts. We write the BaseFragment implementation and add it to the factory.





My solution helped me a lot in the implementation of the project. I was able to speed up development and split the code.





Thanks to all who have read, I hope this will help someone.





Here is the GitHub link of the project








All Articles