#include "./dialogutils.h" #include "../misc/desktoputils.h" #include #include #include #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) #include #include #endif #if defined(QT_UTILITIES_GUI_QTWIDGETS) #include #include #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) #include #endif #include #include #include #endif namespace QtUtilities { /*! * \brief Generates the window title string for the specified \a documentStatus * and \a documentPath. */ QString generateWindowTitle(DocumentStatus documentStatus, const QString &documentPath) { switch (documentStatus) { case DocumentStatus::Saved: if (documentPath.isEmpty()) { return QCoreApplication::translate("Utilities::windowTitle", "Unsaved - %1").arg(QCoreApplication::applicationName()); } else { const QFileInfo file(documentPath); return QCoreApplication::translate("Utilities::windowTitle", "%1 - %2 - %3") .arg(file.fileName(), file.dir().path(), QCoreApplication::applicationName()); } case DocumentStatus::Unsaved: if (documentPath.isEmpty()) { return QCoreApplication::translate("Utilities::windowTitle", "*Unsaved - %1").arg(QCoreApplication::applicationName()); } else { const QFileInfo file(documentPath); return QCoreApplication::translate("Utilities::windowTitle", "*%1 - %2 - %3") .arg(file.fileName(), file.dir().path(), QCoreApplication::applicationName()); } case DocumentStatus::NoDocument: return QCoreApplication::applicationName(); default: return QString(); // to suppress warning: "control reaches end of non-void // function" } } #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) #ifdef Q_OS_WIN32 /*! * \brief Returns the color used to draw frames. */ QColor windowFrameColorForPalette(const QPalette &palette) { return palette.window().color().darker(108); } /*! * \brief Returns the color used to draw frames. */ QColor windowFrameColor() { return windowFrameColorForPalette(QGuiApplication::palette()); } /*! * \brief Returns the color used to draw instructions. */ QColor instructionTextColorForPalette(const QPalette &palette) { return isPaletteDark(palette) ? palette.text().color() : QColor(0x00, 0x33, 0x99); } /*! * \brief Returns the color used to draw instructions. */ QColor instructionTextColor() { return instructionTextColorForPalette(QGuiApplication::palette()); } #endif /*! * \brief Returns the stylesheet for dialogs and other windows used in my * applications. */ QString dialogStyleForPalette(const QPalette &palette) { #ifdef Q_OS_WINDOWS return QStringLiteral("#mainWidget { color: palette(text); background-color: " "palette(base); border: none; }" "#bottomWidget { background-color: palette(window); " "color: palette(window-text); border-top: 1px solid %1; }" "QMessageBox QLabel, QInputDialog QLabel, " "*[classNames~=\"heading\"] { font-size: 12pt; color: %2; " "}" "*[classNames~=\"input-invalid\"] { color: red; }") .arg(windowFrameColorForPalette(palette).name(), instructionTextColorForPalette(palette).name()); #else Q_UNUSED(palette) return QStringLiteral("*[classNames~=\"heading\"] { font-weight: bold; }" "*[classNames~=\"input-invalid\"] { color: red; }"); #endif } /*! * \brief Returns the stylesheet for dialogs and other windows used in my * applications. */ const QString &dialogStyle() { static const auto style = dialogStyleForPalette(QGuiApplication::palette()); return style; } #ifdef QT_UTILITIES_GUI_QTWIDGETS QRect availableScreenGeometryAtPoint(const QPoint &point) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QScreen *const screen = QGuiApplication::screenAt(point); if (!screen) { return QRect(); } return screen->availableGeometry(); #else return QApplication::desktop()->availableGeometry(point); #endif } /// \cond static QRect shrinkRectByMargins(QRect rect, const QMargins &margins) { rect.setLeft(rect.left() + margins.left()); rect.setTop(rect.top() + margins.top()); rect.setRight(rect.right() - margins.right()); rect.setBottom(rect.bottom() - margins.bottom()); return rect; } static QRect limitRect(QRect rect, const QRect &bounds) { if (rect.left() < bounds.left()) { rect.setLeft(bounds.left()); } if (rect.top() < bounds.top()) { rect.setTop(bounds.top()); } if (rect.right() > bounds.right()) { rect.setRight(bounds.right()); } if (rect.bottom() > bounds.bottom()) { rect.setBottom(bounds.bottom()); } return rect; } static QMargins widgetFrame(QWidget *widget, const QMargins &defaultAssumption = QMargins(10, 25, 10, 10)) { if (!widget->isWindow()) { return QMargins(); } const auto widgetGeometry = widget->geometry(); const auto frameGeometry = widget->frameGeometry(); const auto frame = QMargins(widgetGeometry.left() - frameGeometry.left(), widgetGeometry.top() - frameGeometry.top(), frameGeometry.right() - widgetGeometry.right(), frameGeometry.bottom() - widgetGeometry.bottom()); return frame.isNull() ? defaultAssumption : frame; } static bool centerWidgetInternal(QWidget *widget, const QWidget *parent, const QPoint *position, bool avoidOverflow) { const auto availableGeometry = parent ? parent->geometry() : availableScreenGeometryAtPoint(position ? *position : QCursor::pos()); const auto alignedRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, widget->size(), availableGeometry); if (!avoidOverflow) { widget->setGeometry(alignedRect); return false; } const auto limitedRect = limitRect(alignedRect, shrinkRectByMargins(availableGeometry, widgetFrame(widget))); widget->setGeometry(limitedRect); return alignedRect != limitedRect; } /// \endcond /*! * \brief Moves the specified \a widget to be centered within the (available) screen area or \a parent if specified. * \remarks * - The screen containing the current cursor position is used unless \a position is specified. */ void centerWidget(QWidget *widget, const QWidget *parent, const QPoint *position) { centerWidgetInternal(widget, parent, position, false); } /*! * \brief Moves the specified \a widget to be centered within the (available) screen area or \a parent if specified. * \returns Returns whether an overflow occurred. * \remarks * - If the widget overflows it is resized to take the whole available space in the dimension(s) that overflow. * If the widget is a window, its frame is attempted to be taken into account. If the window frame can not be determined * a generous assumption is made. It can nevertheless make sense to simply show \a widget using QWidget::showMaximized() * to make it simply fill the entire screen after all. * - The screen containing the current cursor position is used unless \a position is specified. */ bool centerWidgetAvoidingOverflow(QWidget *widget, const QWidget *parent, const QPoint *position) { return centerWidgetInternal(widget, parent, position, true); } /*! * \brief Moves the specified \a widget to the corner which is closest to the * current cursor position or \a position if specified. * * If there are multiple screens available, the screen where the cursor currently * is located is chosen. */ void cornerWidget(QWidget *widget, const QPoint *position) { const QPoint cursorPos(position ? *position : QCursor::pos()); const QRect availableGeometry(availableScreenGeometryAtPoint(cursorPos)); const Qt::Alignment alignment = (cursorPos.x() - availableGeometry.left() < availableGeometry.right() - cursorPos.x() ? Qt::AlignLeft : Qt::AlignRight) | (cursorPos.y() - availableGeometry.top() < availableGeometry.bottom() - cursorPos.y() ? Qt::AlignTop : Qt::AlignBottom); widget->setGeometry(QStyle::alignedRect(Qt::LeftToRight, alignment, widget->size(), availableGeometry)); } /*! * \brief Makes \a widget a heading. */ void makeHeading(QWidget *widget) { widget->setProperty("classNames", widget->property("classNames").toStringList() << QStringLiteral("heading")); } /*! * \brief Updates the widget style. * \remarks Useful when dynamic properties are used in the stylesheet because * the widget style does not update automatically when a property * changes. */ void updateStyle(QWidget *widget) { widget->style()->unpolish(widget); widget->style()->polish(widget); widget->update(); } #endif #endif } // namespace QtUtilities