File indexing completed on 2024-09-15 13:08:48
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 }