File indexing completed on 2024-04-14 15:48:42

0001 /*
0002  *   Copyright 2017  Jos van den Oever <jos@vandenoever.info>
0003  *
0004  *   This program is free software; you can redistribute it and/or
0005  *   modify it under the terms of the GNU General Public License as
0006  *   published by the Free Software Foundation; either version 2 of
0007  *   the License or (at your option) version 3 or any later version
0008  *   accepted by the membership of KDE e.V. (or its successor approved
0009  *   by the membership of KDE e.V.), which shall act as a proxy
0010  *   defined in Section 14 of version 3 of the license.
0011  *
0012  *   This program is distributed in the hope that it will be useful,
0013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  *   GNU General Public License for more details.
0016  *
0017  *   You should have received a copy of the GNU General Public License
0018  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019  */
0020 
0021 #include "Bindings.h"
0022 #include "SortedModel.h"
0023 
0024 #ifdef QT_CHARTS_LIB
0025 #include <QtCharts>
0026 #endif
0027 
0028 #ifdef QT_QUICK_LIB
0029 #include <QQmlApplicationEngine>
0030 #include <QQmlContext>
0031 #include <QQuickItem>
0032 #include <QQuickStyle>
0033 #include <QQuickView>
0034 #include <QtQml/qqml.h>
0035 #endif
0036 
0037 #include <QApplication>
0038 #include <QComboBox>
0039 #include <QCommandLineParser>
0040 #include <QDebug>
0041 #include <QHBoxLayout>
0042 #include <QHeaderView>
0043 #include <QIntValidator>
0044 #include <QLabel>
0045 #include <QLineEdit>
0046 #include <QListView>
0047 #include <QMainWindow>
0048 #include <QSortFilterProxyModel>
0049 #include <QStatusBar>
0050 #include <QStringListModel>
0051 #include <QStyleFactory>
0052 #include <QSvgRenderer>
0053 #include <QSvgWidget>
0054 #include <QTabWidget>
0055 #include <QTableView>
0056 #include <QTreeView>
0057 #include <QVBoxLayout>
0058 #include <QWindow>
0059 
0060 #include <cstdlib>
0061 
0062 struct Model {
0063     QStringListModel styles;
0064     Demo demo;
0065     SortedModel sortedFileSystem;
0066     SortedModel sortedProcesses;
0067 };
0068 
0069 void setStyle(QWidget* w, QStyle* style) {
0070     for (QObject* o: w->children()) {
0071         QWidget* c = dynamic_cast<QWidget*>(o);
0072         if (c) {
0073             setStyle(c, style);
0074         }
0075     }
0076     w->setStyle(style);
0077 }
0078 
0079 QWindow* getWindow(QWidget* w) {
0080     QWidget* top = w;
0081     while (top && top->parentWidget()) {
0082         top = top->parentWidget();
0083     }
0084     return top->windowHandle();
0085 }
0086 
0087 #ifdef QT_QUICK_LIB
0088 
0089 void copyWindowGeometry(const QRect& rect, QObject* c) {
0090     if (rect.width() && rect.height()) {
0091         c->setProperty("x", rect.x());
0092         c->setProperty("y", rect.y());
0093         c->setProperty("width", rect.width());
0094         c->setProperty("heigth", rect.height());
0095     }
0096 }
0097 
0098 bool isQQC2Style(const QString& name) {
0099     return name == "Kirigami 2" || name.startsWith("QtQuick Controls 2");
0100 }
0101 
0102 QString qqc2Style(const QString& name) {
0103     if (name.startsWith("QtQuick Controls 2")) {
0104         return name.mid(19);
0105     }
0106     return QString("Default");
0107 }
0108 
0109 void createQtQuick(const QString& name, const QString& qml, Model* model,
0110             QWidget* widgets, const QString& initialTab) {
0111     static QString qqc2style;
0112     static bool blocked = false;
0113     if (blocked) {
0114         return;
0115     }
0116     blocked = true;
0117     if (qqc2style.isNull() && isQQC2Style(name)) {
0118         qqc2style = qqc2Style(name);
0119         QQuickStyle::setStyle(qqc2style);
0120         QStringListModel& styles = model->styles;
0121         int i = 0;
0122         while (i < styles.rowCount()) {
0123             QString style = styles.data(styles.index(i)).toString();
0124             if (isQQC2Style(style) && style != name) {
0125                 styles.removeRows(i, 1);
0126             } else {
0127                 ++i;
0128             }
0129         }
0130     }
0131     blocked = false;
0132     QQmlApplicationEngine* engine = new QQmlApplicationEngine();
0133     QQmlContext* c = engine->rootContext();
0134     c->setContextProperty("styles", &model->styles);
0135     c->setContextProperty("demo", &model->demo);
0136     c->setContextProperty("sortedFileSystem", &model->sortedFileSystem);
0137     c->setContextProperty("widgets", widgets);
0138     QRect geometry;
0139     QWindow* window = getWindow(widgets);
0140     if (window) {
0141         geometry = window->geometry();
0142     } else {
0143         geometry = QRect(0, 0, 800, 640);
0144     }
0145     engine->load(QUrl(qml));
0146     QObject* root = engine->rootObjects().first();
0147     copyWindowGeometry(geometry, root);
0148     root->setProperty("initialTab", initialTab);
0149     root->setProperty("qtquickIndex",
0150             QVariant(model->styles.stringList().indexOf(name)));
0151     root->setProperty("processes",
0152             QVariant::fromValue(&model->sortedProcesses));
0153 }
0154 
0155 #endif
0156 
0157 QComboBox* createStyleComboBox(Model* model) {
0158     QComboBox* box = new QComboBox();
0159     box->setModel(&model->styles);
0160     auto styles = QStyleFactory::keys();
0161     QString currentStyle = QApplication::style()->objectName().toLower();
0162     for (auto v: styles) {
0163         box->addItem("QWidgets " + v);
0164         if (v.toLower() == currentStyle) {
0165             box->setCurrentText(v);
0166         }
0167     }
0168 #ifdef QT_QUICK_LIB
0169     box->addItem("QtQuick");
0170 #endif
0171 #ifdef QTQUICKCONTROLS2
0172     for (auto style: QQuickStyle::availableStyles()) {
0173         if (style != "Plasma") { // Plasma style is buggy
0174             box->addItem("QtQuick Controls 2 " + style);
0175         }
0176     }
0177 #endif
0178 #ifdef KIRIGAMI2
0179     box->addItem("Kirigami 2");
0180 #endif
0181     return box;
0182 }
0183 
0184 QStatusBar* createStatusBar(Model* model, QWidget* main, QComboBox* box,
0185         const QString& initialTab) {
0186     QRect windowRect;
0187     auto f = [windowRect, box, main, model, initialTab](const QString &text) mutable {
0188         QWindow* window = getWindow(main);
0189         bool visible = main->isVisible();
0190         if (text.startsWith("QWidgets ")) {
0191             main->setVisible(true);
0192             if (window && !visible) {
0193                 window->setX(windowRect.x());
0194                 window->setY(windowRect.y());
0195                 window->setWidth(windowRect.width());
0196                 window->setHeight(windowRect.height());
0197             }
0198             setStyle(main, QStyleFactory::create(text.mid(9)));
0199 #ifdef QT_QUICK_LIB
0200         } else {
0201             if (window) {
0202                 windowRect.setX(window->x());
0203                 windowRect.setY(window->y());
0204                 windowRect.setWidth(window->width());
0205                 windowRect.setHeight(window->height());
0206             }
0207             main->setVisible(false);
0208 #ifdef QTQUICKCONTROLS2
0209             if (text.startsWith("QtQuick Controls 2")) {
0210                 createQtQuick(text, "qrc:///qml/demo-qtquick2.qml",
0211                     model, box, initialTab);
0212             } else
0213 #endif
0214 #ifdef KIRIGAMI2
0215             if (text == "Kirigami 2") {
0216                 createQtQuick("Kirigami 2", "qrc:///qml/demo-kirigami2.qml",
0217                     model, box, initialTab);
0218             } else
0219 #endif
0220             createQtQuick("QtQuick", "qrc:///qml/demo.qml", model, box, initialTab);
0221 #endif
0222         }
0223     };
0224     box->connect(box, &QComboBox::currentTextChanged, box, f);
0225 
0226     QSvgWidget* rust_qt_binding_generator = new QSvgWidget(":/rust_qt_binding_generator.svg");
0227     rust_qt_binding_generator->setFixedSize(rust_qt_binding_generator->renderer()->defaultSize() / 4);
0228 
0229     QStatusBar* statusBar = new QStatusBar;
0230     statusBar->addPermanentWidget(rust_qt_binding_generator);
0231     statusBar->addPermanentWidget(box);
0232     return statusBar;
0233 }
0234 
0235 QWidget* createObjectTab(Model* model) {
0236     QWidget* view = new QWidget;
0237     Fibonacci* fibonacci = model->demo.fibonacci();
0238 
0239     QLabel* label = new QLabel;
0240     label->setText(QCoreApplication::translate("main", "Calculate the <i>nth</i> Fibonacci number"));
0241 
0242     QLineEdit* input = new QLineEdit;
0243     input->setPlaceholderText("Your number");
0244     input->setValidator(new QIntValidator(0, 100));
0245     input->connect(input, &QLineEdit::textChanged, fibonacci,
0246         [fibonacci](const QString& text) {
0247             fibonacci->setInput(text.toInt());
0248     });
0249     fibonacci->connect(fibonacci, &Fibonacci::inputChanged, input,
0250         [input, fibonacci]() {
0251             input->setText(QString::number(fibonacci->input()));
0252     });
0253 
0254     QLabel* result = new QLabel;
0255     fibonacci->connect(fibonacci, &Fibonacci::resultChanged, result,
0256         [result, fibonacci]() {
0257             result->setText(QCoreApplication::translate("main", "The Fibonacci number: ")
0258                 + QString::number(fibonacci->result()));
0259     });
0260     input->setText(QString::number(model->demo.fibonacci()->input()));
0261 
0262     QVBoxLayout *layout = new QVBoxLayout;
0263     layout->addWidget(label);
0264     layout->addWidget(input);
0265     layout->addWidget(result);
0266     view->setLayout(layout);
0267     return view;
0268 }
0269 
0270 QWidget* createListTab(Model* model) {
0271     QTableView* view = new QTableView();
0272     model->demo.fibonacciList()->setHeaderData(0, Qt::Horizontal,
0273         QCoreApplication::translate("main", "Row"), Qt::DisplayRole);
0274     model->demo.fibonacciList()->setHeaderData(1, Qt::Horizontal,
0275         QCoreApplication::translate("main", "Fibonacci number"), Qt::DisplayRole);
0276     view->setModel(model->demo.fibonacciList());
0277     view->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
0278     return view;
0279 }
0280 
0281 QWidget* createTreeTab(Model* model) {
0282     QTreeView* view = new QTreeView();
0283     model->demo.fileSystemTree()->setHeaderData(0, Qt::Horizontal,
0284         QCoreApplication::translate("main", "Name"), Qt::DisplayRole);
0285     model->demo.fileSystemTree()->setHeaderData(1, Qt::Horizontal,
0286         QCoreApplication::translate("main", "Size"), Qt::DisplayRole);
0287     view->setUniformRowHeights(true);
0288     view->setSortingEnabled(true);
0289     view->setModel(&model->sortedFileSystem);
0290     view->header()->setSectionHidden(2, true);
0291     view->header()->setSectionHidden(3, true);
0292     view->header()->setSectionHidden(4, true);
0293     auto root = model->sortedFileSystem.index(0, 0);
0294     view->expand(root);
0295     view->sortByColumn(0, Qt::AscendingOrder);
0296     view->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
0297     return view;
0298 }
0299 
0300 QWidget* createProcessesTab(Model* model) {
0301     QTreeView* view = new QTreeView();
0302     view->setUniformRowHeights(true);
0303     view->setSortingEnabled(true);
0304     view->setModel(&model->sortedProcesses);
0305     // expand when the model is populated
0306     view->connect(&model->sortedProcesses, &QAbstractItemModel::rowsInserted,
0307         view, [view](const QModelIndex& index) {
0308         if (!index.isValid()) {
0309             view->expandAll();
0310         }
0311     });
0312     view->expandAll();
0313     view->sortByColumn(0, Qt::AscendingOrder);
0314     return view;
0315 }
0316 
0317 #ifdef QT_CHARTS_LIB
0318 
0319 using namespace QtCharts;
0320 
0321 QWidget* createChartTab(Model* model) {
0322     QLineSeries *sin = new QLineSeries();
0323     sin->setName("sin");
0324     QVXYModelMapper *sinMapper = new QVXYModelMapper(sin);
0325     sinMapper->setXColumn(0);
0326     sinMapper->setYColumn(1);
0327     sinMapper->setSeries(sin);
0328     sinMapper->setModel(model->demo.timeSeries());
0329 
0330     QLineSeries *cos = new QLineSeries();
0331     cos->setName("cos");
0332     QVXYModelMapper *cosMapper = new QVXYModelMapper(cos);
0333     cosMapper->setXColumn(0);
0334     cosMapper->setYColumn(2);
0335     cosMapper->setSeries(cos);
0336     cosMapper->setModel(model->demo.timeSeries());
0337 
0338     QChart* chart = new QChart;
0339     chart->addSeries(sin);
0340     chart->addSeries(cos);
0341 
0342     QValueAxis *axisX = new QValueAxis;
0343     axisX->setLabelFormat("%.1f");
0344     axisX->setTitleText("time [s]");
0345     chart->addAxis(axisX, Qt::AlignBottom);
0346     sin->attachAxis(axisX);
0347     cos->attachAxis(axisX);
0348 
0349     QValueAxis *axisY = new QValueAxis;
0350     axisY->setLabelFormat("%.1f");
0351     axisY->setTitleText("electric potential [V]");
0352     chart->addAxis(axisY, Qt::AlignLeft);
0353     sin->attachAxis(axisY);
0354     cos->attachAxis(axisY);
0355 
0356     QWidget* view = new QWidget;
0357     QTableView* data = new QTableView;
0358     data->setModel(model->demo.timeSeries());
0359     QChartView *chartView = new QChartView(chart);
0360 
0361     QHBoxLayout *layout = new QHBoxLayout;
0362     layout->addWidget(data);
0363     layout->addWidget(chartView);
0364     view->setLayout(layout);
0365     return view;
0366 }
0367 #endif
0368 
0369 QTabWidget* createTabs(Model* model) {
0370     QTabWidget* tabs = new QTabWidget();
0371     tabs->addTab(createObjectTab(model), "object");
0372     tabs->addTab(createListTab(model), "list");
0373     tabs->addTab(createTreeTab(model), "tree");
0374     const int procTab = tabs->addTab(createProcessesTab(model), "processes");
0375     tabs->connect(tabs, &QTabWidget::currentChanged, model->demo.processes(),
0376         [model, procTab](int current) {
0377             model->demo.processes()->setActive(current == procTab);
0378         });
0379 #ifdef QT_CHARTS_LIB
0380     tabs->addTab(createChartTab(model), "chart");
0381 #endif
0382     return tabs;
0383 }
0384 
0385 void createMainWindow(Model* model, const QString& initialStyle,
0386                       const QString& initialTab) {
0387     QMainWindow* main = new QMainWindow();
0388     main->setMinimumSize(QSize(800, 640));
0389 
0390     QComboBox* box = createStyleComboBox(model);
0391     QStatusBar* statusBar = createStatusBar(model, main, box, initialTab);
0392     main->setStatusBar(statusBar);
0393     QTabWidget* tabs = createTabs(model);
0394     main->setCentralWidget(tabs);
0395     main->show();
0396     box->setCurrentText(initialStyle);
0397     for (int i = 0; i < tabs->count(); ++i) {
0398         if (tabs->tabText(i) == initialTab) {
0399             tabs->setCurrentIndex(i);
0400         }
0401     }
0402 }
0403 
0404 int main (int argc, char *argv[])
0405 {
0406     QApplication app(argc, argv);
0407     app.setWindowIcon(QIcon(":/rust_qt_binding_generator.svg"));
0408 
0409 #ifdef QT_QUICK_LIB
0410     qmlRegisterType<QSortFilterProxyModel>("org.qtproject.example", 1, 0, "SortFilterProxyModel");
0411     qmlRegisterType<Fibonacci>("rust", 1, 0, "Fibonacci");
0412     qmlRegisterType<FibonacciList>("rust", 1, 0, "FibonacciList");
0413 #endif
0414 
0415     QCommandLineParser parser;
0416     parser.setApplicationDescription("Demo application for Qt and RUst");
0417     parser.addHelpOption();
0418     parser.addVersionOption();
0419     // --initial-style
0420     QCommandLineOption initialStyleOption(QStringList()
0421             << "initial-style",
0422             QCoreApplication::translate("main", "Initial widget style."),
0423             QCoreApplication::translate("main", "style"));
0424     parser.addOption(initialStyleOption);
0425     // --initial-tab
0426     QCommandLineOption initialTabOption(QStringList()
0427             << "initial-tab",
0428             QCoreApplication::translate("main", "Initial tab."),
0429             QCoreApplication::translate("main", "tab"));
0430     parser.addOption(initialTabOption);
0431     parser.process(app);
0432 
0433     Model model;
0434     model.demo.fibonacci()->setInput(1);
0435     model.demo.fileSystemTree()->setPath("/");
0436     model.sortedFileSystem.setSourceModel(model.demo.fileSystemTree());
0437     model.sortedFileSystem.setDynamicSortFilter(true);
0438     model.sortedProcesses.setSourceModel(model.demo.processes());
0439     model.sortedProcesses.setDynamicSortFilter(true);
0440 
0441     createMainWindow(&model, parser.value(initialStyleOption),
0442                              parser.value(initialTabOption));
0443 
0444     return app.exec();
0445 }