diff options
Diffstat (limited to 'tests/manual/examples/widgets/mainwindows')
38 files changed, 3194 insertions, 0 deletions
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/CMakeLists.txt b/tests/manual/examples/widgets/mainwindows/dockwidgets/CMakeLists.txt new file mode 100644 index 0000000000..fd8444ae65 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(dockwidgets LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/dockwidgets") + +find_package(Qt6 + REQUIRED COMPONENTS Core Gui Widgets + OPTIONAL_COMPONENTS PrintSupport +) + +qt_standard_project_setup() + +qt_add_executable(dockwidgets + main.cpp + mainwindow.cpp mainwindow.h +) + +set_target_properties(dockwidgets PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(dockwidgets PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +if (TARGET Qt6::PrintSupport) + target_link_libraries(dockwidgets PRIVATE Qt6::PrintSupport) +endif() + +# Resources: +set(dockwidgets_resource_files + "images/new.png" + "images/print.png" + "images/save.png" + "images/undo.png" +) + +qt_add_resources(dockwidgets "dockwidgets" + PREFIX + "/" + FILES + ${dockwidgets_resource_files} +) + +install(TARGETS dockwidgets + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.pro b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.pro new file mode 100644 index 0000000000..3acded5ed3 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.pro @@ -0,0 +1,12 @@ +QT += widgets +requires(qtConfig(listwidget)) +qtHaveModule(printsupport): QT += printsupport + +HEADERS = mainwindow.h +SOURCES = main.cpp \ + mainwindow.cpp +RESOURCES = dockwidgets.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/dockwidgets +INSTALLS += target diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.qrc b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.qrc new file mode 100644 index 0000000000..968feac7ea --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/new.png</file> + <file>images/print.png</file> + <file>images/save.png</file> + <file>images/undo.png</file> +</qresource> +</RCC> diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/new.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/new.png Binary files differnew file mode 100644 index 0000000000..dd795cfffc --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/new.png diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/print.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/print.png Binary files differnew file mode 100644 index 0000000000..2afb769ee2 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/print.png diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/save.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/save.png Binary files differnew file mode 100644 index 0000000000..46eac82ad1 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/save.png diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/undo.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/undo.png Binary files differnew file mode 100644 index 0000000000..eee23d24a3 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/undo.png diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/main.cpp b/tests/manual/examples/widgets/mainwindows/dockwidgets/main.cpp new file mode 100644 index 0000000000..431d7dae98 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow mainWin; + mainWin.show(); + return app.exec(); +} diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.cpp b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.cpp new file mode 100644 index 0000000000..3493a66a67 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.cpp @@ -0,0 +1,298 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [0] +#include <QtWidgets> +#if defined(QT_PRINTSUPPORT_LIB) +#include <QtPrintSupport/qtprintsupportglobal.h> +#if QT_CONFIG(printdialog) +#include <QtPrintSupport> +#endif +#endif + +#include "mainwindow.h" +//! [0] + +//! [1] +MainWindow::MainWindow() + : textEdit(new QTextEdit) +{ + setCentralWidget(textEdit); + + createActions(); + createStatusBar(); + createDockWindows(); + + setWindowTitle(tr("Dock Widgets")); + + newLetter(); + setUnifiedTitleAndToolBarOnMac(true); +} +//! [1] + +//! [2] +void MainWindow::newLetter() +{ + textEdit->clear(); + + QTextCursor cursor(textEdit->textCursor()); + cursor.movePosition(QTextCursor::Start); + QTextFrame *topFrame = cursor.currentFrame(); + QTextFrameFormat topFrameFormat = topFrame->frameFormat(); + topFrameFormat.setPadding(16); + topFrame->setFrameFormat(topFrameFormat); + + QTextCharFormat textFormat; + QTextCharFormat boldFormat; + boldFormat.setFontWeight(QFont::Bold); + QTextCharFormat italicFormat; + italicFormat.setFontItalic(true); + + QTextTableFormat tableFormat; + tableFormat.setBorder(1); + tableFormat.setCellPadding(16); + tableFormat.setAlignment(Qt::AlignRight); + cursor.insertTable(1, 1, tableFormat); + cursor.insertText("The Firm", boldFormat); + cursor.insertBlock(); + cursor.insertText("321 City Street", textFormat); + cursor.insertBlock(); + cursor.insertText("Industry Park"); + cursor.insertBlock(); + cursor.insertText("Some Country"); + cursor.setPosition(topFrame->lastPosition()); + cursor.insertText(QDate::currentDate().toString("d MMMM yyyy"), textFormat); + cursor.insertBlock(); + cursor.insertBlock(); + cursor.insertText("Dear ", textFormat); + cursor.insertText("NAME", italicFormat); + cursor.insertText(",", textFormat); + for (int i = 0; i < 3; ++i) + cursor.insertBlock(); + cursor.insertText(tr("Yours sincerely,"), textFormat); + for (int i = 0; i < 3; ++i) + cursor.insertBlock(); + cursor.insertText("The Boss", textFormat); + cursor.insertBlock(); + cursor.insertText("ADDRESS", italicFormat); +} +//! [2] + +//! [3] +void MainWindow::print() +{ +#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog) + QTextDocument *document = textEdit->document(); + QPrinter printer; + + QPrintDialog dlg(&printer, this); + if (dlg.exec() != QDialog::Accepted) { + return; + } + + document->print(&printer); + statusBar()->showMessage(tr("Ready"), 2000); +#endif +} +//! [3] + +//! [4] +void MainWindow::save() +{ + QMimeDatabase mimeDatabase; + QString fileName = QFileDialog::getSaveFileName(this, + tr("Choose a file name"), ".", + mimeDatabase.mimeTypeForName("text/html").filterString()); + if (fileName.isEmpty()) + return; + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("Dock Widgets"), + tr("Cannot write file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString())); + return; + } + + QTextStream out(&file); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); + out << textEdit->toHtml(); + QGuiApplication::restoreOverrideCursor(); + + statusBar()->showMessage(tr("Saved '%1'").arg(fileName), 2000); +} +//! [4] + +//! [5] +void MainWindow::undo() +{ + QTextDocument *document = textEdit->document(); + document->undo(); +} +//! [5] + +//! [6] +void MainWindow::insertCustomer(const QString &customer) +{ + if (customer.isEmpty()) + return; + QStringList customerList = customer.split(", "); + QTextDocument *document = textEdit->document(); + QTextCursor cursor = document->find("NAME"); + if (!cursor.isNull()) { + cursor.beginEditBlock(); + cursor.insertText(customerList.at(0)); + QTextCursor oldcursor = cursor; + cursor = document->find("ADDRESS"); + if (!cursor.isNull()) { + for (int i = 1; i < customerList.size(); ++i) { + cursor.insertBlock(); + cursor.insertText(customerList.at(i)); + } + cursor.endEditBlock(); + } + else + oldcursor.endEditBlock(); + } +} +//! [6] + +//! [7] +void MainWindow::addParagraph(const QString ¶graph) +{ + if (paragraph.isEmpty()) + return; + QTextDocument *document = textEdit->document(); + QTextCursor cursor = document->find(tr("Yours sincerely,")); + if (cursor.isNull()) + return; + cursor.beginEditBlock(); + cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, 2); + cursor.insertBlock(); + cursor.insertText(paragraph); + cursor.insertBlock(); + cursor.endEditBlock(); + +} +//! [7] + +void MainWindow::about() +{ + QMessageBox::about(this, tr("About Dock Widgets"), + tr("The <b>Dock Widgets</b> example demonstrates how to " + "use Qt's dock widgets. You can enter your own text, " + "click a customer to add a customer name and " + "address, and click standard paragraphs to add them.")); +} + +void MainWindow::createActions() +{ + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + QToolBar *fileToolBar = addToolBar(tr("File")); + + const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png")); + QAction *newLetterAct = new QAction(newIcon, tr("&New Letter"), this); + newLetterAct->setShortcuts(QKeySequence::New); + newLetterAct->setStatusTip(tr("Create a new form letter")); + connect(newLetterAct, &QAction::triggered, this, &MainWindow::newLetter); + fileMenu->addAction(newLetterAct); + fileToolBar->addAction(newLetterAct); + + const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(":/images/save.png")); + QAction *saveAct = new QAction(saveIcon, tr("&Save..."), this); + saveAct->setShortcuts(QKeySequence::Save); + saveAct->setStatusTip(tr("Save the current form letter")); + connect(saveAct, &QAction::triggered, this, &MainWindow::save); + fileMenu->addAction(saveAct); + fileToolBar->addAction(saveAct); + + const QIcon printIcon = QIcon::fromTheme("document-print", QIcon(":/images/print.png")); + QAction *printAct = new QAction(printIcon, tr("&Print..."), this); + printAct->setShortcuts(QKeySequence::Print); + printAct->setStatusTip(tr("Print the current form letter")); + connect(printAct, &QAction::triggered, this, &MainWindow::print); + fileMenu->addAction(printAct); + fileToolBar->addAction(printAct); + + fileMenu->addSeparator(); + + QAction *quitAct = fileMenu->addAction(tr("&Quit"), qApp, &QCoreApplication::quit); + quitAct->setShortcuts(QKeySequence::Quit); + quitAct->setStatusTip(tr("Quit the application")); + + QMenu *editMenu = menuBar()->addMenu(tr("&Edit")); + QToolBar *editToolBar = addToolBar(tr("Edit")); + const QIcon undoIcon = QIcon::fromTheme("edit-undo", QIcon(":/images/undo.png")); + QAction *undoAct = new QAction(undoIcon, tr("&Undo"), this); + undoAct->setShortcuts(QKeySequence::Undo); + undoAct->setStatusTip(tr("Undo the last editing action")); + connect(undoAct, &QAction::triggered, this, &MainWindow::undo); + editMenu->addAction(undoAct); + editToolBar->addAction(undoAct); + + viewMenu = menuBar()->addMenu(tr("&View")); + + menuBar()->addSeparator(); + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + + QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about); + aboutAct->setStatusTip(tr("Show the application's About box")); + + QAction *aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); + aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); +} + +//! [8] +void MainWindow::createStatusBar() +{ + statusBar()->showMessage(tr("Ready")); +} +//! [8] + +//! [9] +void MainWindow::createDockWindows() +{ + QDockWidget *dock = new QDockWidget(tr("Customers"), this); + dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + customerList = new QListWidget(dock); + customerList->addItems(QStringList() + << "John Doe, Harmony Enterprises, 12 Lakeside, Ambleton" + << "Jane Doe, Memorabilia, 23 Watersedge, Beaton" + << "Tammy Shea, Tiblanka, 38 Sea Views, Carlton" + << "Tim Sheen, Caraba Gifts, 48 Ocean Way, Deal" + << "Sol Harvey, Chicos Coffee, 53 New Springs, Eccleston" + << "Sally Hobart, Tiroli Tea, 67 Long River, Fedula"); + dock->setWidget(customerList); + addDockWidget(Qt::RightDockWidgetArea, dock); + viewMenu->addAction(dock->toggleViewAction()); + + dock = new QDockWidget(tr("Paragraphs"), this); + paragraphsList = new QListWidget(dock); + paragraphsList->addItems(QStringList() + << "Thank you for your payment which we have received today." + << "Your order has been dispatched and should be with you " + "within 28 days." + << "We have dispatched those items that were in stock. The " + "rest of your order will be dispatched once all the " + "remaining items have arrived at our warehouse. No " + "additional shipping charges will be made." + << "You made a small overpayment (less than $5) which we " + "will keep on account for you, or return at your request." + << "You made a small underpayment (less than $1), but we have " + "sent your order anyway. We'll add this underpayment to " + "your next bill." + << "Unfortunately you did not send enough money. Please remit " + "an additional $. Your order will be dispatched as soon as " + "the complete amount has been received." + << "You made an overpayment (more than $5). Do you wish to " + "buy more items, or should we return the excess to you?"); + dock->setWidget(paragraphsList); + addDockWidget(Qt::RightDockWidgetArea, dock); + viewMenu->addAction(dock->toggleViewAction()); + + connect(customerList, &QListWidget::currentTextChanged, + this, &MainWindow::insertCustomer); + connect(paragraphsList, &QListWidget::currentTextChanged, + this, &MainWindow::addParagraph); +} +//! [9] diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.h b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.h new file mode 100644 index 0000000000..67890e8a61 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.h @@ -0,0 +1,46 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +class QAction; +class QListWidget; +class QMenu; +class QTextEdit; +QT_END_NAMESPACE + +//! [0] +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + +private slots: + void newLetter(); + void save(); + void print(); + void undo(); + void about(); + void insertCustomer(const QString &customer); + void addParagraph(const QString ¶graph); + +private: + void createActions(); + void createStatusBar(); + void createDockWindows(); + + QTextEdit *textEdit; + QListWidget *customerList; + QListWidget *paragraphsList; + + QMenu *viewMenu; +}; +//! [0] + +#endif diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/CMakeLists.txt b/tests/manual/examples/widgets/mainwindows/mainwindow/CMakeLists.txt new file mode 100644 index 0000000000..bab95a9145 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(mainwindow LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/mainwindow") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(mainwindow + colorswatch.cpp colorswatch.h + main.cpp + mainwindow.cpp mainwindow.h + toolbar.cpp toolbar.h +) + +set_target_properties(mainwindow PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(mainwindow PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +# Resources: +set(mainwindow_resource_files + "qt.png" + "titlebarCenter.png" + "titlebarLeft.png" + "titlebarRight.png" +) + +qt_add_resources(mainwindow "mainwindow" + PREFIX + "/res" + FILES + ${mainwindow_resource_files} +) + +install(TARGETS mainwindow + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.cpp new file mode 100644 index 0000000000..678f00054c --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.cpp @@ -0,0 +1,685 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "colorswatch.h" + +#include <QActionGroup> +#include <QtEvents> +#include <QFrame> +#include <QMainWindow> +#include <QMenu> +#include <QPainter> +#include <QImage> +#include <QColor> +#include <QDialog> +#include <QDialogButtonBox> +#include <QGridLayout> +#include <QSignalBlocker> +#include <QSpinBox> +#include <QLabel> +#include <QPainterPath> +#include <QPushButton> +#include <QHBoxLayout> +#include <QBitmap> +#include <QtDebug> + +#undef DEBUG_SIZEHINTS + +QColor bgColorForName(const QString &name) +{ + if (name == "Black") + return QColor("#D8D8D8"); + if (name == "White") + return QColor("#F1F1F1"); + if (name == "Red") + return QColor("#F1D8D8"); + if (name == "Green") + return QColor("#D8E4D8"); + if (name == "Blue") + return QColor("#D8D8F1"); + if (name == "Yellow") + return QColor("#F1F0D8"); + return QColor(name).lighter(110); +} + +QColor fgColorForName(const QString &name) +{ + if (name == "Black") + return QColor("#6C6C6C"); + if (name == "White") + return QColor("#F8F8F8"); + if (name == "Red") + return QColor("#F86C6C"); + if (name == "Green") + return QColor("#6CB26C"); + if (name == "Blue") + return QColor("#6C6CF8"); + if (name == "Yellow") + return QColor("#F8F76C"); + return QColor(name); +} + +class ColorDock : public QFrame +{ + Q_OBJECT +public: + explicit ColorDock(const QString &c, QWidget *parent); + + QSize sizeHint() const override { return szHint; } + QSize minimumSizeHint() const override { return minSzHint; } + + void setCustomSizeHint(const QSize &size); + +public slots: + void changeSizeHints(); + +protected: + void paintEvent(QPaintEvent *) override; + +private: + const QString color; + QSize szHint; + QSize minSzHint; +}; + +ColorDock::ColorDock(const QString &c, QWidget *parent) + : QFrame(parent) + , color(c) + , szHint(-1, -1) + , minSzHint(125, 75) +{ + QFont font = this->font(); + font.setPointSize(8); + setFont(font); +} + +void ColorDock::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + p.fillRect(rect(), bgColorForName(color)); + + p.save(); + + extern void render_qt_text(QPainter *, int, int, const QColor &); + render_qt_text(&p, width(), height(), fgColorForName(color)); + + p.restore(); + +#ifdef DEBUG_SIZEHINTS + p.setRenderHint(QPainter::Antialiasing, false); + + QSize sz = size(); + QSize szHint = sizeHint(); + QSize minSzHint = minimumSizeHint(); + QSize maxSz = maximumSize(); + QString text = QString::fromLatin1("sz: %1x%2\nszHint: %3x%4\nminSzHint: %5x%6\n" + "maxSz: %8x%9") + .arg(sz.width()).arg(sz.height()) + .arg(szHint.width()).arg(szHint.height()) + .arg(minSzHint.width()).arg(minSzHint.height()) + .arg(maxSz.width()).arg(maxSz.height()); + + QRect r = fontMetrics().boundingRect(rect(), Qt::AlignLeft|Qt::AlignTop, text); + r.adjust(-2, -2, 1, 1); + p.translate(4, 4); + QColor bg = Qt::yellow; + bg.setAlpha(120); + p.setBrush(bg); + p.setPen(Qt::black); + p.drawRect(r); + p.drawText(rect(), Qt::AlignLeft|Qt::AlignTop, text); +#endif // DEBUG_SIZEHINTS +} + +static QSpinBox *createSpinBox(int value, QWidget *parent, int max = 1000) +{ + QSpinBox *result = new QSpinBox(parent); + result->setMinimum(-1); + result->setMaximum(max); + result->setValue(value); + return result; +} + +void ColorDock::changeSizeHints() +{ + QDialog dialog(this); + dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); + dialog.setWindowTitle(color); + + QVBoxLayout *topLayout = new QVBoxLayout(&dialog); + + QGridLayout *inputLayout = new QGridLayout(); + topLayout->addLayout(inputLayout); + + inputLayout->addWidget(new QLabel(tr("Size Hint:"), &dialog), 0, 0); + inputLayout->addWidget(new QLabel(tr("Min Size Hint:"), &dialog), 1, 0); + inputLayout->addWidget(new QLabel(tr("Max Size:"), &dialog), 2, 0); + inputLayout->addWidget(new QLabel(tr("Dock Widget Max Size:"), &dialog), 3, 0); + + QSpinBox *szHintW = createSpinBox(szHint.width(), &dialog); + inputLayout->addWidget(szHintW, 0, 1); + QSpinBox *szHintH = createSpinBox(szHint.height(), &dialog); + inputLayout->addWidget(szHintH, 0, 2); + + QSpinBox *minSzHintW = createSpinBox(minSzHint.width(), &dialog); + inputLayout->addWidget(minSzHintW, 1, 1); + QSpinBox *minSzHintH = createSpinBox(minSzHint.height(), &dialog); + inputLayout->addWidget(minSzHintH, 1, 2); + + QSize maxSz = maximumSize(); + QSpinBox *maxSzW = createSpinBox(maxSz.width(), &dialog, QWIDGETSIZE_MAX); + inputLayout->addWidget(maxSzW, 2, 1); + QSpinBox *maxSzH = createSpinBox(maxSz.height(), &dialog, QWIDGETSIZE_MAX); + inputLayout->addWidget(maxSzH, 2, 2); + + QSize dwMaxSz = parentWidget()->maximumSize(); + QSpinBox *dwMaxSzW = createSpinBox(dwMaxSz.width(), &dialog, QWIDGETSIZE_MAX); + inputLayout->addWidget(dwMaxSzW, 3, 1); + QSpinBox *dwMaxSzH = createSpinBox(dwMaxSz.height(), &dialog, QWIDGETSIZE_MAX); + inputLayout->addWidget(dwMaxSzH, 3, 2); + + inputLayout->setColumnStretch(1, 1); + inputLayout->setColumnStretch(2, 1); + + topLayout->addStretch(); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + + topLayout->addWidget(buttonBox); + + if (dialog.exec() != QDialog::Accepted) + return; + + szHint = QSize(szHintW->value(), szHintH->value()); + minSzHint = QSize(minSzHintW->value(), minSzHintH->value()); + maxSz = QSize(maxSzW->value(), maxSzH->value()); + setMaximumSize(maxSz); + dwMaxSz = QSize(dwMaxSzW->value(), dwMaxSzH->value()); + parentWidget()->setMaximumSize(dwMaxSz); + updateGeometry(); + update(); +} + +void ColorDock::setCustomSizeHint(const QSize &size) +{ + if (szHint != size) { + szHint = size; + updateGeometry(); + } +} + +ColorSwatch::ColorSwatch(const QString &colorName, QMainWindow *parent, Qt::WindowFlags flags) + : QDockWidget(parent, flags), mainWindow(parent) +{ + setObjectName(colorName + QLatin1String(" Dock Widget")); + setWindowTitle(objectName() + QLatin1String(" [*]")); + + ColorDock *swatch = new ColorDock(colorName, this); + swatch->setFrameStyle(QFrame::Box | QFrame::Sunken); + + setWidget(swatch); + + closableAction = new QAction(tr("Closable"), this); + closableAction->setCheckable(true); + connect(closableAction, &QAction::triggered, this, &ColorSwatch::changeClosable); + + movableAction = new QAction(tr("Movable"), this); + movableAction->setCheckable(true); + connect(movableAction, &QAction::triggered, this, &ColorSwatch::changeMovable); + + floatableAction = new QAction(tr("Floatable"), this); + floatableAction->setCheckable(true); + connect(floatableAction, &QAction::triggered, this, &ColorSwatch::changeFloatable); + + verticalTitleBarAction = new QAction(tr("Vertical title bar"), this); + verticalTitleBarAction->setCheckable(true); + connect(verticalTitleBarAction, &QAction::triggered, + this, &ColorSwatch::changeVerticalTitleBar); + + floatingAction = new QAction(tr("Floating"), this); + floatingAction->setCheckable(true); + connect(floatingAction, &QAction::triggered, this, &ColorSwatch::changeFloating); + + allowedAreasActions = new QActionGroup(this); + allowedAreasActions->setExclusive(false); + + allowLeftAction = new QAction(tr("Allow on Left"), this); + allowLeftAction->setCheckable(true); + connect(allowLeftAction, &QAction::triggered, this, &ColorSwatch::allowLeft); + + allowRightAction = new QAction(tr("Allow on Right"), this); + allowRightAction->setCheckable(true); + connect(allowRightAction, &QAction::triggered, this, &ColorSwatch::allowRight); + + allowTopAction = new QAction(tr("Allow on Top"), this); + allowTopAction->setCheckable(true); + connect(allowTopAction, &QAction::triggered, this, &ColorSwatch::allowTop); + + allowBottomAction = new QAction(tr("Allow on Bottom"), this); + allowBottomAction->setCheckable(true); + connect(allowBottomAction, &QAction::triggered, this, &ColorSwatch::allowBottom); + + allowedAreasActions->addAction(allowLeftAction); + allowedAreasActions->addAction(allowRightAction); + allowedAreasActions->addAction(allowTopAction); + allowedAreasActions->addAction(allowBottomAction); + + areaActions = new QActionGroup(this); + areaActions->setExclusive(true); + + leftAction = new QAction(tr("Place on Left") , this); + leftAction->setCheckable(true); + connect(leftAction, &QAction::triggered, this, &ColorSwatch::placeLeft); + + rightAction = new QAction(tr("Place on Right") , this); + rightAction->setCheckable(true); + connect(rightAction, &QAction::triggered, this, &ColorSwatch::placeRight); + + topAction = new QAction(tr("Place on Top") , this); + topAction->setCheckable(true); + connect(topAction, &QAction::triggered, this, &ColorSwatch::placeTop); + + bottomAction = new QAction(tr("Place on Bottom") , this); + bottomAction->setCheckable(true); + connect(bottomAction, &QAction::triggered, this, &ColorSwatch::placeBottom); + + areaActions->addAction(leftAction); + areaActions->addAction(rightAction); + areaActions->addAction(topAction); + areaActions->addAction(bottomAction); + + connect(movableAction, &QAction::triggered, areaActions, &QActionGroup::setEnabled); + + connect(movableAction, &QAction::triggered, allowedAreasActions, &QActionGroup::setEnabled); + + connect(floatableAction, &QAction::triggered, floatingAction, &QAction::setEnabled); + + connect(floatingAction, &QAction::triggered, floatableAction, &QAction::setDisabled); + connect(movableAction, &QAction::triggered, floatableAction, &QAction::setEnabled); + + tabMenu = new QMenu(this); + tabMenu->setTitle(tr("Tab into")); + connect(tabMenu, &QMenu::triggered, this, &ColorSwatch::tabInto); + + splitHMenu = new QMenu(this); + splitHMenu->setTitle(tr("Split horizontally into")); + connect(splitHMenu, &QMenu::triggered, this, &ColorSwatch::splitInto); + + splitVMenu = new QMenu(this); + splitVMenu->setTitle(tr("Split vertically into")); + connect(splitVMenu, &QMenu::triggered, this, &ColorSwatch::splitInto); + + QAction *windowModifiedAction = new QAction(tr("Modified"), this); + windowModifiedAction->setCheckable(true); + windowModifiedAction->setChecked(false); + connect(windowModifiedAction, &QAction::toggled, this, &QWidget::setWindowModified); + + menu = new QMenu(colorName, this); + menu->addAction(toggleViewAction()); + menu->addAction(tr("Raise"), this, &QWidget::raise); + menu->addAction(tr("Change Size Hints..."), swatch, &ColorDock::changeSizeHints); + + menu->addSeparator(); + menu->addAction(closableAction); + menu->addAction(movableAction); + menu->addAction(floatableAction); + menu->addAction(floatingAction); + menu->addAction(verticalTitleBarAction); + menu->addSeparator(); + menu->addActions(allowedAreasActions->actions()); + menu->addSeparator(); + menu->addActions(areaActions->actions()); + menu->addSeparator(); + menu->addMenu(splitHMenu); + menu->addMenu(splitVMenu); + menu->addMenu(tabMenu); + menu->addSeparator(); + menu->addAction(windowModifiedAction); + + connect(menu, &QMenu::aboutToShow, this, &ColorSwatch::updateContextMenu); + + if (colorName == QLatin1String("Black")) { + leftAction->setShortcut(Qt::CTRL | Qt::Key_W); + rightAction->setShortcut(Qt::CTRL | Qt::Key_E); + toggleViewAction()->setShortcut(Qt::CTRL | Qt::Key_R); + } +} + +void ColorSwatch::updateContextMenu() +{ + const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(this); + const Qt::DockWidgetAreas areas = allowedAreas(); + + closableAction->setChecked(features() & QDockWidget::DockWidgetClosable); + if (windowType() == Qt::Drawer) { + floatableAction->setEnabled(false); + floatingAction->setEnabled(false); + movableAction->setEnabled(false); + verticalTitleBarAction->setChecked(false); + } else { + floatableAction->setChecked(features() & QDockWidget::DockWidgetFloatable); + floatingAction->setChecked(isWindow()); + // done after floating, to get 'floatable' correctly initialized + movableAction->setChecked(features() & QDockWidget::DockWidgetMovable); + verticalTitleBarAction + ->setChecked(features() & QDockWidget::DockWidgetVerticalTitleBar); + } + + allowLeftAction->setChecked(isAreaAllowed(Qt::LeftDockWidgetArea)); + allowRightAction->setChecked(isAreaAllowed(Qt::RightDockWidgetArea)); + allowTopAction->setChecked(isAreaAllowed(Qt::TopDockWidgetArea)); + allowBottomAction->setChecked(isAreaAllowed(Qt::BottomDockWidgetArea)); + + if (allowedAreasActions->isEnabled()) { + allowLeftAction->setEnabled(area != Qt::LeftDockWidgetArea); + allowRightAction->setEnabled(area != Qt::RightDockWidgetArea); + allowTopAction->setEnabled(area != Qt::TopDockWidgetArea); + allowBottomAction->setEnabled(area != Qt::BottomDockWidgetArea); + } + + { + const QSignalBlocker blocker(leftAction); + leftAction->setChecked(area == Qt::LeftDockWidgetArea); + } + { + const QSignalBlocker blocker(rightAction); + rightAction->setChecked(area == Qt::RightDockWidgetArea); + } + { + const QSignalBlocker blocker(topAction); + topAction->setChecked(area == Qt::TopDockWidgetArea); + } + { + const QSignalBlocker blocker(bottomAction); + bottomAction->setChecked(area == Qt::BottomDockWidgetArea); + } + + if (areaActions->isEnabled()) { + leftAction->setEnabled(areas & Qt::LeftDockWidgetArea); + rightAction->setEnabled(areas & Qt::RightDockWidgetArea); + topAction->setEnabled(areas & Qt::TopDockWidgetArea); + bottomAction->setEnabled(areas & Qt::BottomDockWidgetArea); + } + + tabMenu->clear(); + splitHMenu->clear(); + splitVMenu->clear(); + const QList<ColorSwatch *> dockList = mainWindow->findChildren<ColorSwatch*>(); + for (const ColorSwatch *dock : dockList) { + tabMenu->addAction(dock->objectName()); + splitHMenu->addAction(dock->objectName()); + splitVMenu->addAction(dock->objectName()); + } +} + +static ColorSwatch *findByName(const QMainWindow *mainWindow, const QString &name) +{ + const QList<ColorSwatch *> dockList = mainWindow->findChildren<ColorSwatch*>(); + for (ColorSwatch *dock : dockList) { + if (name == dock->objectName()) + return dock; + } + return nullptr; +} + +void ColorSwatch::splitInto(QAction *action) +{ + ColorSwatch *target = findByName(mainWindow, action->text()); + if (!target) + return; + + const Qt::Orientation o = action->parent() == splitHMenu + ? Qt::Horizontal : Qt::Vertical; + mainWindow->splitDockWidget(target, this, o); +} + +void ColorSwatch::tabInto(QAction *action) +{ + if (ColorSwatch *target = findByName(mainWindow, action->text())) + mainWindow->tabifyDockWidget(target, this); +} + +#ifndef QT_NO_CONTEXTMENU +void ColorSwatch::contextMenuEvent(QContextMenuEvent *event) +{ + event->accept(); + menu->popup(event->globalPos()); +} +#endif // QT_NO_CONTEXTMENU + +void ColorSwatch::resizeEvent(QResizeEvent *e) +{ + if (BlueTitleBar *btb = qobject_cast<BlueTitleBar*>(titleBarWidget())) + btb->updateMask(); + + QDockWidget::resizeEvent(e); +} + +void ColorSwatch::allow(Qt::DockWidgetArea area, bool a) +{ + Qt::DockWidgetAreas areas = allowedAreas(); + areas = a ? areas | area : areas & ~area; + setAllowedAreas(areas); + + if (areaActions->isEnabled()) { + leftAction->setEnabled(areas & Qt::LeftDockWidgetArea); + rightAction->setEnabled(areas & Qt::RightDockWidgetArea); + topAction->setEnabled(areas & Qt::TopDockWidgetArea); + bottomAction->setEnabled(areas & Qt::BottomDockWidgetArea); + } +} + +void ColorSwatch::place(Qt::DockWidgetArea area, bool p) +{ + if (!p) + return; + + mainWindow->addDockWidget(area, this); + + if (allowedAreasActions->isEnabled()) { + allowLeftAction->setEnabled(area != Qt::LeftDockWidgetArea); + allowRightAction->setEnabled(area != Qt::RightDockWidgetArea); + allowTopAction->setEnabled(area != Qt::TopDockWidgetArea); + allowBottomAction->setEnabled(area != Qt::BottomDockWidgetArea); + } +} + +void ColorSwatch::setCustomSizeHint(const QSize &size) +{ + if (ColorDock *dock = qobject_cast<ColorDock*>(widget())) + dock->setCustomSizeHint(size); +} + +void ColorSwatch::changeClosable(bool on) +{ setFeatures(on ? features() | DockWidgetClosable : features() & ~DockWidgetClosable); } + +void ColorSwatch::changeMovable(bool on) +{ setFeatures(on ? features() | DockWidgetMovable : features() & ~DockWidgetMovable); } + +void ColorSwatch::changeFloatable(bool on) +{ setFeatures(on ? features() | DockWidgetFloatable : features() & ~DockWidgetFloatable); } + +void ColorSwatch::changeFloating(bool floating) +{ setFloating(floating); } + +void ColorSwatch::allowLeft(bool a) +{ allow(Qt::LeftDockWidgetArea, a); } + +void ColorSwatch::allowRight(bool a) +{ allow(Qt::RightDockWidgetArea, a); } + +void ColorSwatch::allowTop(bool a) +{ allow(Qt::TopDockWidgetArea, a); } + +void ColorSwatch::allowBottom(bool a) +{ allow(Qt::BottomDockWidgetArea, a); } + +void ColorSwatch::placeLeft(bool p) +{ place(Qt::LeftDockWidgetArea, p); } + +void ColorSwatch::placeRight(bool p) +{ place(Qt::RightDockWidgetArea, p); } + +void ColorSwatch::placeTop(bool p) +{ place(Qt::TopDockWidgetArea, p); } + +void ColorSwatch::placeBottom(bool p) +{ place(Qt::BottomDockWidgetArea, p); } + +void ColorSwatch::changeVerticalTitleBar(bool on) +{ + setFeatures(on ? features() | DockWidgetVerticalTitleBar + : features() & ~DockWidgetVerticalTitleBar); +} + +QSize BlueTitleBar::minimumSizeHint() const +{ + QDockWidget *dw = qobject_cast<QDockWidget*>(parentWidget()); + Q_ASSERT(dw); + QSize result(leftPm.width() + rightPm.width(), centerPm.height()); + if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) + result.transpose(); + return result; +} + +BlueTitleBar::BlueTitleBar(QWidget *parent) + : QWidget(parent) + , leftPm(QPixmap(":/res/titlebarLeft.png")) + , centerPm(QPixmap(":/res/titlebarCenter.png")) + , rightPm(QPixmap(":/res/titlebarRight.png")) +{ +} + +void BlueTitleBar::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + QRect rect = this->rect(); + + QDockWidget *dw = qobject_cast<QDockWidget*>(parentWidget()); + Q_ASSERT(dw); + + if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) { + QSize s = rect.size(); + s.transpose(); + rect.setSize(s); + + painter.translate(rect.left(), rect.top() + rect.width()); + painter.rotate(-90); + painter.translate(-rect.left(), -rect.top()); + } + + painter.drawPixmap(rect.topLeft(), leftPm); + painter.drawPixmap(rect.topRight() - QPoint(rightPm.width() - 1, 0), rightPm); + QBrush brush(centerPm); + painter.fillRect(rect.left() + leftPm.width(), rect.top(), + rect.width() - leftPm.width() - rightPm.width(), + centerPm.height(), centerPm); +} + +void BlueTitleBar::mouseReleaseEvent(QMouseEvent *event) +{ + QPoint pos = event->position().toPoint(); + + QRect rect = this->rect(); + + QDockWidget *dw = qobject_cast<QDockWidget*>(parentWidget()); + Q_ASSERT(dw); + + if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) { + QPoint p = pos; + pos.setX(rect.left() + rect.bottom() - p.y()); + pos.setY(rect.top() + p.x() - rect.left()); + + QSize s = rect.size(); + s.transpose(); + rect.setSize(s); + } + + const int buttonRight = 7; + const int buttonWidth = 20; + int right = rect.right() - pos.x(); + int button = (right - buttonRight)/buttonWidth; + switch (button) { + case 0: + event->accept(); + dw->close(); + break; + case 1: + event->accept(); + dw->setFloating(!dw->isFloating()); + break; + case 2: { + event->accept(); + QDockWidget::DockWidgetFeatures features = dw->features(); + if (features & QDockWidget::DockWidgetVerticalTitleBar) + features &= ~QDockWidget::DockWidgetVerticalTitleBar; + else + features |= QDockWidget::DockWidgetVerticalTitleBar; + dw->setFeatures(features); + break; + } + default: + event->ignore(); + break; + } +} + +void BlueTitleBar::updateMask() +{ + QDockWidget *dw = qobject_cast<QDockWidget*>(parent()); + Q_ASSERT(dw); + + QRect rect = dw->rect(); + QBitmap bitmap(dw->size()); + + { + QPainter painter(&bitmap); + + // initialize to transparent + painter.fillRect(rect, Qt::color0); + + QRect contents = rect; + contents.setTopLeft(geometry().bottomLeft()); + contents.setRight(geometry().right()); + contents.setBottom(contents.bottom()-y()); + painter.fillRect(contents, Qt::color1); + + // let's paint the titlebar + QRect titleRect = this->geometry(); + + if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) { + QSize s = rect.size(); + s.transpose(); + rect.setSize(s); + + QSize s2 = size(); + s2.transpose(); + titleRect.setSize(s2); + + painter.translate(rect.left(), rect.top() + rect.width()); + painter.rotate(-90); + painter.translate(-rect.left(), -rect.top()); + } + + contents.setTopLeft(titleRect.bottomLeft()); + contents.setRight(titleRect.right()); + contents.setBottom(rect.bottom()-y()); + + QRect rect = titleRect; + + painter.drawPixmap(rect.topLeft(), leftPm.mask()); + painter.fillRect(rect.left() + leftPm.width(), rect.top(), + rect.width() - leftPm.width() - rightPm.width(), + centerPm.height(), Qt::color1); + painter.drawPixmap(rect.topRight() - QPoint(rightPm.width() - 1, 0), rightPm.mask()); + + painter.fillRect(contents, Qt::color1); + } + + dw->setMask(bitmap); +} + +#include "colorswatch.moc" diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.h b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.h new file mode 100644 index 0000000000..5b144bd428 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.h @@ -0,0 +1,102 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef COLORSWATCH_H +#define COLORSWATCH_H + +#include <QDockWidget> + +QT_FORWARD_DECLARE_CLASS(QAction) +QT_FORWARD_DECLARE_CLASS(QActionGroup) +QT_FORWARD_DECLARE_CLASS(QMenu) + +class ColorSwatch : public QDockWidget +{ + Q_OBJECT + +public: + explicit ColorSwatch(const QString &colorName, QMainWindow *parent = nullptr, Qt::WindowFlags flags = { }); + + void setCustomSizeHint(const QSize &size); + QMenu *colorSwatchMenu() const { return menu; } + +protected: +#ifndef QT_NO_CONTEXTMENU + void contextMenuEvent(QContextMenuEvent *event) override; +#endif // QT_NO_CONTEXTMENU + void resizeEvent(QResizeEvent *e) override; + +private slots: + void changeClosable(bool on); + void changeMovable(bool on); + void changeFloatable(bool on); + void changeFloating(bool on); + void changeVerticalTitleBar(bool on); + void updateContextMenu(); + + void allowLeft(bool a); + void allowRight(bool a); + void allowTop(bool a); + void allowBottom(bool a); + + void placeLeft(bool p); + void placeRight(bool p); + void placeTop(bool p); + void placeBottom(bool p); + + void splitInto(QAction *action); + void tabInto(QAction *action); + +private: + void allow(Qt::DockWidgetArea area, bool allow); + void place(Qt::DockWidgetArea area, bool place); + + QAction *closableAction; + QAction *movableAction; + QAction *floatableAction; + QAction *floatingAction; + QAction *verticalTitleBarAction; + + QActionGroup *allowedAreasActions; + QAction *allowLeftAction; + QAction *allowRightAction; + QAction *allowTopAction; + QAction *allowBottomAction; + + QActionGroup *areaActions; + QAction *leftAction; + QAction *rightAction; + QAction *topAction; + QAction *bottomAction; + + QMenu *tabMenu; + QMenu *splitHMenu; + QMenu *splitVMenu; + QMenu *menu; + + QMainWindow *mainWindow; +}; + +class BlueTitleBar : public QWidget +{ + Q_OBJECT +public: + explicit BlueTitleBar(QWidget *parent = nullptr); + + QSize sizeHint() const override { return minimumSizeHint(); } + QSize minimumSizeHint() const override; + +protected: + void paintEvent(QPaintEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + +public slots: + void updateMask(); + +private: + const QPixmap leftPm; + const QPixmap centerPm; + const QPixmap rightPm; +}; + +#endif // COLORSWATCH_H diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/main.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/main.cpp new file mode 100644 index 0000000000..a64d2f5843 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/main.cpp @@ -0,0 +1,147 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" + +#include <QApplication> +#include <QPainterPath> +#include <QPainter> +#include <QMap> +#include <QDebug> + +void render_qt_text(QPainter *painter, int w, int h, const QColor &color) +{ + QPainterPath path; + path.moveTo(-0.083695, 0.283849); + path.cubicTo(-0.049581, 0.349613, -0.012720, 0.397969, 0.026886, 0.428917); + path.cubicTo(0.066493, 0.459865, 0.111593, 0.477595, 0.162186, 0.482108); + path.lineTo(0.162186, 0.500000); + path.cubicTo(0.115929, 0.498066, 0.066565, 0.487669, 0.014094, 0.468810); + path.cubicTo(-0.038378, 0.449952, -0.088103, 0.423839, -0.135082, 0.390474); + path.cubicTo(-0.182061, 0.357108, -0.222608, 0.321567, -0.256722, 0.283849); + path.cubicTo(-0.304712, 0.262250, -0.342874, 0.239362, -0.371206, 0.215184); + path.cubicTo(-0.411969, 0.179078, -0.443625, 0.134671, -0.466175, 0.081963); + path.cubicTo(-0.488725, 0.029255, -0.500000, -0.033043, -0.500000, -0.104932); + path.cubicTo(-0.500000, -0.218407, -0.467042, -0.312621, -0.401127, -0.387573); + path.cubicTo(-0.335212, -0.462524, -0.255421, -0.500000, -0.161752, -0.500000); + path.cubicTo(-0.072998, -0.500000, 0.003903, -0.462444, 0.068951, -0.387331); + path.cubicTo(0.133998, -0.312218, 0.166522, -0.217440, 0.166522, -0.102998); + path.cubicTo(0.166522, -0.010155, 0.143394, 0.071325, 0.097138, 0.141441); + path.cubicTo(0.050882, 0.211557, -0.009396, 0.259026, -0.083695, 0.283849); + path.moveTo(-0.167823, -0.456963); + path.cubicTo(-0.228823, -0.456963, -0.277826, -0.432624, -0.314831, -0.383946); + path.cubicTo(-0.361665, -0.323340, -0.385082, -0.230335, -0.385082, -0.104932); + path.cubicTo(-0.385082, 0.017569, -0.361376, 0.112025, -0.313964, 0.178433); + path.cubicTo(-0.277248, 0.229368, -0.228534, 0.254836, -0.167823, 0.254836); + path.cubicTo(-0.105088, 0.254836, -0.054496, 0.229368, -0.016045, 0.178433); + path.cubicTo(0.029055, 0.117827, 0.051605, 0.028691, 0.051605, -0.088975); + path.cubicTo(0.051605, -0.179562, 0.039318, -0.255803, 0.014744, -0.317698); + path.cubicTo(-0.004337, -0.365409, -0.029705, -0.400548, -0.061362, -0.423114); + path.cubicTo(-0.093018, -0.445680, -0.128505, -0.456963, -0.167823, -0.456963); + path.moveTo(0.379011, -0.404739); + path.lineTo(0.379011, -0.236460); + path.lineTo(0.486123, -0.236460); + path.lineTo(0.486123, -0.197292); + path.lineTo(0.379011, -0.197292); + path.lineTo(0.379011, 0.134913); + path.cubicTo(0.379011, 0.168117, 0.383276, 0.190442, 0.391804, 0.201886); + path.cubicTo(0.400332, 0.213330, 0.411246, 0.219052, 0.424545, 0.219052); + path.cubicTo(0.435531, 0.219052, 0.446227, 0.215264, 0.456635, 0.207689); + path.cubicTo(0.467042, 0.200113, 0.474993, 0.188910, 0.480486, 0.174081); + path.lineTo(0.500000, 0.174081); + path.cubicTo(0.488436, 0.210509, 0.471957, 0.237911, 0.450564, 0.256286); + path.cubicTo(0.429170, 0.274662, 0.407054, 0.283849, 0.384215, 0.283849); + path.cubicTo(0.368893, 0.283849, 0.353859, 0.279094, 0.339115, 0.269584); + path.cubicTo(0.324371, 0.260074, 0.313530, 0.246534, 0.306592, 0.228965); + path.cubicTo(0.299653, 0.211396, 0.296184, 0.184075, 0.296184, 0.147002); + path.lineTo(0.296184, -0.197292); + path.lineTo(0.223330, -0.197292); + path.lineTo(0.223330, -0.215667); + path.cubicTo(0.241833, -0.224049, 0.260697, -0.237992, 0.279922, -0.257495); + path.cubicTo(0.299147, -0.276999, 0.316276, -0.300129, 0.331310, -0.326886); + path.cubicTo(0.338826, -0.341070, 0.349523, -0.367021, 0.363400, -0.404739); + path.lineTo(0.379011, -0.404739); + path.moveTo(-0.535993, 0.275629); + + painter->translate(w / 2, h / 2); + double scale = qMin(w, h) * 8 / 10.0; + painter->scale(scale, scale); + + painter->setRenderHint(QPainter::Antialiasing); + + painter->save(); + painter->translate(.1, .1); + painter->fillPath(path, QColor(0, 0, 0, 63)); + painter->restore(); + + painter->setBrush(color); + painter->setPen(QPen(Qt::black, 0.02, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin)); + painter->drawPath(path); +} + +static void usage() +{ + qWarning() << "Usage: mainwindow [-SizeHint<color> <width>x<height>] ..."; + exit(1); +} + +enum ParseCommandLineArgumentsResult { + CommandLineArgumentsOk, + CommandLineArgumentsError, + HelpRequested +}; + +static ParseCommandLineArgumentsResult + parseCustomSizeHints(const QStringList &arguments, MainWindow::CustomSizeHintMap *result) +{ + result->clear(); + const auto argumentCount = arguments.size(); + for (int i = 1; i < argumentCount; ++i) { + const QString &arg = arguments.at(i); + if (arg.startsWith(QLatin1String("-SizeHint"))) { + const QString name = arg.mid(9); + if (name.isEmpty()) + return CommandLineArgumentsError; + if (++i == argumentCount) + return CommandLineArgumentsError; + const QStringView sizeStr{ arguments.at(i) }; + const auto idx = sizeStr.indexOf(QLatin1Char('x')); + if (idx == -1) + return CommandLineArgumentsError; + bool ok; + const int w = sizeStr.left(idx).toInt(&ok); + if (!ok) + return CommandLineArgumentsError; + const int h = sizeStr.mid(idx + 1).toInt(&ok); + if (!ok) + return CommandLineArgumentsError; + result->insert(name, QSize(w, h)); + } else if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) { + return HelpRequested; + } else { + return CommandLineArgumentsError; + } + } + + return CommandLineArgumentsOk; +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + MainWindow::CustomSizeHintMap customSizeHints; + switch (parseCustomSizeHints(QCoreApplication::arguments(), &customSizeHints)) { + case CommandLineArgumentsOk: + break; + case CommandLineArgumentsError: + usage(); + return -1; + case HelpRequested: + usage(); + return 0; + } + MainWindow mainWin(customSizeHints); + mainWin.resize(800, 600); + mainWin.show(); + return app.exec(); +} diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.cpp new file mode 100644 index 0000000000..2f989ab97c --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.cpp @@ -0,0 +1,444 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" +#include "colorswatch.h" +#include "toolbar.h" + +#include <QActionGroup> +#include <QLayout> +#include <QMenu> +#include <QMenuBar> +#include <QStatusBar> +#include <QTextEdit> +#include <QFile> +#include <QDataStream> +#include <QFileDialog> +#include <QDialogButtonBox> +#include <QMessageBox> +#include <QApplication> +#include <QPainter> +#include <QMouseEvent> +#include <QLineEdit> +#include <QComboBox> +#include <QLabel> +#include <QPushButton> +#include <QTextEdit> +#include <QDebug> + +static const char message[] = + "<p><b>Qt Main Window Example</b></p>" + + "<p>This is a demonstration of the QMainWindow, QToolBar and " + "QDockWidget classes.</p>" + + "<p>The tool bar and dock widgets can be dragged around and rearranged " + "using the mouse or via the menu.</p>" + + "<p>Each dock widget contains a colored frame and a context " + "(right-click) menu.</p>" + +#ifdef Q_OS_MAC + "<p>On OS X, the \"Black\" dock widget has been created as a " + "<em>Drawer</em>, which is a special kind of QDockWidget.</p>" +#endif + ; + +Q_DECLARE_METATYPE(QDockWidget::DockWidgetFeatures) + +MainWindow::MainWindow(const CustomSizeHintMap &customSizeHints, + QWidget *parent, Qt::WindowFlags flags) + : QMainWindow(parent, flags) +{ + Q_UNUSED(message); + setObjectName("MainWindow"); + setWindowTitle("Qt Main Window Example"); + + QTextEdit *center = new QTextEdit(this); + center->setReadOnly(true); + center->setMinimumSize(400, 205); + setCentralWidget(center); + + setupToolBar(); + setupMenuBar(); + setupDockWidgets(customSizeHints); + + statusBar()->showMessage(tr("Status Bar")); +} + +void MainWindow::actionTriggered(QAction *action) +{ + qDebug("action '%s' triggered", action->text().toLocal8Bit().data()); +} + +void MainWindow::setupToolBar() +{ +#ifdef Q_OS_MACOS + setUnifiedTitleAndToolBarOnMac(true); +#endif + + for (int i = 0; i < 3; ++i) { + ToolBar *tb = new ToolBar(QString::fromLatin1("Tool Bar %1").arg(i + 1), this); + toolBars.append(tb); + addToolBar(tb); + } +} + +void MainWindow::setupMenuBar() +{ + QMenu *menu = menuBar()->addMenu(tr("&File")); + + menu->addAction(tr("Save layout..."), this, &MainWindow::saveLayout); + menu->addAction(tr("Load layout..."), this, &MainWindow::loadLayout); + menu->addAction(tr("Switch layout direction"),this, &MainWindow::switchLayoutDirection); + + menu->addSeparator(); + menu->addAction(tr("&Quit"), qApp, &QCoreApplication::quit); + + mainWindowMenu = menuBar()->addMenu(tr("Main window")); + + QAction *action = mainWindowMenu->addAction(tr("Animated docks")); + action->setCheckable(true); + action->setChecked(dockOptions() & AnimatedDocks); + connect(action, &QAction::toggled, this, &MainWindow::setDockOptions); + + action = mainWindowMenu->addAction(tr("Allow nested docks")); + action->setCheckable(true); + action->setChecked(dockOptions() & AllowNestedDocks); + connect(action, &QAction::toggled, this, &MainWindow::setDockOptions); + + action = mainWindowMenu->addAction(tr("Allow tabbed docks")); + action->setCheckable(true); + action->setChecked(dockOptions() & AllowTabbedDocks); + connect(action, &QAction::toggled, this, &MainWindow::setDockOptions); + + action = mainWindowMenu->addAction(tr("Force tabbed docks")); + action->setCheckable(true); + action->setChecked(dockOptions() & ForceTabbedDocks); + connect(action, &QAction::toggled, this, &MainWindow::setDockOptions); + + action = mainWindowMenu->addAction(tr("Vertical tabs")); + action->setCheckable(true); + action->setChecked(dockOptions() & VerticalTabs); + connect(action, &QAction::toggled, this, &MainWindow::setDockOptions); + + action = mainWindowMenu->addAction(tr("Grouped dragging")); + action->setCheckable(true); + action->setChecked(dockOptions() & GroupedDragging); + connect(action, &QAction::toggled, this, &MainWindow::setDockOptions); + + QMenu *toolBarMenu = menuBar()->addMenu(tr("Tool bars")); + for (int i = 0; i < toolBars.count(); ++i) + toolBarMenu->addMenu(toolBars.at(i)->toolbarMenu()); + +#ifdef Q_OS_MACOS + toolBarMenu->addSeparator(); + + action = toolBarMenu->addAction(tr("Unified")); + action->setCheckable(true); + action->setChecked(unifiedTitleAndToolBarOnMac()); + connect(action, &QAction::toggled, this, &QMainWindow::setUnifiedTitleAndToolBarOnMac); +#endif + + dockWidgetMenu = menuBar()->addMenu(tr("&Dock Widgets")); + + QMenu *aboutMenu = menuBar()->addMenu(tr("About")); + QAction *aboutAct = aboutMenu->addAction(tr("&About"), this, &MainWindow::about); + aboutAct->setStatusTip(tr("Show the application's About box")); + + QAction *aboutQtAct = aboutMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); + aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); +} + +void MainWindow::setDockOptions() +{ + DockOptions opts; + QList<QAction*> actions = mainWindowMenu->actions(); + + if (actions.at(0)->isChecked()) + opts |= AnimatedDocks; + if (actions.at(1)->isChecked()) + opts |= AllowNestedDocks; + if (actions.at(2)->isChecked()) + opts |= AllowTabbedDocks; + if (actions.at(3)->isChecked()) + opts |= ForceTabbedDocks; + if (actions.at(4)->isChecked()) + opts |= VerticalTabs; + if (actions.at(5)->isChecked()) + opts |= GroupedDragging; + + QMainWindow::setDockOptions(opts); +} + +void MainWindow::saveLayout() +{ + QString fileName + = QFileDialog::getSaveFileName(this, tr("Save layout")); + if (fileName.isEmpty()) + return; + QFile file(fileName); + if (!file.open(QFile::WriteOnly)) { + QString msg = tr("Failed to open %1\n%2") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + QMessageBox::warning(this, tr("Error"), msg); + return; + } + + QByteArray geo_data = saveGeometry(); + QByteArray layout_data = saveState(); + + bool ok = file.putChar((uchar)geo_data.size()); + if (ok) + ok = file.write(geo_data) == geo_data.size(); + if (ok) + ok = file.write(layout_data) == layout_data.size(); + + if (!ok) { + QString msg = tr("Error writing to %1\n%2") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + QMessageBox::warning(this, tr("Error"), msg); + return; + } +} + +void MainWindow::loadLayout() +{ + QString fileName + = QFileDialog::getOpenFileName(this, tr("Load layout")); + if (fileName.isEmpty()) + return; + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) { + QString msg = tr("Failed to open %1\n%2") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + QMessageBox::warning(this, tr("Error"), msg); + return; + } + + uchar geo_size; + QByteArray geo_data; + QByteArray layout_data; + + bool ok = file.getChar((char*)&geo_size); + if (ok) { + geo_data = file.read(geo_size); + ok = geo_data.size() == geo_size; + } + if (ok) { + layout_data = file.readAll(); + ok = layout_data.size() > 0; + } + + if (ok) + ok = restoreGeometry(geo_data); + if (ok) + ok = restoreState(layout_data); + + if (!ok) { + QString msg = tr("Error reading %1").arg(QDir::toNativeSeparators(fileName)); + QMessageBox::warning(this, tr("Error"), msg); + return; + } +} + +static QAction *addCornerAction(const QString &text, QMainWindow *mw, QMenu *menu, QActionGroup *group, + Qt::Corner c, Qt::DockWidgetArea a) +{ + QAction *result = menu->addAction(text, mw, [=]() { mw->setCorner(c, a); }); + result->setCheckable(true); + group->addAction(result); + return result; +} + +void MainWindow::setupDockWidgets(const CustomSizeHintMap &customSizeHints) +{ + qRegisterMetaType<QDockWidget::DockWidgetFeatures>(); + + QMenu *cornerMenu = dockWidgetMenu->addMenu(tr("Top left corner")); + QActionGroup *group = new QActionGroup(this); + group->setExclusive(true); + QAction *cornerAction = addCornerAction(tr("Top dock area"), this, cornerMenu, group, Qt::TopLeftCorner, Qt::TopDockWidgetArea); + cornerAction->setChecked(true); + addCornerAction(tr("Left dock area"), this, cornerMenu, group, Qt::TopLeftCorner, Qt::LeftDockWidgetArea); + + cornerMenu = dockWidgetMenu->addMenu(tr("Top right corner")); + group = new QActionGroup(this); + group->setExclusive(true); + cornerAction = addCornerAction(tr("Top dock area"), this, cornerMenu, group, Qt::TopRightCorner, Qt::TopDockWidgetArea); + cornerAction->setChecked(true); + addCornerAction(tr("Right dock area"), this, cornerMenu, group, Qt::TopRightCorner, Qt::RightDockWidgetArea); + + cornerMenu = dockWidgetMenu->addMenu(tr("Bottom left corner")); + group = new QActionGroup(this); + group->setExclusive(true); + cornerAction = addCornerAction(tr("Bottom dock area"), this, cornerMenu, group, Qt::BottomLeftCorner, Qt::BottomDockWidgetArea); + cornerAction->setChecked(true); + addCornerAction(tr("Left dock area"), this, cornerMenu, group, Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + + cornerMenu = dockWidgetMenu->addMenu(tr("Bottom right corner")); + group = new QActionGroup(this); + group->setExclusive(true); + cornerAction = addCornerAction(tr("Bottom dock area"), this, cornerMenu, group, Qt::BottomRightCorner, Qt::BottomDockWidgetArea); + cornerAction->setChecked(true); + addCornerAction(tr("Right dock area"), this, cornerMenu, group, Qt::BottomRightCorner, Qt::RightDockWidgetArea); + + dockWidgetMenu->addSeparator(); + + static const struct Set { + const char * name; + uint flags; + Qt::DockWidgetArea area; + } sets [] = { +#ifndef Q_OS_MAC + { "Black", 0, Qt::LeftDockWidgetArea }, +#else + { "Black", Qt::Drawer, Qt::LeftDockWidgetArea }, +#endif + { "White", 0, Qt::RightDockWidgetArea }, + { "Red", 0, Qt::TopDockWidgetArea }, + { "Green", 0, Qt::TopDockWidgetArea }, + { "Blue", 0, Qt::BottomDockWidgetArea }, + { "Yellow", 0, Qt::BottomDockWidgetArea } + }; + const int setCount = sizeof(sets) / sizeof(Set); + + const QIcon qtIcon(QPixmap(":/res/qt.png")); + for (int i = 0; i < setCount; ++i) { + ColorSwatch *swatch = new ColorSwatch(tr(sets[i].name), this, Qt::WindowFlags(sets[i].flags)); + if (i % 2) + swatch->setWindowIcon(qtIcon); + if (qstrcmp(sets[i].name, "Blue") == 0) { + BlueTitleBar *titlebar = new BlueTitleBar(swatch); + swatch->setTitleBarWidget(titlebar); + connect(swatch, &QDockWidget::topLevelChanged, titlebar, &BlueTitleBar::updateMask); + connect(swatch, &QDockWidget::featuresChanged, titlebar, &BlueTitleBar::updateMask, Qt::QueuedConnection); + } + + QString name = QString::fromLatin1(sets[i].name); + if (customSizeHints.contains(name)) + swatch->setCustomSizeHint(customSizeHints.value(name)); + + addDockWidget(sets[i].area, swatch); + dockWidgetMenu->addMenu(swatch->colorSwatchMenu()); + } + + destroyDockWidgetMenu = new QMenu(tr("Destroy dock widget"), this); + destroyDockWidgetMenu->setEnabled(false); + connect(destroyDockWidgetMenu, &QMenu::triggered, this, &MainWindow::destroyDockWidget); + + dockWidgetMenu->addSeparator(); + dockWidgetMenu->addAction(tr("Add dock widget..."), this, &MainWindow::createDockWidget); + dockWidgetMenu->addMenu(destroyDockWidgetMenu); +} + +void MainWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + QApplication::setLayoutDirection(Qt::RightToLeft); + else + QApplication::setLayoutDirection(Qt::LeftToRight); +} + +class CreateDockWidgetDialog : public QDialog +{ +public: + explicit CreateDockWidgetDialog(QWidget *parent = nullptr); + + QString enteredObjectName() const { return m_objectName->text(); } + Qt::DockWidgetArea location() const; + +private: + QLineEdit *m_objectName; + QComboBox *m_location; +}; + +CreateDockWidgetDialog::CreateDockWidgetDialog(QWidget *parent) + : QDialog(parent) + , m_objectName(new QLineEdit(this)) + , m_location(new QComboBox(this)) +{ + setWindowTitle(tr("Add Dock Widget")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QGridLayout *layout = new QGridLayout(this); + + layout->addWidget(new QLabel(tr("Object name:")), 0, 0); + layout->addWidget(m_objectName, 0, 1); + + layout->addWidget(new QLabel(tr("Location:")), 1, 0); + m_location->setEditable(false); + m_location->addItem(tr("Top")); + m_location->addItem(tr("Left")); + m_location->addItem(tr("Right")); + m_location->addItem(tr("Bottom")); + m_location->addItem(tr("Restore")); + layout->addWidget(m_location, 1, 1); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + layout->addWidget(buttonBox, 2, 0, 1, 2); +} + +Qt::DockWidgetArea CreateDockWidgetDialog::location() const +{ + switch (m_location->currentIndex()) { + case 0: return Qt::TopDockWidgetArea; + case 1: return Qt::LeftDockWidgetArea; + case 2: return Qt::RightDockWidgetArea; + case 3: return Qt::BottomDockWidgetArea; + default: + break; + } + return Qt::NoDockWidgetArea; +} + +void MainWindow::createDockWidget() +{ + CreateDockWidgetDialog dialog(this); + if (dialog.exec() == QDialog::Rejected) + return; + + QDockWidget *dw = new QDockWidget; + const QString name = dialog.enteredObjectName(); + dw->setObjectName(name); + dw->setWindowTitle(name); + dw->setWidget(new QTextEdit); + + Qt::DockWidgetArea area = dialog.location(); + switch (area) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + case Qt::TopDockWidgetArea: + case Qt::BottomDockWidgetArea: + addDockWidget(area, dw); + break; + default: + if (!restoreDockWidget(dw)) { + QMessageBox::warning(this, QString(), tr("Failed to restore dock widget")); + delete dw; + return; + } + break; + } + + extraDockWidgets.append(dw); + destroyDockWidgetMenu->setEnabled(true); + destroyDockWidgetMenu->addAction(new QAction(name, this)); +} + +void MainWindow::destroyDockWidget(QAction *action) +{ + auto index = destroyDockWidgetMenu->actions().indexOf(action); + delete extraDockWidgets.takeAt(index); + destroyDockWidgetMenu->removeAction(action); + action->deleteLater(); + + if (destroyDockWidgetMenu->isEmpty()) + destroyDockWidgetMenu->setEnabled(false); +} + +void MainWindow::about() +{ + QMessageBox::about(this, tr("About MainWindows"), message); +} diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.h b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.h new file mode 100644 index 0000000000..ec522d1a51 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.h @@ -0,0 +1,50 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> +#include <QMap> +#include <QString> +#include <QSize> + +class ToolBar; +QT_FORWARD_DECLARE_CLASS(QMenu) + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + typedef QMap<QString, QSize> CustomSizeHintMap; + + explicit MainWindow(const CustomSizeHintMap &customSizeHints, + QWidget *parent = nullptr, + Qt::WindowFlags flags = { }); + +public slots: + void actionTriggered(QAction *action); + void saveLayout(); + void loadLayout(); + void switchLayoutDirection(); + void setDockOptions(); + + void createDockWidget(); + void destroyDockWidget(QAction *action); + + void about(); + +private: + void setupToolBar(); + void setupMenuBar(); + void setupDockWidgets(const CustomSizeHintMap &customSizeHints); + + QList<ToolBar*> toolBars; + QMenu *dockWidgetMenu; + QMenu *mainWindowMenu; + QList<QDockWidget *> extraDockWidgets; + QMenu *destroyDockWidgetMenu; +}; + +#endif // MAINWINDOW_H diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.pro b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.pro new file mode 100644 index 0000000000..49f4d30720 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.pro @@ -0,0 +1,18 @@ +TEMPLATE = app +QT += widgets +requires(qtConfig(combobox)) + +HEADERS += colorswatch.h mainwindow.h toolbar.h +SOURCES += colorswatch.cpp mainwindow.cpp toolbar.cpp main.cpp +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} + +#! [qrc] +RESOURCES += mainwindow.qrc +#! [qrc] + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/mainwindow +INSTALLS += target diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.qrc b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.qrc new file mode 100644 index 0000000000..47ff22a3e4 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/res"> + <file>qt.png</file> + <file>titlebarLeft.png</file> + <file>titlebarCenter.png</file> + <file>titlebarRight.png</file> +</qresource> +</RCC> diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/qt.png b/tests/manual/examples/widgets/mainwindows/mainwindow/qt.png Binary files differnew file mode 100644 index 0000000000..4f68e162de --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/qt.png diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarCenter.png b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarCenter.png Binary files differnew file mode 100644 index 0000000000..5cc141355c --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarCenter.png diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarLeft.png b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarLeft.png Binary files differnew file mode 100644 index 0000000000..315166202b --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarLeft.png diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarRight.png b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarRight.png Binary files differnew file mode 100644 index 0000000000..a4505268ec --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarRight.png diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.cpp new file mode 100644 index 0000000000..d73c12f50d --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.cpp @@ -0,0 +1,308 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "toolbar.h" + +#include <QRandomGenerator> + +#include <QActionGroup> +#include <QMainWindow> +#include <QMenu> +#include <QPainter> +#include <QPainterPath> +#include <QSpinBox> +#include <QLabel> +#include <QToolTip> + +#include <stdlib.h> + +static QPixmap genIcon(const QSize &iconSize, const QString &, const QColor &color, qreal pixelRatio) +{ + int w = qRound(iconSize.width() * pixelRatio); + int h = qRound(iconSize.height() * pixelRatio); + + QImage image(w, h, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + + QPainter p(&image); + + extern void render_qt_text(QPainter *, int, int, const QColor &); + render_qt_text(&p, w, h, color); + + QPixmap pm = QPixmap::fromImage(image, Qt::DiffuseDither | Qt::DiffuseAlphaDither); + pm.setDevicePixelRatio(pixelRatio); + return pm; +} + +static QPixmap genIcon(const QSize &iconSize, int number, const QColor &color, qreal pixelRatio) +{ return genIcon(iconSize, QString::number(number), color, pixelRatio); } + +ToolBar::ToolBar(const QString &title, QMainWindow *mainWindow) + : QToolBar(mainWindow) + , mainWindow(mainWindow) + , spinbox(nullptr) + , spinboxAction(nullptr) +{ + setWindowTitle(title); + setObjectName(title); + + setIconSize(QSize(32, 32)); + + qreal dpr = devicePixelRatio(); + menu = new QMenu("One", this); + menu->setIcon(genIcon(iconSize(), 1, Qt::black, dpr)); + menu->addAction(genIcon(iconSize(), "A", Qt::blue, dpr), "A"); + menu->addAction(genIcon(iconSize(), "B", Qt::blue, dpr), "B"); + menu->addAction(genIcon(iconSize(), "C", Qt::blue, dpr), "C"); + addAction(menu->menuAction()); + + QAction *two = addAction(genIcon(iconSize(), 2, Qt::white, dpr), "Two"); + QFont boldFont; + boldFont.setBold(true); + two->setFont(boldFont); + + addAction(genIcon(iconSize(), 3, Qt::red, dpr), "Three"); + addAction(genIcon(iconSize(), 4, Qt::green, dpr), "Four"); + addAction(genIcon(iconSize(), 5, Qt::blue, dpr), "Five"); + addAction(genIcon(iconSize(), 6, Qt::yellow, dpr), "Six"); + orderAction = new QAction(this); + orderAction->setText(tr("Order Items in Tool Bar")); + connect(orderAction, &QAction::triggered, this, &ToolBar::order); + + randomizeAction = new QAction(this); + randomizeAction->setText(tr("Randomize Items in Tool Bar")); + connect(randomizeAction, &QAction::triggered, this, &ToolBar::randomize); + + addSpinBoxAction = new QAction(this); + addSpinBoxAction->setText(tr("Add Spin Box")); + connect(addSpinBoxAction, &QAction::triggered, this, &ToolBar::addSpinBox); + + removeSpinBoxAction = new QAction(this); + removeSpinBoxAction->setText(tr("Remove Spin Box")); + removeSpinBoxAction->setEnabled(false); + connect(removeSpinBoxAction, &QAction::triggered, this, &ToolBar::removeSpinBox); + + movableAction = new QAction(tr("Movable"), this); + movableAction->setCheckable(true); + connect(movableAction, &QAction::triggered, this, &ToolBar::changeMovable); + + allowedAreasActions = new QActionGroup(this); + allowedAreasActions->setExclusive(false); + + allowLeftAction = new QAction(tr("Allow on Left"), this); + allowLeftAction->setCheckable(true); + connect(allowLeftAction, &QAction::triggered, this, &ToolBar::allowLeft); + + allowRightAction = new QAction(tr("Allow on Right"), this); + allowRightAction->setCheckable(true); + connect(allowRightAction, &QAction::triggered, this, &ToolBar::allowRight); + + allowTopAction = new QAction(tr("Allow on Top"), this); + allowTopAction->setCheckable(true); + connect(allowTopAction, &QAction::triggered, this, &ToolBar::allowTop); + + allowBottomAction = new QAction(tr("Allow on Bottom"), this); + allowBottomAction->setCheckable(true); + connect(allowBottomAction, &QAction::triggered, this, &ToolBar::allowBottom); + + allowedAreasActions->addAction(allowLeftAction); + allowedAreasActions->addAction(allowRightAction); + allowedAreasActions->addAction(allowTopAction); + allowedAreasActions->addAction(allowBottomAction); + + areaActions = new QActionGroup(this); + areaActions->setExclusive(true); + + leftAction = new QAction(tr("Place on Left") , this); + leftAction->setCheckable(true); + connect(leftAction, &QAction::triggered, this, &ToolBar::placeLeft); + + rightAction = new QAction(tr("Place on Right") , this); + rightAction->setCheckable(true); + connect(rightAction, &QAction::triggered, this, &ToolBar::placeRight); + + topAction = new QAction(tr("Place on Top") , this); + topAction->setCheckable(true); + connect(topAction, &QAction::triggered, this, &ToolBar::placeTop); + + bottomAction = new QAction(tr("Place on Bottom") , this); + bottomAction->setCheckable(true); + connect(bottomAction, &QAction::triggered, this, &ToolBar::placeBottom); + + areaActions->addAction(leftAction); + areaActions->addAction(rightAction); + areaActions->addAction(topAction); + areaActions->addAction(bottomAction); + + connect(movableAction, &QAction::triggered, areaActions, &QActionGroup::setEnabled); + + connect(movableAction, &QAction::triggered, allowedAreasActions, &QActionGroup::setEnabled); + + menu = new QMenu(title, this); + menu->addAction(toggleViewAction()); + menu->addSeparator(); + menu->addAction(orderAction); + menu->addAction(randomizeAction); + menu->addSeparator(); + menu->addAction(addSpinBoxAction); + menu->addAction(removeSpinBoxAction); + menu->addSeparator(); + menu->addAction(movableAction); + menu->addSeparator(); + menu->addActions(allowedAreasActions->actions()); + menu->addSeparator(); + menu->addActions(areaActions->actions()); + menu->addSeparator(); + menu->addAction(tr("Insert break"), this, &ToolBar::insertToolBarBreak); + + connect(menu, &QMenu::aboutToShow, this, &ToolBar::updateMenu); + + randomize(); +} + +void ToolBar::updateMenu() +{ + const Qt::ToolBarArea area = mainWindow->toolBarArea(this); + const Qt::ToolBarAreas areas = allowedAreas(); + + movableAction->setChecked(isMovable()); + + allowLeftAction->setChecked(isAreaAllowed(Qt::LeftToolBarArea)); + allowRightAction->setChecked(isAreaAllowed(Qt::RightToolBarArea)); + allowTopAction->setChecked(isAreaAllowed(Qt::TopToolBarArea)); + allowBottomAction->setChecked(isAreaAllowed(Qt::BottomToolBarArea)); + + if (allowedAreasActions->isEnabled()) { + allowLeftAction->setEnabled(area != Qt::LeftToolBarArea); + allowRightAction->setEnabled(area != Qt::RightToolBarArea); + allowTopAction->setEnabled(area != Qt::TopToolBarArea); + allowBottomAction->setEnabled(area != Qt::BottomToolBarArea); + } + + leftAction->setChecked(area == Qt::LeftToolBarArea); + rightAction->setChecked(area == Qt::RightToolBarArea); + topAction->setChecked(area == Qt::TopToolBarArea); + bottomAction->setChecked(area == Qt::BottomToolBarArea); + + if (areaActions->isEnabled()) { + leftAction->setEnabled(areas & Qt::LeftToolBarArea); + rightAction->setEnabled(areas & Qt::RightToolBarArea); + topAction->setEnabled(areas & Qt::TopToolBarArea); + bottomAction->setEnabled(areas & Qt::BottomToolBarArea); + } +} + +void ToolBar::order() +{ + QList<QAction *> ordered; + QList<QAction *> actions1 = actions(); + const QList<QAction *> childActions = findChildren<QAction *>(); + for (QAction *action : childActions) { + if (!actions1.contains(action)) + continue; + actions1.removeAll(action); + ordered.append(action); + } + + clear(); + addActions(ordered); + + orderAction->setEnabled(false); +} + +void ToolBar::randomize() +{ + QList<QAction *> randomized; + QList<QAction *> actions = this->actions(); + while (!actions.isEmpty()) { + QAction *action = actions.takeAt(QRandomGenerator::global()->bounded(actions.size())); + randomized.append(action); + } + clear(); + addActions(randomized); + + orderAction->setEnabled(true); +} + +void ToolBar::addSpinBox() +{ + if (!spinbox) + spinbox = new QSpinBox(this); + if (!spinboxAction) + spinboxAction = addWidget(spinbox); + else + addAction(spinboxAction); + + addSpinBoxAction->setEnabled(false); + removeSpinBoxAction->setEnabled(true); +} + +void ToolBar::removeSpinBox() +{ + if (spinboxAction) + removeAction(spinboxAction); + + addSpinBoxAction->setEnabled(true); + removeSpinBoxAction->setEnabled(false); +} + +void ToolBar::allow(Qt::ToolBarArea area, bool a) +{ + Qt::ToolBarAreas areas = allowedAreas(); + areas = a ? areas | area : areas & ~area; + setAllowedAreas(areas); + + if (areaActions->isEnabled()) { + leftAction->setEnabled(areas & Qt::LeftToolBarArea); + rightAction->setEnabled(areas & Qt::RightToolBarArea); + topAction->setEnabled(areas & Qt::TopToolBarArea); + bottomAction->setEnabled(areas & Qt::BottomToolBarArea); + } +} + +void ToolBar::place(Qt::ToolBarArea area, bool p) +{ + if (!p) + return; + + mainWindow->addToolBar(area, this); + + if (allowedAreasActions->isEnabled()) { + allowLeftAction->setEnabled(area != Qt::LeftToolBarArea); + allowRightAction->setEnabled(area != Qt::RightToolBarArea); + allowTopAction->setEnabled(area != Qt::TopToolBarArea); + allowBottomAction->setEnabled(area != Qt::BottomToolBarArea); + } +} + +void ToolBar::changeMovable(bool movable) +{ setMovable(movable); } + +void ToolBar::allowLeft(bool a) +{ allow(Qt::LeftToolBarArea, a); } + +void ToolBar::allowRight(bool a) +{ allow(Qt::RightToolBarArea, a); } + +void ToolBar::allowTop(bool a) +{ allow(Qt::TopToolBarArea, a); } + +void ToolBar::allowBottom(bool a) +{ allow(Qt::BottomToolBarArea, a); } + +void ToolBar::placeLeft(bool p) +{ place(Qt::LeftToolBarArea, p); } + +void ToolBar::placeRight(bool p) +{ place(Qt::RightToolBarArea, p); } + +void ToolBar::placeTop(bool p) +{ place(Qt::TopToolBarArea, p); } + +void ToolBar::placeBottom(bool p) +{ place(Qt::BottomToolBarArea, p); } + +void ToolBar::insertToolBarBreak() +{ + mainWindow->insertToolBarBreak(this); +} diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.h b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.h new file mode 100644 index 0000000000..0030adbe23 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.h @@ -0,0 +1,74 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef TOOLBAR_H +#define TOOLBAR_H + +#include <QToolBar> + +QT_FORWARD_DECLARE_CLASS(QAction) +QT_FORWARD_DECLARE_CLASS(QActionGroup) +QT_FORWARD_DECLARE_CLASS(QMenu) +QT_FORWARD_DECLARE_CLASS(QSpinBox) + +class ToolBar : public QToolBar +{ + Q_OBJECT + +public: + explicit ToolBar(const QString &title, QMainWindow *mainWindow); + + QMenu *toolbarMenu() const { return menu; } + +private slots: + void order(); + void randomize(); + void addSpinBox(); + void removeSpinBox(); + + void changeMovable(bool movable); + + void allowLeft(bool a); + void allowRight(bool a); + void allowTop(bool a); + void allowBottom(bool a); + + void placeLeft(bool p); + void placeRight(bool p); + void placeTop(bool p); + void placeBottom(bool p); + + void updateMenu(); + void insertToolBarBreak(); + +private: + void allow(Qt::ToolBarArea area, bool allow); + void place(Qt::ToolBarArea area, bool place); + + QMainWindow *mainWindow; + + QSpinBox *spinbox; + QAction *spinboxAction; + + QMenu *menu; + QAction *orderAction; + QAction *randomizeAction; + QAction *addSpinBoxAction; + QAction *removeSpinBoxAction; + + QAction *movableAction; + + QActionGroup *allowedAreasActions; + QAction *allowLeftAction; + QAction *allowRightAction; + QAction *allowTopAction; + QAction *allowBottomAction; + + QActionGroup *areaActions; + QAction *leftAction; + QAction *rightAction; + QAction *topAction; + QAction *bottomAction; +}; + +#endif // TOOLBAR_H diff --git a/tests/manual/examples/widgets/mainwindows/mdi/CMakeLists.txt b/tests/manual/examples/widgets/mainwindows/mdi/CMakeLists.txt new file mode 100644 index 0000000000..2f544b9510 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(mdi LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/mdi") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(mdi + main.cpp + mainwindow.cpp mainwindow.h + mdichild.cpp mdichild.h +) + +set_target_properties(mdi PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(mdi PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +# Resources: +set(mdi_resource_files + "images/copy.png" + "images/cut.png" + "images/new.png" + "images/open.png" + "images/paste.png" + "images/save.png" +) + +qt_add_resources(mdi "mdi" + PREFIX + "/" + FILES + ${mdi_resource_files} +) + +install(TARGETS mdi + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/copy.png b/tests/manual/examples/widgets/mainwindows/mdi/images/copy.png Binary files differnew file mode 100644 index 0000000000..2aeb28288f --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/images/copy.png diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/cut.png b/tests/manual/examples/widgets/mainwindows/mdi/images/cut.png Binary files differnew file mode 100644 index 0000000000..54638e9386 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/images/cut.png diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/new.png b/tests/manual/examples/widgets/mainwindows/mdi/images/new.png Binary files differnew file mode 100644 index 0000000000..12131b0100 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/images/new.png diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/open.png b/tests/manual/examples/widgets/mainwindows/mdi/images/open.png Binary files differnew file mode 100644 index 0000000000..45fa2883a7 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/images/open.png diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/paste.png b/tests/manual/examples/widgets/mainwindows/mdi/images/paste.png Binary files differnew file mode 100644 index 0000000000..c14425cad1 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/images/paste.png diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/save.png b/tests/manual/examples/widgets/mainwindows/mdi/images/save.png Binary files differnew file mode 100644 index 0000000000..e65a29d5f1 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/images/save.png diff --git a/tests/manual/examples/widgets/mainwindows/mdi/main.cpp b/tests/manual/examples/widgets/mainwindows/mdi/main.cpp new file mode 100644 index 0000000000..ee70050544 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/main.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QApplication> +#include <QCommandLineParser> +#include <QCommandLineOption> + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QCoreApplication::setApplicationName("MDI Example"); + QCoreApplication::setOrganizationName("QtProject"); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + QCommandLineParser parser; + parser.setApplicationDescription("Qt MDI Example"); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("file", "The file to open."); + parser.process(app); + + MainWindow mainWin; + const QStringList posArgs = parser.positionalArguments(); + for (const QString &fileName : posArgs) + mainWin.openFile(fileName); + mainWin.show(); + return app.exec(); +} diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.cpp b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.cpp new file mode 100644 index 0000000000..c0f9091002 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.cpp @@ -0,0 +1,476 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> + +#include "mainwindow.h" +#include "mdichild.h" + +MainWindow::MainWindow() + : mdiArea(new QMdiArea) +{ + mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setCentralWidget(mdiArea); + connect(mdiArea, &QMdiArea::subWindowActivated, + this, &MainWindow::updateMenus); + + createActions(); + createStatusBar(); + updateMenus(); + + readSettings(); + + setWindowTitle(tr("MDI")); + setUnifiedTitleAndToolBarOnMac(true); +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + event->ignore(); + } else { + writeSettings(); + event->accept(); + } +} + +void MainWindow::newFile() +{ + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + +void MainWindow::open() +{ + const QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) + openFile(fileName); +} + +bool MainWindow::openFile(const QString &fileName) +{ + if (QMdiSubWindow *existing = findMdiChild(fileName)) { + mdiArea->setActiveSubWindow(existing); + return true; + } + const bool succeeded = loadFile(fileName); + if (succeeded) + statusBar()->showMessage(tr("File loaded"), 2000); + return succeeded; +} + +bool MainWindow::loadFile(const QString &fileName) +{ + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + MainWindow::prependToRecentFiles(fileName); + return succeeded; +} + +void MainWindow::newSubWindow() +{ + MdiChild *child = createMdiChild(static_cast<MdiChild *>(mdiArea->activeSubWindow()->widget())); + child->show(); +} + +static inline QString recentFilesKey() { return QStringLiteral("recentFileList"); } +static inline QString fileKey() { return QStringLiteral("file"); } + +static QStringList readRecentFiles(QSettings &settings) +{ + QStringList result; + const int count = settings.beginReadArray(recentFilesKey()); + for (int i = 0; i < count; ++i) { + settings.setArrayIndex(i); + result.append(settings.value(fileKey()).toString()); + } + settings.endArray(); + return result; +} + +static void writeRecentFiles(const QStringList &files, QSettings &settings) +{ + const int count = files.size(); + settings.beginWriteArray(recentFilesKey()); + for (int i = 0; i < count; ++i) { + settings.setArrayIndex(i); + settings.setValue(fileKey(), files.at(i)); + } + settings.endArray(); +} + +bool MainWindow::hasRecentFiles() +{ + QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); + const int count = settings.beginReadArray(recentFilesKey()); + settings.endArray(); + return count > 0; +} + +void MainWindow::prependToRecentFiles(const QString &fileName) +{ + QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); + + const QStringList oldRecentFiles = readRecentFiles(settings); + QStringList recentFiles = oldRecentFiles; + recentFiles.removeAll(fileName); + recentFiles.prepend(fileName); + if (oldRecentFiles != recentFiles) + writeRecentFiles(recentFiles, settings); + + setRecentFilesVisible(!recentFiles.isEmpty()); +} + +void MainWindow::setRecentFilesVisible(bool visible) +{ + recentFileSubMenuAct->setVisible(visible); + recentFileSeparator->setVisible(visible); +} + +void MainWindow::updateRecentFileActions() +{ + QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); + + const QStringList recentFiles = readRecentFiles(settings); + const int count = qMin(int(MaxRecentFiles), recentFiles.size()); + int i = 0; + for ( ; i < count; ++i) { + const QString fileName = QFileInfo(recentFiles.at(i)).fileName(); + recentFileActs[i]->setText(tr("&%1 %2").arg(i + 1).arg(fileName)); + recentFileActs[i]->setData(recentFiles.at(i)); + recentFileActs[i]->setVisible(true); + } + for ( ; i < MaxRecentFiles; ++i) + recentFileActs[i]->setVisible(false); +} + +void MainWindow::openRecentFile() +{ + if (const QAction *action = qobject_cast<const QAction *>(sender())) + openFile(action->data().toString()); +} + +void MainWindow::save() +{ + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + +void MainWindow::saveAs() +{ + MdiChild *child = activeMdiChild(); + if (child && child->saveAs()) { + statusBar()->showMessage(tr("File saved"), 2000); + MainWindow::prependToRecentFiles(child->currentFile()); + } +} + +#ifndef QT_NO_CLIPBOARD +void MainWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + +void MainWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + +void MainWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + +void MainWindow::about() +{ + QMessageBox::about(this, tr("About MDI"), + tr("The <b>MDI</b> example demonstrates how to write multiple " + "document interface applications using Qt.")); +} + +void MainWindow::updateMenus() +{ + bool hasMdiChild = (activeMdiChild() != nullptr); + saveAct->setEnabled(hasMdiChild); + saveAsAct->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + pasteAct->setEnabled(hasMdiChild); +#endif + newWindowAct->setEnabled(hasMdiChild); + closeAct->setEnabled(hasMdiChild); + closeAllAct->setEnabled(hasMdiChild); + tileAct->setEnabled(hasMdiChild); + cascadeAct->setEnabled(hasMdiChild); + nextAct->setEnabled(hasMdiChild); + previousAct->setEnabled(hasMdiChild); + windowMenuSeparatorAct->setVisible(hasMdiChild); + +#ifndef QT_NO_CLIPBOARD + bool hasSelection = (activeMdiChild() && + activeMdiChild()->textCursor().hasSelection()); + cutAct->setEnabled(hasSelection); + copyAct->setEnabled(hasSelection); +#endif +} + +void MainWindow::updateWindowMenu() +{ + windowMenu->clear(); + windowMenu->addAction(newWindowAct); + windowMenu->addAction(closeAct); + windowMenu->addAction(closeAllAct); + windowMenu->addSeparator(); + windowMenu->addAction(tileAct); + windowMenu->addAction(cascadeAct); + windowMenu->addSeparator(); + windowMenu->addAction(nextAct); + windowMenu->addAction(previousAct); + windowMenu->addAction(windowMenuSeparatorAct); + + QList<QMdiSubWindow *> windows = mdiArea->subWindowList(); + windowMenuSeparatorAct->setVisible(!windows.isEmpty()); + + for (int i = 0; i < windows.size(); ++i) { + QMdiSubWindow *mdiSubWindow = windows.at(i); + MdiChild *child = qobject_cast<MdiChild *>(mdiSubWindow->widget()); + + QString text; + if (i < 9) { + text = tr("&%1 %2").arg(i + 1) + .arg(child->userFriendlyCurrentFile()); + } else { + text = tr("%1 %2").arg(i + 1) + .arg(child->userFriendlyCurrentFile()); + } + QAction *action = windowMenu->addAction(text, mdiSubWindow, [this, mdiSubWindow]() { + mdiArea->setActiveSubWindow(mdiSubWindow); + }); + action->setCheckable(true); + action ->setChecked(child == activeMdiChild()); + } +} + +MdiChild *MainWindow::createMdiChild(MdiChild *other) +{ + MdiChild *child = new MdiChild(other); + mdiArea->addSubWindow(child); + +#ifndef QT_NO_CLIPBOARD + connect(child, &QTextEdit::copyAvailable, cutAct, &QAction::setEnabled); + connect(child, &QTextEdit::copyAvailable, copyAct, &QAction::setEnabled); +#endif + + return child; +} + +void MainWindow::createActions() +{ + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + QToolBar *fileToolBar = addToolBar(tr("File")); + + const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png")); + newAct = new QAction(newIcon, tr("&New"), this); + newAct->setShortcuts(QKeySequence::New); + newAct->setStatusTip(tr("Create a new file")); + connect(newAct, &QAction::triggered, this, &MainWindow::newFile); + fileMenu->addAction(newAct); + fileToolBar->addAction(newAct); + +//! [qaction setup] + const QIcon openIcon = QIcon::fromTheme("document-open", QIcon(":/images/open.png")); + QAction *openAct = new QAction(openIcon, tr("&Open..."), this); + openAct->setShortcuts(QKeySequence::Open); + openAct->setStatusTip(tr("Open an existing file")); + connect(openAct, &QAction::triggered, this, &MainWindow::open); + fileMenu->addAction(openAct); + fileToolBar->addAction(openAct); +//! [qaction setup] + + const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(":/images/save.png")); + saveAct = new QAction(saveIcon, tr("&Save"), this); + saveAct->setShortcuts(QKeySequence::Save); + saveAct->setStatusTip(tr("Save the document to disk")); + connect(saveAct, &QAction::triggered, this, &MainWindow::save); + fileToolBar->addAction(saveAct); + + const QIcon saveAsIcon = QIcon::fromTheme("document-save-as"); + saveAsAct = new QAction(saveAsIcon, tr("Save &As..."), this); + saveAsAct->setShortcuts(QKeySequence::SaveAs); + saveAsAct->setStatusTip(tr("Save the document under a new name")); + connect(saveAsAct, &QAction::triggered, this, &MainWindow::saveAs); + fileMenu->addAction(saveAsAct); + + fileMenu->addSeparator(); + + QMenu *recentMenu = fileMenu->addMenu(tr("Recent...")); + connect(recentMenu, &QMenu::aboutToShow, this, &MainWindow::updateRecentFileActions); + recentFileSubMenuAct = recentMenu->menuAction(); + + for (int i = 0; i < MaxRecentFiles; ++i) { + recentFileActs[i] = recentMenu->addAction(QString(), this, &MainWindow::openRecentFile); + recentFileActs[i]->setVisible(false); + } + + recentFileSeparator = fileMenu->addSeparator(); + + setRecentFilesVisible(MainWindow::hasRecentFiles()); + + fileMenu->addAction(tr("Switch layout direction"), this, &MainWindow::switchLayoutDirection); + + fileMenu->addSeparator(); + +//! [0] + const QIcon exitIcon = QIcon::fromTheme("application-exit"); + QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::quit); + exitAct->setShortcuts(QKeySequence::Quit); + exitAct->setStatusTip(tr("Exit the application")); + fileMenu->addAction(exitAct); +//! [0] + +#ifndef QT_NO_CLIPBOARD + QMenu *editMenu = menuBar()->addMenu(tr("&Edit")); + QToolBar *editToolBar = addToolBar(tr("Edit")); + + const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png")); + cutAct = new QAction(cutIcon, tr("Cu&t"), this); + cutAct->setShortcuts(QKeySequence::Cut); + cutAct->setStatusTip(tr("Cut the current selection's contents to the " + "clipboard")); + connect(cutAct, &QAction::triggered, this, &MainWindow::cut); + editMenu->addAction(cutAct); + editToolBar->addAction(cutAct); + + const QIcon copyIcon = QIcon::fromTheme("edit-copy", QIcon(":/images/copy.png")); + copyAct = new QAction(copyIcon, tr("&Copy"), this); + copyAct->setShortcuts(QKeySequence::Copy); + copyAct->setStatusTip(tr("Copy the current selection's contents to the " + "clipboard")); + connect(copyAct, &QAction::triggered, this, &MainWindow::copy); + editMenu->addAction(copyAct); + editToolBar->addAction(copyAct); + + const QIcon pasteIcon = QIcon::fromTheme("edit-paste", QIcon(":/images/paste.png")); + pasteAct = new QAction(pasteIcon, tr("&Paste"), this); + pasteAct->setShortcuts(QKeySequence::Paste); + pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current " + "selection")); + connect(pasteAct, &QAction::triggered, this, &MainWindow::paste); + editMenu->addAction(pasteAct); + editToolBar->addAction(pasteAct); +#endif + + windowMenu = menuBar()->addMenu(tr("&Window")); + connect(windowMenu, &QMenu::aboutToShow, this, &MainWindow::updateWindowMenu); + + newWindowAct = new QAction(tr("&New view"), this); + newWindowAct->setStatusTip(tr("Make another window viewing the same document")); + connect(newWindowAct, &QAction::triggered, + this, &MainWindow::newSubWindow); + + closeAct = new QAction(tr("Cl&ose"), this); + closeAct->setStatusTip(tr("Close the active window")); + connect(closeAct, &QAction::triggered, + mdiArea, &QMdiArea::closeActiveSubWindow); + + closeAllAct = new QAction(tr("Close &All"), this); + closeAllAct->setStatusTip(tr("Close all the windows")); + connect(closeAllAct, &QAction::triggered, mdiArea, &QMdiArea::closeAllSubWindows); + + tileAct = new QAction(tr("&Tile"), this); + tileAct->setStatusTip(tr("Tile the windows")); + connect(tileAct, &QAction::triggered, mdiArea, &QMdiArea::tileSubWindows); + + cascadeAct = new QAction(tr("&Cascade"), this); + cascadeAct->setStatusTip(tr("Cascade the windows")); + connect(cascadeAct, &QAction::triggered, mdiArea, &QMdiArea::cascadeSubWindows); + + nextAct = new QAction(tr("Ne&xt"), this); + nextAct->setShortcuts(QKeySequence::NextChild); + nextAct->setStatusTip(tr("Move the focus to the next window")); + connect(nextAct, &QAction::triggered, mdiArea, &QMdiArea::activateNextSubWindow); + + previousAct = new QAction(tr("Pre&vious"), this); + previousAct->setShortcuts(QKeySequence::PreviousChild); + previousAct->setStatusTip(tr("Move the focus to the previous " + "window")); + connect(previousAct, &QAction::triggered, mdiArea, &QMdiArea::activatePreviousSubWindow); + + windowMenuSeparatorAct = new QAction(this); + windowMenuSeparatorAct->setSeparator(true); + + updateWindowMenu(); + + menuBar()->addSeparator(); + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + + QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about); + aboutAct->setStatusTip(tr("Show the application's About box")); + + QAction *aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); + aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); +} + +void MainWindow::createStatusBar() +{ + statusBar()->showMessage(tr("Ready")); +} + +void MainWindow::readSettings() +{ + QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); + const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray(); + if (geometry.isEmpty()) { + const QRect availableGeometry = screen()->availableGeometry(); + resize(availableGeometry.width() / 3, availableGeometry.height() / 2); + move((availableGeometry.width() - width()) / 2, + (availableGeometry.height() - height()) / 2); + } else { + restoreGeometry(geometry); + } +} + +void MainWindow::writeSettings() +{ + QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); + settings.setValue("geometry", saveGeometry()); +} + +MdiChild *MainWindow::activeMdiChild() const +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast<MdiChild *>(activeSubWindow->widget()); + return nullptr; +} + +QMdiSubWindow *MainWindow::findMdiChild(const QString &fileName) const +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList(); + for (QMdiSubWindow *window : subWindows) { + MdiChild *mdiChild = qobject_cast<MdiChild *>(window->widget()); + if (mdiChild->currentFile() == canonicalFilePath) + return window; + } + return nullptr; +} + +void MainWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + QGuiApplication::setLayoutDirection(Qt::RightToLeft); + else + QGuiApplication::setLayoutDirection(Qt::LeftToRight); +} diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.h b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.h new file mode 100644 index 0000000000..c8b720e216 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.h @@ -0,0 +1,87 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +class MdiChild; +QT_BEGIN_NAMESPACE +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QTextDocument; +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + + bool openFile(const QString &fileName); + +protected: + void closeEvent(QCloseEvent *event) override; + +private slots: + void newFile(); + void open(); + void save(); + void saveAs(); + void updateRecentFileActions(); + void openRecentFile(); +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + void about(); + void updateMenus(); + void updateWindowMenu(); + MdiChild *createMdiChild(MdiChild *other = nullptr); + void switchLayoutDirection(); + void newSubWindow(); + +private: + enum { MaxRecentFiles = 5 }; + + void createActions(); + void createStatusBar(); + void readSettings(); + void writeSettings(); + bool loadFile(const QString &fileName); + static bool hasRecentFiles(); + void prependToRecentFiles(const QString &fileName); + void setRecentFilesVisible(bool visible); + MdiChild *activeMdiChild() const; + QMdiSubWindow *findMdiChild(const QString &fileName) const; + + QMdiArea *mdiArea; + + QMenu *windowMenu; + QAction *newAct; + QAction *saveAct; + QAction *saveAsAct; + QAction *recentFileActs[MaxRecentFiles]; + QAction *recentFileSeparator; + QAction *recentFileSubMenuAct; +#ifndef QT_NO_CLIPBOARD + QAction *cutAct; + QAction *copyAct; + QAction *pasteAct; +#endif + QAction *newWindowAct; + QAction *closeAct; + QAction *closeAllAct; + QAction *tileAct; + QAction *cascadeAct; + QAction *nextAct; + QAction *previousAct; + QAction *windowMenuSeparatorAct; +}; + +#endif diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdi.pro b/tests/manual/examples/widgets/mainwindows/mdi/mdi.pro new file mode 100644 index 0000000000..f2c236f7e2 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/mdi.pro @@ -0,0 +1,13 @@ +QT += widgets +requires(qtConfig(filedialog)) + +HEADERS = mainwindow.h \ + mdichild.h +SOURCES = main.cpp \ + mainwindow.cpp \ + mdichild.cpp +RESOURCES = mdi.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/mdi +INSTALLS += target diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdi.qrc b/tests/manual/examples/widgets/mainwindows/mdi/mdi.qrc new file mode 100644 index 0000000000..0a776fab4d --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/mdi.qrc @@ -0,0 +1,10 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/copy.png</file> + <file>images/cut.png</file> + <file>images/new.png</file> + <file>images/open.png</file> + <file>images/paste.png</file> + <file>images/save.png</file> +</qresource> +</RCC> diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdichild.cpp b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.cpp new file mode 100644 index 0000000000..b922826ae3 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.cpp @@ -0,0 +1,158 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> + +#include "mdichild.h" + +MdiChild::MdiChild(MdiChild *other) +{ + setAttribute(Qt::WA_DeleteOnClose); + if (other) { + auto *doc = other->document(); + setDocument(doc); + setWindowModified(other->isWindowModified()); + connect(doc, &QTextDocument::contentsChanged, + this, &MdiChild::documentWasModified); + curFile = other->curFile; + setWindowTitle(userFriendlyCurrentFile() + "[*]"); + } +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + + connect(document(), &QTextDocument::contentsChanged, + this, &MdiChild::documentWasModified); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.") + .arg(fileName) + .arg(file.errorString())); + return false; + } + + QTextStream in(&file); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QGuiApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + + connect(document(), &QTextDocument::contentsChanged, + this, &MdiChild::documentWasModified); + + return true; +} + +bool MdiChild::save() +{ + if (isUntitled) { + return saveAs(); + } else { + return saveFile(curFile); + } +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), + curFile); + if (fileName.isEmpty()) + return false; + + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QString errorMessage; + + QGuiApplication::setOverrideCursor(Qt::WaitCursor); + QSaveFile file(fileName); + if (file.open(QFile::WriteOnly | QFile::Text)) { + QTextStream out(&file); + out << toPlainText(); + if (!file.commit()) { + errorMessage = tr("Cannot write file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } + } else { + errorMessage = tr("Cannot open file %1 for writing:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } + QGuiApplication::restoreOverrideCursor(); + + if (!errorMessage.isEmpty()) { + QMessageBox::warning(this, tr("MDI"), errorMessage); + return false; + } + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (!document()->isModified()) + return true; + const QMessageBox::StandardButton ret + = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + switch (ret) { + case QMessageBox::Save: + return save(); + case QMessageBox::Cancel: + return false; + default: + break; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdichild.h b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.h new file mode 100644 index 0000000000..813a845594 --- /dev/null +++ b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.h @@ -0,0 +1,39 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MDICHILD_H +#define MDICHILD_H + +#include <QTextEdit> + +class MdiChild : public QTextEdit +{ + Q_OBJECT + +public: + MdiChild(MdiChild *other = nullptr); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + +protected: + void closeEvent(QCloseEvent *event) override; + +private slots: + void documentWasModified(); + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled = true; +}; + +#endif |