├── .gitignore
├── test
├── qrouter.qrc
├── pages
│ ├── customdata.h
│ ├── testpage1.h
│ ├── testpage2.h
│ ├── testpage1.cpp
│ ├── testpage2.cpp
│ ├── page1.ui
│ └── page2.ui
├── main.cpp
├── qroutertest.h
├── CMakeLists.txt
├── qrouter.ui
└── qroutertest.cpp
├── src
├── QRouterConfig.cmake.in
├── container
│ ├── stackroutercontainer.cpp
│ └── layoutroutercontainer.cpp
├── qrouter.cpp
├── CMakeLists.txt
├── abstractrouterwidget.cpp
└── routerstackmanager.cpp
├── CMakeLists.txt
├── include
├── qrouterpageevent.h
├── qrouterexception.h
├── container
│ ├── stackroutercontainer.h
│ └── layoutroutercontainer.h
├── abstractroutercontainer.h
├── abstractrouterwidget.h
├── qrouter.h
└── routerstackmanager.h
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | cmake-build*
--------------------------------------------------------------------------------
/test/qrouter.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/QRouterConfig.cmake.in:
--------------------------------------------------------------------------------
1 | @PACKAGE_INIT@
2 |
3 | include(CMakeFindDependencyMacro)
4 | @_find_dependency_calls@
5 |
6 | include("${CMAKE_CURRENT_LIST_DIR}/QRouterTargets.cmake")
--------------------------------------------------------------------------------
/test/pages/customdata.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | struct CustomData {
6 | qint64 sendTime;
7 | QString message;
8 | int pageStackId;
9 | };
10 |
11 | Q_DECLARE_METATYPE(CustomData);
--------------------------------------------------------------------------------
/test/main.cpp:
--------------------------------------------------------------------------------
1 | #include "qroutertest.h"
2 | #include
3 |
4 | int main(int argc, char *argv[])
5 | {
6 | QApplication a(argc, argv);
7 | QRouterTest w;
8 | w.show();
9 | return a.exec();
10 | }
11 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.12)
2 |
3 | project(qrouter)
4 |
5 | add_subdirectory(src)
6 |
7 | option(QROUTER_BUILD_TEST "enable the test project of qrouter" OFF)
8 |
9 | if (QROUTER_BUILD_TEST)
10 | add_subdirectory(test)
11 | endif()
--------------------------------------------------------------------------------
/include/qrouterpageevent.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class QRouterPageEvent : public QEvent {
6 | public:
7 | QRouterPageEvent(QString event, QVariant data)
8 | : QEvent(type)
9 | , event(std::move(event))
10 | , data(std::move(data))
11 | {}
12 |
13 | static QEvent::Type type;
14 |
15 | QString event;
16 | QVariant data;
17 | };
--------------------------------------------------------------------------------
/include/qrouterexception.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class QRouterRuntimeException : public QException {
6 | public:
7 | explicit QRouterRuntimeException(QString message)
8 | : message(std::move(message))
9 | {}
10 |
11 | void raise() const override { throw *this; }
12 | [[nodiscard]] QRouterRuntimeException* clone() const override { return new QRouterRuntimeException(*this); }
13 |
14 | QString message;
15 | };
--------------------------------------------------------------------------------
/test/qroutertest.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "ui_qrouter.h"
5 |
6 | class QRouterTest : public QWidget
7 | {
8 | Q_OBJECT
9 |
10 | public:
11 | QRouterTest(QWidget *parent = Q_NULLPTR);
12 |
13 | private:
14 | Ui::QRouterClass ui;
15 |
16 | private:
17 | void printPageStack();
18 |
19 | protected:
20 | bool event(QEvent* event) override;
21 | bool eventFilter(QObject *watched, QEvent *event) override;
22 | };
23 |
--------------------------------------------------------------------------------
/test/pages/testpage1.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "abstractrouterwidget.h"
4 |
5 | #include
6 |
7 | class TestPage1 : public AbstractRouterWidget {
8 | Q_OBJECT
9 |
10 | public:
11 | Q_INVOKABLE explicit TestPage1(const QVariant& data, QWidget* parent = nullptr);
12 |
13 | void onNavigateResult(const QVariant& data) override;
14 |
15 | private:
16 | Ui::TestPage1 ui;
17 |
18 | int pageStackId;
19 |
20 | static int typeId;
21 |
22 | protected:
23 | void runRouterEvent(const QString& event, const QVariant& data) override;
24 | };
25 |
26 | Q_DECLARE_METATYPE(TestPage1*);
--------------------------------------------------------------------------------
/include/container/stackroutercontainer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../abstractroutercontainer.h"
6 | #include "../abstractrouterwidget.h"
7 |
8 | class StackRouterContainer : public AbstractRouterContainer {
9 | public:
10 | explicit StackRouterContainer(QStackedWidget* stackedWidget);
11 |
12 | QWidget* childPageParent() override;
13 |
14 | QObject * rootPageEventReceiver() override;
15 |
16 | void setCurrentWidget(AbstractRouterWidget *widget) override;
17 |
18 | void removeWidget(AbstractRouterWidget *widget) override;
19 |
20 | private:
21 | QStackedWidget* container;
22 | };
--------------------------------------------------------------------------------
/test/pages/testpage2.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "abstractrouterwidget.h"
4 |
5 | #include
6 |
7 | class TestPage2 : public AbstractRouterWidget {
8 | Q_OBJECT
9 |
10 | public:
11 | Q_INVOKABLE explicit TestPage2(const QVariant& data, QWidget* parent = nullptr);
12 |
13 | bool attemptClose() override;
14 |
15 | QVariant readAttemptCloseData() override;
16 |
17 | private:
18 | Ui::TestPage2 ui;
19 |
20 | int pageStackId;
21 |
22 | static int typeId;
23 |
24 | protected:
25 | void runRouterEvent(const QString& event, const QVariant& data) override;
26 |
27 | private:
28 | void popWithData();
29 | };
30 |
31 | Q_DECLARE_METATYPE(TestPage2*);
--------------------------------------------------------------------------------
/include/container/layoutroutercontainer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../abstractroutercontainer.h"
6 | #include "../abstractrouterwidget.h"
7 |
8 | class LayoutRouterContainer : public AbstractRouterContainer {
9 | public:
10 | explicit LayoutRouterContainer(QBoxLayout* boxLayout);
11 |
12 | QWidget* childPageParent() override;
13 |
14 | QObject * rootPageEventReceiver() override;
15 |
16 | void setCurrentWidget(AbstractRouterWidget *widget) override;
17 |
18 | void removeWidget(AbstractRouterWidget* widget) override;
19 |
20 | virtual void removeAllWidget();
21 |
22 | private:
23 | QBoxLayout* container;
24 | QList pageInstances;
25 | };
--------------------------------------------------------------------------------
/src/container/stackroutercontainer.cpp:
--------------------------------------------------------------------------------
1 | #include "container/stackroutercontainer.h"
2 |
3 | StackRouterContainer::StackRouterContainer(QStackedWidget *stackedWidget)
4 | : AbstractRouterContainer(stackedWidget)
5 | , container(stackedWidget)
6 | {}
7 |
8 | QWidget *StackRouterContainer::childPageParent() {
9 | return container;
10 | }
11 |
12 | QObject *StackRouterContainer::rootPageEventReceiver() {
13 | return container->parent();
14 | }
15 |
16 | void StackRouterContainer::setCurrentWidget(AbstractRouterWidget *widget) {
17 | if (container->indexOf(widget) == -1) {
18 | container->addWidget(widget);
19 | }
20 | container->setCurrentWidget(widget);
21 | }
22 |
23 | void StackRouterContainer::removeWidget(AbstractRouterWidget *widget) {
24 | container->removeWidget(widget);
25 | }
--------------------------------------------------------------------------------
/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.12)
2 | project(qroutertest)
3 |
4 | set(CMAKE_CXX_STANDARD 14)
5 | set(CMAKE_AUTOMOC ON)
6 | set(CMAKE_AUTOUIC ON)
7 | set(CMAKE_AUTORCC ON)
8 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
9 |
10 | find_package(QT NAMES Qt6 Qt5 REQUIRED)
11 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Widgets REQUIRED)
12 |
13 | add_executable(${PROJECT_NAME} WIN32
14 | main.cpp
15 | qrouter.qrc
16 | qrouter.ui
17 | qroutertest.h
18 | qroutertest.cpp
19 |
20 | pages/customdata.h
21 | pages/page1.ui
22 | pages/testpage1.h
23 | pages/testpage1.cpp
24 | pages/page2.ui
25 | pages/testpage2.h
26 | pages/testpage2.cpp
27 | )
28 |
29 | target_link_libraries(${PROJECT_NAME}
30 | Qt${QT_VERSION_MAJOR}::Core
31 | Qt${QT_VERSION_MAJOR}::Gui
32 | Qt${QT_VERSION_MAJOR}::Widgets
33 | qrouter
34 | )
--------------------------------------------------------------------------------
/include/abstractroutercontainer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class AbstractRouterWidget;
6 | class AbstractRouterContainer : public QObject {
7 | public:
8 | explicit AbstractRouterContainer(QWidget* container): QObject(container) {}
9 |
10 | /**
11 | * get all child page of parent widget
12 | * @return
13 | */
14 | virtual QWidget* childPageParent() = 0;
15 |
16 | /**
17 | * set event send target for 'postEventToRoot'
18 | * @return
19 | */
20 | virtual QObject* rootPageEventReceiver() = 0;
21 |
22 | /**
23 | * set current display page widget
24 | * @param widget
25 | */
26 | virtual void setCurrentWidget(AbstractRouterWidget* widget) = 0;
27 |
28 | /**
29 | * remove page widget from container
30 | * @param widget
31 | */
32 | virtual void removeWidget(AbstractRouterWidget* widget) = 0;
33 | };
--------------------------------------------------------------------------------
/include/abstractrouterwidget.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class AbstractRouterWidget : public QWidget {
7 | public:
8 | explicit AbstractRouterWidget(const QVariant& data, QWidget* parent);
9 |
10 | virtual void onNavigateResult(const QVariant& data);
11 |
12 | virtual bool attemptClose();
13 |
14 | virtual QVariant readAttemptCloseData();
15 |
16 | QVariant onRouterEvent(const QString& event, const QVariant& data);
17 |
18 | virtual bool singletonInstance();
19 |
20 | private:
21 | QVariant navigateData;
22 | bool firstShow;
23 |
24 | protected:
25 | QVariant getNavigateData(bool clear = true);
26 |
27 | virtual void runRouterEvent(const QString& event, const QVariant& data);
28 |
29 | virtual QVariant executeRouterEvent(const QString& event, const QVariant& data);
30 |
31 | void showEvent(QShowEvent* event) override;
32 | void resizeEvent(QResizeEvent* event) override;
33 | void hideEvent(QHideEvent* event) override;
34 |
35 | bool event(QEvent* event) override;
36 |
37 | virtual void onViewLoad();
38 | virtual void onReshow();
39 | virtual void onHidden();
40 | virtual void onResizeWindow();
41 | };
--------------------------------------------------------------------------------
/src/qrouter.cpp:
--------------------------------------------------------------------------------
1 | #include "qrouter.h"
2 |
3 | QRouter QRouter::router;
4 |
5 | QRouter::QRouter()
6 | {}
7 |
8 | QRouter &QRouter::install(AbstractRouterContainer *container, int contextId) {
9 | RouterStackManager manager;
10 | manager.container = container;
11 | router.managers.insert(contextId, manager);
12 | return router;
13 | }
14 |
15 | QRouter& QRouter::install(QStackedWidget* stackContainer, int contextId) {
16 | return install(new StackRouterContainer(stackContainer), contextId);
17 | }
18 |
19 | QRouter &QRouter::install(QBoxLayout *boxLayout, int contextId) {
20 | return install(new LayoutRouterContainer(boxLayout), contextId);
21 | }
22 |
23 | RouterStackManager& QRouter::of(int contextId) {
24 | if (!router.managers.contains(contextId)) {
25 | throw QRouterRuntimeException(QString("cannot find container with context id:%1, the container may not be installed.").arg(contextId));
26 | }
27 | return router.managers[contextId];
28 | }
29 |
30 | int QRouter::getIdByContainer(QWidget *container) {
31 | for (auto i = router.managers.begin(); i != router.managers.end(); ++i) {
32 | if (i.value().container->parent() == container) {
33 | return i.key();
34 | }
35 | }
36 | return -1;
37 | }
--------------------------------------------------------------------------------
/src/container/layoutroutercontainer.cpp:
--------------------------------------------------------------------------------
1 | #include "container/layoutroutercontainer.h"
2 |
3 | LayoutRouterContainer::LayoutRouterContainer(QBoxLayout *boxLayout)
4 | : AbstractRouterContainer(boxLayout->parentWidget())
5 | , container(boxLayout)
6 | {}
7 |
8 | QWidget *LayoutRouterContainer::childPageParent() {
9 | return container->parentWidget();
10 | }
11 |
12 | QObject *LayoutRouterContainer::rootPageEventReceiver() {
13 | return container->parentWidget();
14 | }
15 |
16 | void LayoutRouterContainer::setCurrentWidget(AbstractRouterWidget *widget) {
17 | if (!pageInstances.contains(widget)) {
18 | pageInstances.append(widget);
19 | }
20 | removeAllWidget();
21 | widget->setVisible(true);
22 | container->addWidget(widget);
23 | }
24 |
25 | void LayoutRouterContainer::removeWidget(AbstractRouterWidget *widget) {
26 | if (pageInstances.contains(widget)) {
27 | pageInstances.removeOne(widget);
28 | }
29 | if (auto item = container->itemAt(0)) {
30 | if (item->widget() == widget) {
31 | removeAllWidget();
32 | }
33 | }
34 | }
35 |
36 | void LayoutRouterContainer::removeAllWidget() {
37 | if (auto item = container->takeAt(0)) {
38 | item->widget()->setVisible(false);
39 | delete item;
40 | }
41 | }
--------------------------------------------------------------------------------
/test/pages/testpage1.cpp:
--------------------------------------------------------------------------------
1 | #include "testpage1.h"
2 |
3 | #include "qrouter.h"
4 |
5 | #include "customdata.h"
6 |
7 | #include
8 |
9 | int TestPage1::typeId = qRegisterMetaType();
10 |
11 | TestPage1::TestPage1(const QVariant& data, QWidget* parent)
12 | : AbstractRouterWidget(data, parent)
13 | {
14 | ui.setupUi(this);
15 |
16 | pageStackId = data.toInt();
17 |
18 | connect(ui.btn_push, &QPushButton::clicked, [&] {
19 | CustomData data;
20 | data.sendTime = QDateTime::currentMSecsSinceEpoch();
21 | data.message = "send data from page1 to page2";
22 | data.pageStackId = pageStackId;
23 | QRouter::of(pageStackId).push("TestPage2", QVariant::fromValue(data));
24 | });
25 | }
26 |
27 | void TestPage1::onNavigateResult(const QVariant& data) {
28 |
29 | auto value = data.value();
30 |
31 | ui.data_from_pop->setText(
32 | QString("from %1 to %2, msg: <%3>")
33 | .arg(QDateTime::fromMSecsSinceEpoch(value.sendTime).toString("HH:mm:ss.zzz"))
34 | .arg(QDateTime::currentDateTime().toString("HH:mm:ss.zzz"))
35 | .arg(value.message)
36 | );
37 | }
38 |
39 | void TestPage1::runRouterEvent(const QString& event, const QVariant& data) {
40 | if (event == "eventtest") {
41 | QRouter::of(pageStackId).postEventToRoot("printevent", "test page1 receive event!");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.12)
2 |
3 | project(qrouter VERSION 0.0.8)
4 |
5 | set(CMAKE_AUTOMOC ON)
6 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
7 |
8 | find_package(QT NAMES Qt6 Qt5 REQUIRED)
9 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Widgets REQUIRED)
10 |
11 | set(CMAKE_DEBUG_POSTFIX "d")
12 |
13 | add_library(${PROJECT_NAME}
14 | abstractrouterwidget.cpp
15 | qrouter.cpp
16 | routerstackmanager.cpp
17 |
18 | container/layoutroutercontainer.cpp
19 | container/stackroutercontainer.cpp
20 | )
21 |
22 | target_link_libraries(${PROJECT_NAME}
23 | Qt${QT_VERSION_MAJOR}::Core
24 | Qt${QT_VERSION_MAJOR}::Gui
25 | Qt${QT_VERSION_MAJOR}::Widgets
26 | )
27 |
28 | target_include_directories(${PROJECT_NAME} PUBLIC
29 | $
30 | $
31 | )
32 |
33 | include(CMakePackageConfigHelpers)
34 |
35 | install(TARGETS ${PROJECT_NAME}
36 | EXPORT QRouterTargets
37 | LIBRARY DESTINATION lib
38 | ARCHIVE DESTINATION lib
39 | RUNTIME DESTINATION bin
40 | INCLUDES DESTINATION include
41 | )
42 |
43 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/
44 | DESTINATION include
45 | FILES_MATCHING PATTERN "*.h"
46 | )
47 |
48 | configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/QRouterConfig.cmake.in
49 | "${CMAKE_CURRENT_BINARY_DIR}/QRouterConfig.cmake"
50 | INSTALL_DESTINATION lib/cmake/QRouter
51 | )
52 |
53 | install(FILES
54 | "${CMAKE_CURRENT_BINARY_DIR}/QRouterConfig.cmake"
55 | DESTINATION lib/cmake/QRouter
56 | )
57 |
58 | install(EXPORT QRouterTargets
59 | FILE QRouterTargets.cmake
60 | DESTINATION lib/cmake/QRouter
61 | )
--------------------------------------------------------------------------------
/test/pages/testpage2.cpp:
--------------------------------------------------------------------------------
1 | #include "testpage2.h"
2 |
3 | #include "qrouter.h"
4 |
5 | #include "customdata.h"
6 |
7 | #include
8 | #include
9 |
10 | int TestPage2::typeId = qRegisterMetaType();
11 |
12 | TestPage2::TestPage2(const QVariant& data, QWidget* parent)
13 | : AbstractRouterWidget(data, parent) {
14 | ui.setupUi(this);
15 |
16 | auto value = data.value();
17 | pageStackId = value.pageStackId;
18 |
19 | ui.data_from_last_page->setText(
20 | QString("from %1 to %2, msg: <%3>")
21 | .arg(QDateTime::fromMSecsSinceEpoch(value.sendTime).toString("HH:mm:ss.zzz"))
22 | .arg(QDateTime::currentDateTime().toString("HH:mm:ss.zzz"))
23 | .arg(value.message)
24 | );
25 |
26 | connect(ui.btn_pop, &QPushButton::clicked, this, &TestPage2::popWithData);
27 | }
28 |
29 | bool TestPage2::attemptClose() {
30 | if (ui.block_pop->isChecked()) {
31 | QMessageBox::warning(0, "warning", "page close blocked!");
32 | return false;
33 | }
34 | return true;
35 | }
36 |
37 | QVariant TestPage2::readAttemptCloseData() {
38 | CustomData data;
39 | data.sendTime = QDateTime::currentMSecsSinceEpoch();
40 | data.message = "close page2";
41 | return QVariant::fromValue(data);
42 | }
43 |
44 | void TestPage2::runRouterEvent(const QString& event, const QVariant& data) {
45 | if (event == "eventtest") {
46 | QRouter::of(pageStackId).postEventToRoot("printevent", "test page2 receive event!");
47 | }
48 | }
49 |
50 | void TestPage2::popWithData() {
51 | if (ui.pop_data_extra->isChecked()) {
52 | auto d = readAttemptCloseData().value();
53 | d.message += " with extra";
54 | QRouter::of(pageStackId).pop(QVariant::fromValue(d));
55 | } else {
56 | QRouter::of(pageStackId).pop();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/test/pages/page1.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | TestPage1
4 |
5 |
6 |
7 | 0
8 | 0
9 | 799
10 | 433
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 | this is page1
21 |
22 |
23 |
24 | -
25 |
26 |
27 | test send value page to page
28 |
29 |
30 |
31 | -
32 |
33 |
34 | push with data
35 |
36 |
37 |
38 | -
39 |
40 |
-
41 |
42 |
43 | receive from pop page:
44 |
45 |
46 |
47 | -
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 | Qt::Vertical
60 |
61 |
62 |
63 | 20
64 | 327
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/abstractrouterwidget.cpp:
--------------------------------------------------------------------------------
1 | #include "abstractrouterwidget.h"
2 |
3 | #include "qrouter.h"
4 |
5 | AbstractRouterWidget::AbstractRouterWidget(const QVariant& data, QWidget* parent)
6 | : QWidget(parent)
7 | , navigateData(data)
8 | , firstShow(true)
9 | {}
10 |
11 | void AbstractRouterWidget::onNavigateResult(const QVariant&) {
12 | }
13 |
14 | bool AbstractRouterWidget::attemptClose() {
15 | return true;
16 | }
17 |
18 | QVariant AbstractRouterWidget::readAttemptCloseData() {
19 | return QVariant();
20 | }
21 |
22 | QVariant AbstractRouterWidget::onRouterEvent(const QString& event, const QVariant& data) {
23 | runRouterEvent(event, data);
24 |
25 | return executeRouterEvent(event, data);
26 | }
27 |
28 | bool AbstractRouterWidget::singletonInstance() {
29 | return false;
30 | }
31 |
32 | QVariant AbstractRouterWidget::getNavigateData(bool clear) {
33 | auto data = navigateData;
34 | if (clear) {
35 | navigateData = QVariant();
36 | }
37 | return data;
38 | }
39 |
40 | void AbstractRouterWidget::runRouterEvent(const QString&, const QVariant&) {
41 | }
42 |
43 | QVariant AbstractRouterWidget::executeRouterEvent(const QString&, const QVariant&) {
44 | return QVariant();
45 | }
46 |
47 | void AbstractRouterWidget::showEvent(QShowEvent*) {
48 | if (firstShow) {
49 | onViewLoad();
50 | firstShow = false;
51 | } else {
52 | onReshow();
53 | }
54 | }
55 |
56 | void AbstractRouterWidget::resizeEvent(QResizeEvent*) {
57 | onResizeWindow();
58 | }
59 |
60 | void AbstractRouterWidget::hideEvent(QHideEvent*) {
61 | onHidden();
62 | }
63 |
64 | bool AbstractRouterWidget::event(QEvent* event) {
65 | if (event->type() == QRouterPageEvent::type) {
66 | auto e = static_cast(event);
67 | runRouterEvent(e->event, e->data);
68 | return true;
69 | }
70 | return QWidget::event(event);
71 | }
72 |
73 |
74 | void AbstractRouterWidget::onViewLoad() {
75 | }
76 |
77 | void AbstractRouterWidget::onReshow() {
78 | }
79 |
80 | void AbstractRouterWidget::onHidden() {
81 | }
82 |
83 | void AbstractRouterWidget::onResizeWindow() {
84 | }
85 |
--------------------------------------------------------------------------------
/include/qrouter.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "qrouterexception.h"
7 | #include "qrouterpageevent.h"
8 | #include "routerstackmanager.h"
9 |
10 | #include "container/stackroutercontainer.h"
11 | #include "container/layoutroutercontainer.h"
12 |
13 | class QRouter {
14 | public:
15 | /**
16 | * install a page stack container for 'contextId'
17 | * @param container page stack container
18 | * @param contextId page stack id
19 | * @return
20 | */
21 | static QRouter& install(AbstractRouterContainer* container, int contextId = 0);
22 |
23 | /**
24 | * Use the specified 'stackedWidget' to manage page stack
25 | * @param stackContainer page stack container
26 | * @param contextId page stack id
27 | * @return
28 | */
29 | static QRouter& install(QStackedWidget* stackContainer, int contextId = 0);
30 |
31 | /**
32 | * Use the specified 'layout' to manage page stack
33 | * @param boxLayout page stack container
34 | * @param contextId page stack id
35 | * @return
36 | */
37 | static QRouter& install(QBoxLayout* boxLayout, int contextId = 0);
38 |
39 | /**
40 | * Get page stack manager by id
41 | * @param contextId
42 | * @return
43 | */
44 | static RouterStackManager& of(int contextId = 0);
45 |
46 | /**
47 | * Get current top page instance
48 | * @tparam T instance type
49 | * @param contextId page id
50 | * @return
51 | */
52 | template
53 | static T* currentInstance(int contextId = 0) {
54 | return dynamic_cast(QRouter::of(contextId).current());
55 | }
56 |
57 | /**
58 | * Get instance by name in current page stack, return null if not exist
59 | * @tparam T instance type
60 | * @param contextId page id
61 | * @return
62 | */
63 | template
64 | static T* getInstance(int contextId = 0) {
65 | return dynamic_cast(QRouter::of(contextId).getInstanceFromStack(T::staticMetaObject.className()));
66 | }
67 |
68 | /**
69 | * Get page stack id of the specified container
70 | * @param container container instance
71 | * @return -1 if not register by 'install'
72 | */
73 | static int getIdByContainer(QWidget* container);
74 |
75 | private:
76 | QRouter();
77 |
78 | private:
79 | static QRouter router;
80 |
81 | QHash managers;
82 | };
--------------------------------------------------------------------------------
/test/pages/page2.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | TestPage2
4 |
5 |
6 |
7 | 0
8 | 0
9 | 728
10 | 417
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 | this is page2
21 |
22 |
23 |
24 | -
25 |
26 |
-
27 |
28 |
29 | receive from last page data:
30 |
31 |
32 |
33 | -
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | -
43 |
44 |
-
45 |
46 |
47 | pop with data
48 |
49 |
50 |
51 | -
52 |
53 |
54 | block pop
55 |
56 |
57 |
58 | -
59 |
60 |
61 | pop data extra
62 |
63 |
64 |
65 | -
66 |
67 |
68 | Qt::Horizontal
69 |
70 |
71 |
72 | 40
73 | 20
74 |
75 |
76 |
77 |
78 |
79 |
80 | -
81 |
82 |
83 | Qt::Vertical
84 |
85 |
86 |
87 | 20
88 | 40
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/test/qrouter.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | QRouterClass
4 |
5 |
6 |
7 | 0
8 | 0
9 | 1064
10 | 958
11 |
12 |
13 |
14 | QRouter
15 |
16 |
17 | -
18 |
19 |
-
20 |
21 |
22 | send event to page1
23 |
24 |
25 |
26 | -
27 |
28 |
29 | send event to page current
30 |
31 |
32 |
33 | -
34 |
35 |
36 | send event to all page
37 |
38 |
39 |
40 | -
41 |
42 |
43 | post method
44 |
45 |
46 |
47 | -
48 |
49 |
50 | Qt::Horizontal
51 |
52 |
53 |
54 | 40
55 | 20
56 |
57 |
58 |
59 |
60 |
61 |
62 | -
63 |
64 |
65 | stack widget:
66 |
67 |
68 |
69 | -
70 |
71 |
72 | QStackedWidget{border:1px solid red;}
73 |
74 |
75 |
76 | -
77 |
78 |
79 | widget:
80 |
81 |
82 |
83 | -
84 |
85 |
86 | #widget{
87 | border:1px solid red;
88 | }
89 |
90 |
91 |
92 |
93 | -
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/test/qroutertest.cpp:
--------------------------------------------------------------------------------
1 | #include "qroutertest.h"
2 |
3 | #include "qrouter.h"
4 |
5 | #include "pages/customdata.h"
6 |
7 | #include "pages/testpage1.h"
8 |
9 | #include
10 | #include
11 |
12 | QRouterTest::QRouterTest(QWidget *parent)
13 | : QWidget(parent)
14 | {
15 | ui.setupUi(this);
16 | ui.widget->installEventFilter(this);
17 |
18 | QRouter::install(ui.stackedWidget, 0);
19 | QRouter::install(ui.container_layout, 1);
20 |
21 | connect(ui.stackedWidget, &QStackedWidget::currentChanged, this, &QRouterTest::printPageStack);
22 |
23 | QRouter::of(0).push("TestPage1", 0);
24 | QRouter::of(1).push("TestPage1", 1);
25 |
26 | //test send event
27 | connect(ui.btn_send1, &QPushButton::clicked, [&] {
28 | if (ui.send_by_post->isChecked()) {
29 | auto ptr = QRouter::getInstance();
30 | Q_ASSERT(ptr != nullptr);
31 | QRouter::of(0).postEventTo("TestPage1", "eventtest");
32 | QRouter::of(1).postEventTo("TestPage1", "eventtest");
33 | } else {
34 | QRouter::of(0).sendEventTo("TestPage1", "eventtest");
35 | QRouter::of(1).sendEventTo("TestPage1", "eventtest");
36 | }
37 | });
38 |
39 | connect(ui.btn_send_cur, &QPushButton::clicked, [&] {
40 | if (ui.send_by_post->isChecked()) {
41 | QRouter::of(0).postEventCur("eventtest");
42 | QRouter::of(1).postEventCur("eventtest");
43 | } else {
44 | QRouter::of(0).sendEventCur("eventtest");
45 | QRouter::of(1).sendEventCur("eventtest");
46 | }
47 | });
48 |
49 | connect(ui.btn_send_all, &QPushButton::clicked, [&] {
50 | if (ui.send_by_post->isChecked()) {
51 | QRouter::of(0).postEventAll("eventtest");
52 | QRouter::of(1).postEventAll("eventtest");
53 | } else {
54 | QRouter::of(0).sendEventAll("eventtest");
55 | QRouter::of(1).sendEventAll("eventtest");
56 | }
57 | });
58 | }
59 |
60 | void QRouterTest::printPageStack() {
61 | QString stackString;
62 | QDebug(&stackString) << QRouter::of().readStack();
63 |
64 | ui.log_view->append(QString("[%1] page stack: %2\n")
65 | .arg(QDateTime::currentDateTime().toString("HH:mm:ss.zzz"), stackString));
66 | }
67 |
68 | bool QRouterTest::event(QEvent* event) {
69 | if (event->type() == QRouterPageEvent::type) {
70 | ui.log_view->append(QString("[%1] receive router event: %2\n")
71 | .arg(QDateTime::currentDateTime().toString("HH:mm:ss.zzz"),
72 | dynamic_cast(event)->data.toString()));
73 | return true;
74 | }
75 | return QWidget::event(event);
76 | }
77 |
78 | bool QRouterTest::eventFilter(QObject *watched, QEvent *event) {
79 | if (watched == ui.widget) {
80 | if (event->type() == QRouterPageEvent::type) {
81 | ui.log_view->append(QString("[%1][layout base] receive router event: %2\n")
82 | .arg(QDateTime::currentDateTime().toString("HH:mm:ss.zzz"),
83 | dynamic_cast(event)->data.toString()));
84 | return true;
85 | }
86 | }
87 | return QObject::eventFilter(watched, event);
88 | }
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # qrouter
2 | 这是一个使用QStackedWidget进行子页面切换的路由框架,基本原理基于qt的反射实现解耦合,使用QList保存页面栈,页面之间通信通过QRouter转发
3 |
4 | ## 如何使用
5 | ### 1. 编写一个page类继承于AbstractRouterWidget,并使这个类可反射
6 | ```cpp
7 | //YourPage.h
8 | class YourPage : public AbstractRouterWidget {
9 | Q_OBJECT
10 |
11 | public:
12 | Q_INVOKABLE explicit YourPage(const QVariant& data, QWidget* parent = nullptr);
13 |
14 | //optional, navigate page closed with data
15 | void onNavigateResult(const QVariant& data) override;
16 |
17 | //optional, be called when attemp close this page
18 | bool attempClose() override;
19 |
20 | protected:
21 | //optional, receive event from other page
22 | void runRouterEvent(const QString& event, const QVariant& data) override;
23 |
24 | //optional, receive event from other page with return data
25 | QVariant executeRouterEvent(const QString& event, const QVariant& data) override;
26 |
27 | //optional, showEvent first be called
28 | void onViewLoad();
29 |
30 | //optional, showEvent called at other time
31 | void onReshow();
32 |
33 | //optional, same as hideEvent
34 | void onHidden();
35 |
36 | //optional, same as resizeEvent
37 | void onResizeWindow();
38 |
39 | private:
40 | Ui::YourPageUi ui;
41 |
42 | //reflect type id
43 | static int typeId;
44 | }
45 | //register to meta-object system
46 | Q_DECLARE_METATYPE(YourPage*);
47 |
48 |
49 | //YourPage.cpp
50 |
51 | //register meta type
52 | int YourPage::typeId = qRegisterMetaType();
53 |
54 | YourPage::YourPage(const QVariant& data, QWidget* parent)
55 | : YourPage(data, parent)
56 | {
57 | ui.setupUi(this);
58 | }
59 |
60 | ```
61 | 可选重载函数说明
62 | - `virtual void onNavigateResult(const QVariant& data)`
63 | 当跳到的目标界面关闭时并携带数据,这个函数将被调用用于接收返回数据`data`,`data`可以传递任何类型数据
64 |
65 | - `virtual bool attempClose()`
66 | qrouter尝试关闭当前页面时,可以重载此函数阻止页面切换,返回false阻止关闭
67 |
68 | - `virtual void runRouterEvent(const QString& event, const QVariant& data)`
69 | 接收其他页面发送的消息,`event`为事件名称,`data`为传递的数据
70 |
71 | - `QVariant executeRouterEvent(const QString& event, const QVariant& data)`
72 | 等同于`runRouterEvent`,可额外返回数据到发送者
73 |
74 | - `virtual void onViewLoad()`
75 | 当页面第一次界面加载完时调用,等同于`showEvent`第一次被调用
76 |
77 | - `virtual void onReshow()`
78 | 除了第一次调用,其他时候`showEvent`被调用
79 |
80 | - `void onHidden()`
81 | 等同于`hideEvent`
82 |
83 | - `void onResizeWindow()`
84 | 等同于`resizeEvent`
85 |
86 | ### 2. 注册页面容器
87 | ```cpp
88 | QRouter::install(ui.stackedWidget, 0);
89 | ```
90 | - `static QRouter& install(QStackedWidget* stackContainer, int contextId = 0)`
91 | `stackContainer`为用于切换的页面容器,`contextId`为当前页面栈id
92 |
93 | ### 3. 如何使用qrouter切换页面
94 | ```cpp
95 | CustomData data;
96 | data.message = "send data from page1 to page2";
97 |
98 | //初始化并创建页面栈
99 | QRouter::of().initStack({"Page1", "Page2"});
100 |
101 | //从页面1跳转到page2,"Page2"为页面2的类名,of函数传递页面栈id,默认id为0的页面栈
102 | QRouter::of().push("Page2", QVariant::fromValue(data));
103 |
104 | //从页面栈移除当前页面1并跳转到页面2
105 | QRouter::of().pushReplace("Page2", QVariant::fromValue(data));
106 |
107 | //创建新页面page2并入栈,如果page2已经存在,则移动到栈顶
108 | QRouter::of().pushOrMove2Top("Page2", QVariant::fromValue(data));
109 |
110 | //尝试从页面栈移除当前页面1(如果可以关闭页面1)并跳转到页面2
111 | QRouter::of().popAndPush("Page2", QVariant::fromValue(data));
112 |
113 | //关闭指定页面2,页面2不一定在栈顶
114 | QRouter::of().close("Page2");
115 |
116 | //清除当前页面栈并跳转到页面2
117 | QRouter::of().pushAndClear("Page2", QVariant::fromValue(data));
118 |
119 | //将页面2移动到栈顶
120 | QRouter::of().move2Top("Page2");
121 |
122 | //关闭当前页面,并向上个页面传递数据
123 | QRouter::of().pop(QVariant::fromValue(data));
124 |
125 | //关闭页面栈Page2上面的所有页面
126 | QRouter::of().popUntil("Page2", QVariant::fromValue(data));
127 |
128 | //页面栈只保留4个页面,并关闭栈之上的所有页面
129 | QRouter::of().popUntil(4);
130 |
131 | //其他功能函数
132 |
133 | //读取当前页面栈所有页面名称
134 | QRouter::of().readStack();
135 |
136 | //获取当前栈顶页面名
137 | QRouter::of().currentName();
138 |
139 | //获取当前栈栈顶实例
140 | QRouter::of().current();
141 | QRouter::currentInstance();
142 |
143 | //从当前页面栈中获取指定页面实例
144 | QRouter::of().getInstanceFromStack("MyPage");
145 | QRouter::getInstance("MyPage");
146 |
147 | //获取页面栈容器的id
148 | QRouter::getIdByContainer(ui.page_container);
149 | ```
150 |
151 | ### 4. 发送事件
152 | ```cpp
153 | //同步发送事件到当前页面(页面栈顶)
154 | QRouter::of().sendEventCur("eventName");
155 |
156 | //同步发送事件到Page1
157 | QRouter::of().sendEventTo("Page1", "eventName");
158 |
159 | //同步发送事件到当前页面栈所有页面
160 | QRouter::of().sendEventAll("eventName");
161 |
162 | //发送事件到当前页面,使用事件队列
163 | QRouter::of().postEventCur("eventName");
164 |
165 | //发送事件到Page1,使用事件队列
166 | QRouter::of().postEventTo("Page1", "eventName");
167 |
168 | //发送事件到当前页面栈所有页面,使用事件队列
169 | QRouter::of().postEventAll("eventName");
170 |
171 | //发送事件到容器父页面
172 | QRouter::of().postEventToRoot("eventName");
173 |
174 | //容器所在父页面接收子页面发送的事件
175 | bool event(QEvent* event) override {
176 | if (event->type() == QRouterPageEvent::type) {
177 | //do something...
178 | return true;
179 | }
180 | return QWidget::event(event);
181 | }
182 |
183 | //父页面必须是stackWidget的parent,否则使用eventFilter接收事件
184 | ui.page_container->parent()->installEventFilter(this);
185 | bool eventFilter(QObject *watched, QEvent *event) override {
186 | if (event->type() == QRouterPageEvent::type) {
187 | if (watched == ui.page_container->parent()) {
188 | //do something...
189 | }
190 | return true;
191 | }
192 | return QWidget::eventFilter(watched, event);
193 | }
194 | ```
--------------------------------------------------------------------------------
/include/routerstackmanager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "abstractroutercontainer.h"
7 |
8 | class AbstractRouterWidget;
9 | class RouterStackManager {
10 | public:
11 | /////////////////////////////// base information ///////////////////////////////
12 | /**
13 | * Get all current page stack names
14 | * @return names
15 | */
16 | QStringList readStack();
17 |
18 | /**
19 | * Get current top page name
20 | * @return
21 | */
22 | QString currentName();
23 |
24 | /**
25 | * Get current top page instance
26 | * @return
27 | */
28 | AbstractRouterWidget* current();
29 |
30 | /**
31 | * Get all page instance from current page stack
32 | * @return
33 | */
34 | QList currentInstances();
35 |
36 | /**
37 | * Get instance by name in current page stack, return null if not exist
38 | * @param pageClassName
39 | * @return
40 | */
41 | AbstractRouterWidget* getInstanceFromStack(const QByteArray& pageClassName);
42 |
43 | /**
44 | * Get instance by stack index in current page stack, return null if out of range
45 | * @param stackIndex
46 | * @return
47 | */
48 | AbstractRouterWidget* getInstanceFromStack(int stackIndex);
49 |
50 | /////////////////////////////// stack operation ///////////////////////////////
51 |
52 | /**
53 | * Initialize the page stack by page names
54 | * @param pages
55 | */
56 | void initStack(const QList& pages);
57 |
58 | /**
59 | * Create a page instance and push it into the current page stack
60 | * @param pageClassName
61 | * @param data data for new page instance
62 | */
63 | void push(const QByteArray& pageClassName, const QVariant& data = QVariant());
64 |
65 | /**
66 | * Create a page instance and replace the top one on the current page stack
67 | * @param pageClassName
68 | * @param data data for new page instance
69 | */
70 | void pushReplace(const QByteArray& pageClassName, const QVariant& data = QVariant());
71 |
72 | /**
73 | * If the page does not exist in the current page stack, create a page instance and push it into the current stack,
74 | * otherwise move the page to the top of the stack
75 | * @param pageClassName
76 | * @param data data for the created page instance
77 | * @return true if the page instance exist in stack
78 | */
79 | bool pushOrMove2Top(const QByteArray& pageClassName, const QVariant& data = QVariant());
80 |
81 | /**
82 | * Remove the top page from the stack and push the new page into the current stack
83 | * @param pageClassName
84 | * @param data data for new page instance
85 | */
86 | void popAndPush(const QByteArray& pageClassName, const QVariant& data = QVariant());
87 |
88 | /**
89 | * Close the specified page (if it exists)
90 | * @param pageClassName the specified page
91 | */
92 | void close(const QByteArray& pageClassName);
93 |
94 | /**
95 | * Clear current page stack, create a new page and push into the stack
96 | * @param pageClassName
97 | * @param data data for new page instance
98 | */
99 | void pushAndClear(const QByteArray& pageClassName, const QVariant& data = QVariant());
100 |
101 | /**
102 | * Move the specified page to top of the page stack
103 | * @param pageClassName the specified page
104 | * @return true if the specified page exist in page stack
105 | */
106 | bool move2Top(const QByteArray& pageClassName);
107 |
108 | /**
109 | * Pop up the current page and return to the previous page
110 | * @param data data for the previous page (receive data by 'onNavigateResult')
111 | */
112 | void pop(QVariant data = QVariant());
113 |
114 | /**
115 | * Pop up the page until the specified page
116 | * @param untilName
117 | */
118 | void popUntil(const QByteArray& untilName);
119 |
120 | /**
121 | * Pop up the page until 'stackSize' of the current page stack
122 | * @param stackSize
123 | */
124 | void popUntil(int stackSize);
125 |
126 | /////////////////////////////// event operation ///////////////////////////////
127 |
128 | /**
129 | * Send a event to current page of the top of page stack
130 | * @param event event name
131 | * @param data data for current page, receive data by 'runRouterEvent' (no data to feedback) or 'executeRouterEvent'
132 | * @param ignoreContainerNotInstalled ignore send event when the current context id of container is not installed
133 | * @return processed data of current page
134 | */
135 | QVariant sendEventCur(const QString& event, const QVariant& data = QVariant(), bool ignoreContainerNotInstalled = true);
136 |
137 | /**
138 | * Send a event to the specified page
139 | * @param pageClassName the specified page name
140 | * @param event event name
141 | * @param data data for current the specified page, receive data by 'runRouterEvent' (no data to feedback) or 'executeRouterEvent'
142 | * @param ignoreContainerNotInstalled ignore send event when the current context id of container is not installed
143 | * @return processed data of the specified page
144 | */
145 | QVariant sendEventTo(const QByteArray& pageClassName, const QString& event, const QVariant& data = QVariant(), bool ignoreContainerNotInstalled = true);
146 |
147 | /**
148 | * Send a event to all page of the current page stack
149 | * @param event event name
150 | * @param data data for all page, receive data by 'runRouterEvent'
151 | * @param ignoreContainerNotInstalled ignore send event when the current context id of container is not installed
152 | */
153 | void sendEventAll(const QString& event, const QVariant& data = QVariant(), bool ignoreContainerNotInstalled = true);
154 |
155 | /**
156 | * Post a event to current page of the top of page stack
157 | * @param event event name
158 | * @param data data for current page, receive data by 'runRouterEvent'
159 | * @param ignoreContainerNotInstalled ignore send event when the current context id of container is not installed
160 | */
161 | void postEventCur(const QString& event, const QVariant& data = QVariant(), bool ignoreContainerNotInstalled = true);
162 |
163 | /**
164 | * Post a event to the specified page
165 | * @param pageClassName the specified page name
166 | * @param event event name
167 | * @param data data for current the specified page, receive data by 'runRouterEvent'
168 | * @param ignoreContainerNotInstalled ignore send event when the current context id of container is not installed
169 | */
170 | void postEventTo(const QByteArray& pageClassName, const QString& event, const QVariant& data = QVariant(), bool ignoreContainerNotInstalled = true);
171 |
172 | /**
173 | * Post a event to the container parent widget (stackWidget->parent())
174 | * @param event event name
175 | * @param data data for container parent, receive data by override 'bool event(QEvent* event)'
176 | * @param ignoreContainerNotInstalled ignore send event when the current context id of container is not installed
177 | */
178 | void postEventToRoot(const QString& event, const QVariant& data = QVariant(), bool ignoreContainerNotInstalled = true);
179 |
180 | /**
181 | * Post a event to all page of the current page stack
182 | * @param event event name
183 | * @param data data for all page, receive data by 'runRouterEvent'
184 | * @param ignoreContainerNotInstalled ignore send event when the current context id of container is not installed
185 | */
186 | void postEventAll(const QString& event, const QVariant& data = QVariant(), bool ignoreContainerNotInstalled = true);
187 |
188 | /////////////////////////////// transition animation operation ///////////////////////////////
189 |
190 | //RouterStackManager& alpha(int duration = 300, const QEasingCurve& easingCurve = QEasingCurve::OutCubic);
191 | //RouterStackManager& translate(int duration = 300, const QEasingCurve& easingCurve = QEasingCurve::OutCubic);
192 | //RouterStackManager& scale(int duration = 300, const QEasingCurve& easingCurve = QEasingCurve::OutCubic);
193 | //RouterStackManager& rotate(int duration = 300, const QEasingCurve& easingCurve = QEasingCurve::OutCubic);
194 |
195 | private:
196 | AbstractRouterContainer* container = nullptr;
197 | QList stack;
198 |
199 | QHash keepSingletonPageInstance;
200 |
201 | friend class QRouter;
202 |
203 | private:
204 | static AbstractRouterWidget* reflectByName(const QByteArray& className, QWidget* parent, const QVariant& data);
205 |
206 | void removePageInstance(AbstractRouterWidget* widget);
207 | };
--------------------------------------------------------------------------------
/src/routerstackmanager.cpp:
--------------------------------------------------------------------------------
1 | #include "routerstackmanager.h"
2 |
3 | #include "abstractrouterwidget.h"
4 | #include "qrouterexception.h"
5 | #include "qrouterpageevent.h"
6 |
7 | #include
8 |
9 | QEvent::Type QRouterPageEvent::type = static_cast(QEvent::registerEventType());
10 |
11 | QStringList RouterStackManager::readStack() {
12 | QStringList stackNames;
13 |
14 | for (const auto& i : stack) {
15 | stackNames << i->metaObject()->className();
16 | }
17 | return stackNames;
18 | }
19 |
20 | AbstractRouterWidget* RouterStackManager::current() {
21 | if (stack.isEmpty()) {
22 | return nullptr;
23 | }
24 | return stack.last();
25 | }
26 |
27 | QList RouterStackManager::currentInstances() {
28 | return stack;
29 | }
30 |
31 | QString RouterStackManager::currentName() {
32 | auto item = current();
33 | if (item == nullptr) {
34 | return {};
35 | }
36 | return item->metaObject()->className();
37 | }
38 |
39 | AbstractRouterWidget *RouterStackManager::getInstanceFromStack(const QByteArray &pageClassName) {
40 |
41 | AbstractRouterWidget* widgetTag = nullptr;
42 | for (int i=0; imetaObject()->className() == pageClassName) {
44 | widgetTag = stack.value(i);
45 | break;
46 | }
47 | }
48 |
49 | if (widgetTag == nullptr) {
50 | if (keepSingletonPageInstance.contains(pageClassName)) {
51 | widgetTag = keepSingletonPageInstance.value(pageClassName);
52 | }
53 | }
54 |
55 | return widgetTag;
56 | }
57 |
58 | AbstractRouterWidget *RouterStackManager::getInstanceFromStack(int stackIndex) {
59 | if (stackIndex < 0 || stackIndex >= stack.size()) {
60 | return nullptr;
61 | }
62 | return stack[stackIndex];
63 | }
64 |
65 | void RouterStackManager::initStack(const QList& pages) {
66 | if (pages.isEmpty()) {
67 | return;
68 | }
69 | for (const auto& page: pages) {
70 | try {
71 | stack.append(reflectByName(page, container->childPageParent(), {}));
72 | container->setCurrentWidget(stack.last());
73 | } catch (QRouterRuntimeException& e) {
74 | qFatal(e.message.toLatin1().data());
75 | }
76 | }
77 | }
78 |
79 | void RouterStackManager::push(const QByteArray& pageClassName, const QVariant& data) {
80 | AbstractRouterWidget* widget;
81 | if (keepSingletonPageInstance.contains(pageClassName)) {
82 | widget = keepSingletonPageInstance.take(pageClassName);
83 | } else {
84 | for (auto p : stack) {
85 | if (p->metaObject()->className() == pageClassName) {
86 | if (p->singletonInstance()) {
87 | move2Top(pageClassName);
88 | return;
89 | }
90 | }
91 | }
92 | try {
93 | widget = reflectByName(pageClassName, container->childPageParent(), data);
94 | } catch (QRouterRuntimeException& e) {
95 | qFatal(e.message.toLatin1().data());
96 | }
97 | }
98 | stack.append(widget);
99 | container->setCurrentWidget(widget);
100 | }
101 |
102 | void RouterStackManager::pushReplace(const QByteArray& pageClassName, const QVariant& data) {
103 | if (!stack.isEmpty()) {
104 | auto widget = stack.takeLast();
105 | container->removeWidget(widget);
106 | removePageInstance(widget);
107 | }
108 |
109 | push(pageClassName, data);
110 | }
111 |
112 | bool RouterStackManager::pushOrMove2Top(const QByteArray &pageClassName, const QVariant &data) {
113 | if (move2Top(pageClassName)) {
114 | return true;
115 | }
116 | push(pageClassName, data);
117 | return false;
118 | }
119 |
120 | void RouterStackManager::popAndPush(const QByteArray &pageClassName, const QVariant& data) {
121 | if (!stack.isEmpty()) {
122 | if (stack.last()->attemptClose()) {
123 | auto widget = stack.takeLast();
124 | container->removeWidget(widget);
125 | removePageInstance(widget);
126 | } else {
127 | return;
128 | }
129 | }
130 |
131 | push(pageClassName, data);
132 | }
133 |
134 | void RouterStackManager::close(const QByteArray &pageClassName) {
135 | bool isLast = false;
136 | for (int i=0; imetaObject()->className() == pageClassName) {
138 | isLast = i == stack.size() - 1;
139 | auto widget = stack.takeAt(i);
140 | container->removeWidget(widget);
141 | removePageInstance(widget);
142 | break;
143 | }
144 | }
145 |
146 | if (isLast && !stack.isEmpty()) {
147 | container->setCurrentWidget(stack.last());
148 | }
149 | }
150 |
151 | void RouterStackManager::pushAndClear(const QByteArray& pageClassName, const QVariant& data) {
152 | while (!stack.isEmpty()) {
153 | auto widget = stack.takeLast();
154 | container->removeWidget(widget);
155 | removePageInstance(widget);
156 | }
157 |
158 | push(pageClassName, data);
159 | }
160 |
161 | bool RouterStackManager::move2Top(const QByteArray& pageClassName) {
162 | AbstractRouterWidget* widgetTag = nullptr;
163 | for (int i=0; imetaObject()->className() == pageClassName) {
165 | widgetTag = stack.takeAt(i);
166 | break;
167 | }
168 | }
169 |
170 | if (widgetTag == nullptr) {
171 | if (keepSingletonPageInstance.contains(pageClassName)) {
172 | widgetTag = keepSingletonPageInstance.take(pageClassName);
173 | } else {
174 | return false;
175 | }
176 | }
177 |
178 | stack.append(widgetTag);
179 | container->setCurrentWidget(widgetTag);
180 |
181 | return true;
182 | }
183 |
184 | void RouterStackManager::pop(QVariant data) {
185 | if (!stack.isEmpty()) {
186 | if (stack.last()->attemptClose()) {
187 | auto widget = stack.takeLast();
188 | if (data.isNull()) {
189 | data = widget->readAttemptCloseData();
190 | }
191 | container->removeWidget(widget);
192 | removePageInstance(widget);
193 |
194 | container->setCurrentWidget(stack.last());
195 | stack.last()->onNavigateResult(data);
196 | }
197 | }
198 | }
199 |
200 | void RouterStackManager::popUntil(const QByteArray& untilName) {
201 | while (!stack.isEmpty() && stack.last()->metaObject()->className() != untilName) {
202 | if (!stack.last()->attemptClose()) {
203 | container->setCurrentWidget(stack.last());
204 | return;
205 | }
206 | auto widget = stack.takeLast();
207 | container->removeWidget(widget);
208 | removePageInstance(widget);
209 | }
210 |
211 | if (!stack.isEmpty()) {
212 | container->setCurrentWidget(stack.last());
213 | }
214 | }
215 |
216 | void RouterStackManager::popUntil(int stackSize) {
217 | while (stack.size() > stackSize) {
218 | auto widget = stack.takeLast();
219 | container->removeWidget(widget);
220 | removePageInstance(widget);
221 | }
222 |
223 | if (!stack.isEmpty()) {
224 | container->setCurrentWidget(stack.last());
225 | }
226 | }
227 |
228 |
229 | QVariant RouterStackManager::sendEventCur(const QString& event, const QVariant& data, bool ignoreContainerNotInstalled) {
230 | try {
231 | if (stack.isEmpty()) {
232 | return {};
233 | }
234 | return stack.last()->onRouterEvent(event, data);
235 | } catch (QRouterRuntimeException& e) {
236 | if (!ignoreContainerNotInstalled) {
237 | #ifdef QT_DEBUG
238 | qFatal(e.message.toLatin1().data());
239 | #endif
240 | throw e;
241 | }
242 | }
243 | return {};
244 | }
245 |
246 | QVariant RouterStackManager::sendEventTo(const QByteArray& pageClassName, const QString& event, const QVariant& data, bool ignoreContainerNotInstalled) {
247 | try {
248 | for (const auto &page: stack) {
249 | if (page->metaObject()->className() == pageClassName) {
250 | return page->onRouterEvent(event, data);
251 | }
252 | }
253 | } catch (QRouterRuntimeException& e) {
254 | if (!ignoreContainerNotInstalled) {
255 | #ifdef QT_DEBUG
256 | qFatal(e.message.toLatin1().data());
257 | #endif
258 | throw e;
259 | }
260 | }
261 |
262 | if (keepSingletonPageInstance.contains(pageClassName)) {
263 | return keepSingletonPageInstance[pageClassName]->onRouterEvent(event, data);
264 | }
265 |
266 | return {};
267 | }
268 |
269 | void RouterStackManager::sendEventAll(const QString& event, const QVariant& data, bool ignoreContainerNotInstalled) {
270 | try {
271 | for (const auto &widget: stack) {
272 | widget->onRouterEvent(event, data);
273 | }
274 | } catch (QRouterRuntimeException& e) {
275 | if (!ignoreContainerNotInstalled) {
276 | #ifdef QT_DEBUG
277 | qFatal(e.message.toLatin1().data());
278 | #endif
279 | throw e;
280 | }
281 | }
282 |
283 | for (auto i = keepSingletonPageInstance.begin(); i != keepSingletonPageInstance.end(); ++i) {
284 | i.value()->onRouterEvent(event, data);
285 | }
286 | }
287 |
288 | void RouterStackManager::postEventCur(const QString& event, const QVariant& data, bool ignoreContainerNotInstalled) {
289 | try {
290 | if (stack.isEmpty()) {
291 | return;
292 | }
293 | return qApp->postEvent(stack.last(), new QRouterPageEvent(event, data));
294 | } catch (QRouterRuntimeException& e) {
295 | if (!ignoreContainerNotInstalled) {
296 | #ifdef QT_DEBUG
297 | qFatal(e.message.toLatin1().data());
298 | #endif
299 | throw e;
300 | }
301 | }
302 | }
303 |
304 | void RouterStackManager::postEventTo(const QByteArray& pageClassName, const QString& event, const QVariant& data, bool ignoreContainerNotInstalled) {
305 | try {
306 | for (const auto &page: stack) {
307 | if (page->metaObject()->className() == pageClassName) {
308 | qApp->postEvent(page, new QRouterPageEvent(event, data));
309 | return;
310 | }
311 | }
312 | } catch (QRouterRuntimeException& e) {
313 | if (!ignoreContainerNotInstalled) {
314 | #ifdef QT_DEBUG
315 | qFatal(e.message.toLatin1().data());
316 | #endif
317 | throw e;
318 | }
319 | }
320 |
321 | if (keepSingletonPageInstance.contains(pageClassName)) {
322 | qApp->postEvent(keepSingletonPageInstance[pageClassName], new QRouterPageEvent(event, data));
323 | }
324 | }
325 |
326 | void RouterStackManager::postEventToRoot(const QString& event, const QVariant& data, bool ignoreContainerNotInstalled) {
327 | try {
328 | qApp->postEvent(container->rootPageEventReceiver(), new QRouterPageEvent(event, data));
329 | } catch (QRouterRuntimeException& e) {
330 | if (!ignoreContainerNotInstalled) {
331 | #ifdef QT_DEBUG
332 | qFatal(e.message.toLatin1().data());
333 | #endif
334 | throw e;
335 | }
336 | }
337 | }
338 |
339 | void RouterStackManager::postEventAll(const QString& event, const QVariant& data, bool ignoreContainerNotInstalled) {
340 | try {
341 | for (const auto &widget: stack) {
342 | qApp->postEvent(widget, new QRouterPageEvent(event, data));
343 | }
344 | } catch (QRouterRuntimeException& e) {
345 | if (!ignoreContainerNotInstalled) {
346 | #ifdef QT_DEBUG
347 | qFatal(e.message.toLatin1().data());
348 | #endif
349 | throw e;
350 | }
351 | }
352 |
353 | for (auto i = keepSingletonPageInstance.begin(); i != keepSingletonPageInstance.end(); ++i) {
354 | qApp->postEvent(i.value(), new QRouterPageEvent(event, data));
355 | }
356 | }
357 |
358 | AbstractRouterWidget* RouterStackManager::reflectByName(const QByteArray& className, QWidget* parent, const QVariant& data) {
359 | #if QT_VERSION_MAJOR >= 6
360 | const auto metaObj = QMetaType::fromName(className + '*').metaObject();
361 | #else
362 | int type = QMetaType::type(className + '*');
363 | const auto metaObj = QMetaType::metaObjectForType(type);
364 | #endif
365 | if (metaObj == nullptr) {
366 | throw QRouterRuntimeException(QString("cannot find page:'%1', the page class may not register by call 'qRegisterMetaType<%1*>()'.").arg(QString(className)));
367 | }
368 |
369 | auto obj = metaObj->newInstance(Q_ARG(QVariant, data), Q_ARG(QWidget*, parent));
370 | auto widget = dynamic_cast(obj);
371 | if (widget == nullptr) {
372 | throw QRouterRuntimeException(QString("cannot create instance of page:'%1', the page class constructor may not be assigned of 'Q_INVOKABLE'.").arg(QString(className)));
373 | }
374 |
375 | return widget;
376 | }
377 |
378 | void RouterStackManager::removePageInstance(AbstractRouterWidget *widget) {
379 | if (widget->singletonInstance()) {
380 | keepSingletonPageInstance.insert(widget->metaObject()->className(), widget);
381 | } else {
382 | widget->deleteLater();
383 | }
384 | }
--------------------------------------------------------------------------------