diff --git a/CMakeLists.txt b/CMakeLists.txt index 7296c14..e8dac26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,34 +3,18 @@ project (rust_qt_binding_generator) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) cmake_policy(SET CMP0046 NEW) cmake_policy(SET CMP0063 NEW) -set(QT_MIN_VERSION "5.3.0") -set(KF5_MIN_VERSION "5.2.0") +set(QT_MIN_VERSION "5.6.0") -find_package(ECM 1.0.0 REQUIRED NO_MODULE) -set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) - -include(ExternalProject) -include(KDEInstallDirs) -include(KDECMakeSettings) -include(KDECompilerSettings) include(FeatureSummary) -set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/Rust) - # Find Qt modules find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS - Core # QCommandLineParser, QStringLiteral - Quick + Core Test - Widgets # QApplication -) - -# Find KDE modules -find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS - CoreAddons # KAboutData - I18n # KLocalizedString - WidgetsAddons # KMessageBox ) +find_package(Qt5Widgets ${QT_MIN_VERSION} CONFIG) +find_package(Qt5Quick ${QT_MIN_VERSION} CONFIG) +set(CMAKE_AUTOMOC ON) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) @@ -44,6 +28,10 @@ else() endif() add_subdirectory(rust_qt_binding_generator) +enable_testing() add_subdirectory(tests) -add_subdirectory(demo) + +if(Qt5Widgets_FOUND) + add_subdirectory(demo) +endif() diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index f580bed..fd160f5 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -1,5 +1,6 @@ set(CMAKE_AUTORCC ON) +# generate c++ and rust code from fibonacci.json add_custom_command( OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/rust/src/fibonacci_interface.rs" "${CMAKE_CURRENT_SOURCE_DIR}/rust/src/fibonacci_types.rs" @@ -10,6 +11,7 @@ add_custom_command( DEPENDS rust_qt_binding_generator fibonacci.json ) +# generate c++ and rust code from tree.json add_custom_command( OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/rust/src/interface.rs" "${CMAKE_CURRENT_SOURCE_DIR}/rust/src/types.rs" @@ -20,6 +22,7 @@ add_custom_command( DEPENDS rust_qt_binding_generator tree.json ) +# compile the rust code into a static library add_custom_command( OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/rust/${RUST_TARGET_DIR}/librust.a" COMMAND cargo build ${RUST_BUILD_FLAG} @@ -34,6 +37,11 @@ add_custom_command( ) add_custom_target(rust_target DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/rust/${RUST_TARGET_DIR}/librust.a") +if(Qt5Quick_FOUND) + add_definitions(-DQTQUICK) + set(Qt5Quick_LIBS Qt5::Quick) +endif() + set(Demo_SRCS src/main.cpp src/Tree.cpp src/Fibonacci.cpp resource_file.qrc) @@ -41,11 +49,8 @@ add_executable(Demo ${Demo_SRCS}) add_dependencies(Demo rust_target) target_link_libraries(Demo - Qt5::Quick + "${Qt5Quick_LIBS}" Qt5::Widgets - KF5::CoreAddons - KF5::I18n - KF5::WidgetsAddons "${CMAKE_CURRENT_SOURCE_DIR}/rust/${RUST_TARGET_DIR}/librust.a" ) diff --git a/demo/demo.qml b/demo/demo.qml index dd7c78e..0d31298 100644 --- a/demo/demo.qml +++ b/demo/demo.qml @@ -5,18 +5,32 @@ import QtQuick.Layouts 1.3 import rust 1.0 ApplicationWindow { - width: 500 - height: 480 + id: application + x: windowX + y: windowY + width: windowWidth + height: windowHeight visible: true ItemSelectionModel { id: selectionModel model: fsModel } - FibonacciList { - id: fibonacciList - } TabView { anchors.fill: parent + Tab { + title: "style" + ComboBox { + currentIndex: qtquickIndex + model: styles + textRole: "display" + onCurrentIndexChanged: { + if (currentText && currentText != "QtQuick") { + widgets.currentText = currentText; + application.close(); + } + } + } + } Tab { title: "object" RowLayout { @@ -24,14 +38,12 @@ ApplicationWindow { id: fibonacciInput placeholderText: "Your number" validator: IntValidator {bottom: 0; top: 100;} + Component.onCompleted: { text = fibonacci.input } + onTextChanged: { fibonacci.input = parseInt(text, 10) } } Text { text: "The Fibonacci number: " + fibonacci.result } - Fibonacci { - id: fibonacci - input: parseInt(fibonacciInput.text, 10) - } } } Tab { diff --git a/demo/resource_file.qrc b/demo/resource_file.qrc index 6d9c32d..8ace119 100644 --- a/demo/resource_file.qrc +++ b/demo/resource_file.qrc @@ -1,5 +1,6 @@ demo.qml + demo-qtquick2.qml diff --git a/demo/src/main.cpp b/demo/src/main.cpp index cc7557c..54e913e 100644 --- a/demo/src/main.cpp +++ b/demo/src/main.cpp @@ -1,77 +1,214 @@ #include "Tree.h" #include "Fibonacci.h" -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef QTQUICK #include #include #include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include + +#include + +struct Models { + Tree tree; + QSortFilterProxyModel sortedTree; + Fibonacci fibonacci; + FibonacciList fibonacciList; + QStringListModel styles; +}; + +void setStyle(QWidget* w, QStyle* style) { + for (QObject* o: w->children()) { + QWidget* c = dynamic_cast(o); + if (c) { + setStyle(c, style); + } + } + w->setStyle(style); +} + +QWindow* getWindow(QWidget* w) { + QWidget* top = w; + while (top && top->parentWidget()) { + top = top->parentWidget(); + } + return top->windowHandle(); +} + +#ifdef QTQUICK + +void copyWindowGeometry(QWidget* w, QQmlContext* c) { + QWindow* window = getWindow(w); + if (window) { + c->setContextProperty("windowX", window->x()); + c->setContextProperty("windowY", window->y()); + c->setContextProperty("windowWidth", window->width()); + c->setContextProperty("windowHeight", window->height()); + } +} + +void createQtQuick(Models* models, QWidget* widgets) { + QQmlApplicationEngine* engine = new QQmlApplicationEngine(); + QQmlContext* c = engine->rootContext(); + c->setContextProperty("fsModel", &models->tree); + c->setContextProperty("sortedFsModel", &models->sortedTree); + c->setContextProperty("fibonacci", &models->fibonacci); + c->setContextProperty("fibonacciList", &models->fibonacciList); + c->setContextProperty("styles", &models->styles); + c->setContextProperty("widgets", widgets); + c->setContextProperty("qtquickIndex", + QVariant(models->styles.stringList().indexOf("QtQuick"))); + copyWindowGeometry(widgets, engine->rootContext()); + engine->load(QUrl(QStringLiteral("qrc:///demo.qml"))); +} + +#endif + +QComboBox* createStyleComboBox(Models* models) { + QComboBox* box = new QComboBox(); + box->setModel(&models->styles); + auto styles = QStyleFactory::keys(); + QString currentStyle = QApplication::style()->objectName().toLower(); + for (auto v: styles) { + box->addItem("QWidgets " + v); + if (v.toLower() == currentStyle) { + box->setCurrentText(v); + } + } +#ifdef QTQUICK + box->addItem("QtQuick"); +#endif + return box; +} + +QWidget* createStyleTab(Models* models, QWidget* tabs) { + QComboBox* box = createStyleComboBox(models); + QRect windowRect; + box->connect(box, &QComboBox::currentTextChanged, box, [windowRect, box, tabs, models](const QString &text) mutable { + QWindow* window = getWindow(tabs); + bool visible = tabs->isVisible(); + if (text.startsWith("QWidgets ")) { + tabs->setVisible(true); + if (window && !visible) { + window->setX(windowRect.x()); + window->setY(windowRect.y()); + window->setWidth(windowRect.width()); + window->setHeight(windowRect.height()); + } + setStyle(tabs, QStyleFactory::create(text.mid(9))); +#ifdef QTQUICK + } else { + if (window) { + windowRect.setX(window->x()); + windowRect.setY(window->y()); + windowRect.setWidth(window->width()); + windowRect.setHeight(window->height()); + } + tabs->setVisible(false); + createQtQuick(models, box); +#endif + } + }); + return box; +} + +QWidget* createObjectTab(Models* models) { + QWidget* view = new QWidget; + Fibonacci* fibonacci = &models->fibonacci; + + QLineEdit* input = new QLineEdit; + input->setPlaceholderText("Your number"); + input->setValidator(new QIntValidator(0, 100)); + input->connect(input, &QLineEdit::textChanged, fibonacci, + [fibonacci](const QString& text) { + fibonacci->setInput(text.toInt()); + }); + fibonacci->connect(fibonacci, &Fibonacci::inputChanged, input, + [input, fibonacci]() { + input->setText(QString::number(fibonacci->input())); + }); + + QLabel* label = new QLabel; + fibonacci->connect(fibonacci, &Fibonacci::resultChanged, label, + [label, fibonacci]() { + label->setText("The Fibonacci number: " + + QString::number(fibonacci->result())); + }); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(input); + layout->addWidget(label); + view->setLayout(layout); + return view; +} + +QWidget* createListTab(Models* models) { + QListView* view = new QListView(); + view->setModel(&models->fibonacciList); + return view; +} + +QWidget* createTreeTab(Models* models) { + QTreeView* view = new QTreeView(); + view->setUniformRowHeights(true); + view->setSortingEnabled(true); + view->setModel(&models->sortedTree); + auto root = models->sortedTree.index(0, 0); + view->expand(root); + view->sortByColumn(0, Qt::AscendingOrder); + view->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + return view; +} + +void createWidgets(Models* models) { + QTabWidget* tabs = new QTabWidget(); + + tabs->addTab(createStyleTab(models, tabs), "style"); + tabs->addTab(createObjectTab(models), "object"); + tabs->addTab(createListTab(models), "list"); + tabs->addTab(createTreeTab(models), "tree"); + tabs->setMinimumSize(QSize(500, 500)); + tabs->show(); +} int main (int argc, char *argv[]) { QApplication app(argc, argv); - KLocalizedString::setApplicationDomain("Demo"); - - KAboutData aboutData( - // The program name used internally. (componentName) - QStringLiteral("Demo"), - // A displayable program name string. (displayName) - i18n("Demo"), - // The program version string. (version) - QStringLiteral("0.1"), - // Short description of what the app does. (shortDescription) - i18n("Demo application for Rust bindings"), - // The license this code is released under - KAboutLicense::GPL, - // Copyright Statement (copyrightStatement = QString()) - i18n("(c) 2017"), - // Optional text shown in the About box. - // Can contain any information desired. (otherText) - i18n("Some text..."), - // The program homepage string. (homePageAddress = QString()) - QStringLiteral("http://kde.org/"), - // The bug report email address - // (bugsEmailAddress = QLatin1String("submit@bugs.kde.org") - QStringLiteral("submit@bugs.kde.org")); - aboutData.addAuthor(i18n("Jos van den Oever"), i18n("Task"), QStringLiteral("your@email.com"), - QStringLiteral("http://vandenoever.info"), QStringLiteral("OSC Username")); - KAboutData::setApplicationData(aboutData); - QCommandLineParser parser; - parser.addHelpOption(); - parser.addVersionOption(); - aboutData.setupCommandLine(&parser); - parser.process(app); - aboutData.processCommandLine(&parser); +#ifdef QTQUICK qmlRegisterType("org.qtproject.example", 1, 0, "SortFilterProxyModel"); qmlRegisterType("rust", 1, 0, "Fibonacci"); qmlRegisterType("rust", 1, 0, "FibonacciList"); - Tree model; +#endif + + Models models; + Tree& model = models.tree; + QSortFilterProxyModel& sortedModel = models.sortedTree; model.setPath("/"); - QSortFilterProxyModel sortedModel; sortedModel.setSourceModel(&model); sortedModel.setDynamicSortFilter(true); - QTreeView view; - view.setUniformRowHeights(true); - view.setSortingEnabled(true); - view.setModel(&sortedModel); - auto root = sortedModel.index(0, 0); - view.expand(root); - view.sortByColumn(0, Qt::AscendingOrder); - view.show(); - view.header()->setSectionResizeMode(QHeaderView::ResizeToContents); - QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty("fsModel", &model); - engine.rootContext()->setContextProperty("sortedFsModel", &sortedModel); - engine.load(QUrl(QStringLiteral("qrc:///demo.qml"))); + + createWidgets(&models); return app.exec(); } diff --git a/dev b/dev index 385c514..d587b74 100644 --- a/dev +++ b/dev @@ -10,8 +10,8 @@ if [ -z "${1-}" ]; then else # run with NixPkgs master that has Qt5.9 and QtCharts export NIX_PATH=nixpkgs=$HOME/nixpkgs - export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:/nix/store/cmnbj3s42vfwfkibr9ksv28g44iqbq1y-qtquickcontrols2-5.9.1-bin/lib/qt-5.9/qml/:/nix/store/cv2ayyx56jsqifb1b65ksm0n522ji733-kirigami2-2.1.0/lib/qt-5.9/qml +# export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:/nix/store/cmnbj3s42vfwfkibr9ksv28g44iqbq1y-qtquickcontrols2-5.9.1-bin/lib/qt-5.9/qml/:/nix/store/cv2ayyx56jsqifb1b65ksm0n522ji733-kirigami2-2.1.0/lib/qt-5.9/qml charts="qt5.qtcharts libsForQt5.kirigami_2" fi -nix-shell -p qtcreator cmake ninja gcc rustc cargo qt5.full extra-cmake-modules kdeFrameworks.kwidgetsaddons kdeFrameworks.kcoreaddons kdeFrameworks.ki18n appstream cmakeCurses kdeFrameworks.ktexteditor qt5.qtquickcontrols2 $charts +nix-shell -p cmake ninja gcc rustc cargo qt5.full extra-cmake-modules kdeFrameworks.kwidgetsaddons kdeFrameworks.kcoreaddons kdeFrameworks.ki18n appstream cmakeCurses kdeFrameworks.ktexteditor qt5.qtquickcontrols2 $charts diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c73f4a3..76686ed 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,6 @@ -enable_testing() SET(GENERATOR "${CMAKE_BINARY_DIR}/rust_qt_binding_generator/rust_qt_binding_generator") +include_directories("${CMAKE_CURRENT_BINARY_DIR}") add_custom_target("clean-rust") @@ -33,6 +33,10 @@ function(rust_test NAME DIRECTORY) DEPENDS "${DIR}/${RUST_TARGET_DIR}/librust.a") add_executable("${NAME}" "${NAME}.cpp" "${NAME}_rust.cpp" "${NAME}_rust.h") + set_target_properties("${NAME}" PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + ) add_dependencies("${NAME}" "test_${DIRECTORY}") target_link_libraries("${NAME}" Qt5::Core