diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp b/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp index a2cd9863f6..9ef6918381 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include #include @@ -120,277 +118,267 @@ dpiScaled(qreal value) { #endif } -class CalibreStyle: public QProxyStyle { - private: - const QHash icon_map; - QByteArray desktop_environment; - QDialogButtonBox::ButtonLayout button_layout; - int transient_scroller; +CalibreStyle::CalibreStyle(const QHash &icmap, int transient_scroller) : QProxyStyle(QString::fromUtf8("Fusion")), icon_map(icmap), transient_scroller(transient_scroller) { + setObjectName(QString("calibre")); + desktop_environment = detectDesktopEnvironment(); + button_layout = static_cast(QProxyStyle::styleHint(SH_DialogButtonLayout)); + if (QLatin1String("GNOME") == desktop_environment || QLatin1String("MATE") == desktop_environment || QLatin1String("UNITY") == desktop_environment || QLatin1String("CINNAMON") == desktop_environment || QLatin1String("X-CINNAMON") == desktop_environment) + button_layout = QDialogButtonBox::GnomeLayout; +} - public: - CalibreStyle(QStyle *base, const QHash &icmap, int transient_scroller) : QProxyStyle(base), icon_map(icmap), transient_scroller(transient_scroller) { - setObjectName(QString("calibre")); - desktop_environment = detectDesktopEnvironment(); - button_layout = static_cast(QProxyStyle::styleHint(SH_DialogButtonLayout)); - if (QLatin1String("GNOME") == desktop_environment || QLatin1String("MATE") == desktop_environment || QLatin1String("UNITY") == desktop_environment || QLatin1String("CINNAMON") == desktop_environment || QLatin1String("X-CINNAMON") == desktop_environment) - button_layout = QDialogButtonBox::GnomeLayout; - } - - int styleHint(StyleHint hint, const QStyleOption *option = 0, - const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const { - switch (hint) { - case SH_DialogButtonBox_ButtonsHaveIcons: - return 1; // We want icons on dialog button box buttons - case SH_DialogButtonLayout: - // Use platform specific button orders always +int CalibreStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const { + switch (hint) { + case SH_DialogButtonBox_ButtonsHaveIcons: + return 1; // We want icons on dialog button box buttons + case SH_DialogButtonLayout: + // Use platform specific button orders always #ifdef Q_OS_WIN32 - return QDialogButtonBox::WinLayout; + return QDialogButtonBox::WinLayout; #elif defined(Q_OS_MAC) - return QDialogButtonBox::MacLayout; + return QDialogButtonBox::MacLayout; #endif - return button_layout; - case SH_FormLayoutFieldGrowthPolicy: - return QFormLayout::FieldsStayAtSizeHint; // Do not have fields expand to fill all available space in QFormLayout - case SH_ScrollBar_Transient: - return transient_scroller; + return button_layout; + case SH_FormLayoutFieldGrowthPolicy: + return QFormLayout::FieldsStayAtSizeHint; // Do not have fields expand to fill all available space in QFormLayout + case SH_ScrollBar_Transient: + return transient_scroller; #ifdef Q_OS_MAC - case SH_UnderlineShortcut: - return 0; + case SH_UnderlineShortcut: + return 0; #endif - default: + default: + break; + } + return QProxyStyle::styleHint(hint, option, widget, returnData); +} + +QIcon CalibreStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption * option, const QWidget * widget) const { + if (standardIcon == QStyle::SP_DialogCloseButton) { + bool is_dark_theme = QApplication::instance()->property("is_dark_theme").toBool(); + return QIcon(icon_map.value(QStyle::SP_CustomBase + (is_dark_theme ? 2 : 1))); + } + if (icon_map.contains(standardIcon)) return QIcon(icon_map.value(standardIcon)); + return QProxyStyle::standardIcon(standardIcon, option, widget); +} + +int CalibreStyle::pixelMetric(PixelMetric metric, const QStyleOption * option, const QWidget * widget) const { + switch (metric) { + case PM_TabBarTabVSpace: + return 8; // Make tab bars a little narrower, the value for the Fusion style is 12 + case PM_TreeViewIndentation: + return int(dpiScaled(12)); // Reduce indentation in tree views + default: + break; + } + return QProxyStyle::pixelMetric(metric, option, widget); +} + +void CalibreStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex * option, QPainter * painter, const QWidget * widget) const { + const QStyleOptionToolButton *toolbutton = NULL; + switch (control) { + case CC_ToolButton: // {{{ + // We do not want an arrow if the toolbutton has an instant popup + toolbutton = qstyleoption_cast(option); + if (toolbutton && (toolbutton->features & QStyleOptionToolButton::HasMenu) && !(toolbutton->features & QStyleOptionToolButton::PopupDelay)) { + QStyleOptionToolButton opt = QStyleOptionToolButton(*toolbutton); + opt.features = toolbutton->features & ~QStyleOptionToolButton::HasMenu; + return QProxyStyle::drawComplexControl(control, &opt, painter, widget); + } + break; /// }}} + default: + break; + } + return QProxyStyle::drawComplexControl(control, option, painter, widget); +} + +void CalibreStyle::drawPrimitive(PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget) const { + const QStyleOptionViewItem *vopt = NULL; + switch (element) { + case PE_FrameTabBarBase: // {{{ + // dont draw line below tabs in dark mode as it looks bad + if (const QStyleOptionTabBarBase *tbb = qstyleoption_cast(option)) { + if (tbb->shape == QTabBar::RoundedNorth) { + QColor bg = option->palette.color(QPalette::Window); + if (is_color_dark(bg)) return; + } + } + break; // }}} + + case PE_IndicatorCheckBox: // {{{ + // Fix color used to draw checkbox outline in dark mode + if (is_color_dark(option->palette.color(QPalette::Window))) { + baseStyle()->drawPrimitive(element, option, painter, widget); + painter->save(); + painter->translate(0.5, 0.5); + QRect rect = option->rect; + rect = rect.adjusted(0, 0, -1, -1); + + painter->setPen(QPen(option->palette.color(QPalette::WindowText))); + if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) + painter->setPen(QPen(Qt::white)); + painter->drawRect(rect); + painter->restore(); + return; + } + break; // }}} + + case PE_IndicatorRadioButton: // {{{ + // Fix color used to draw radiobutton outline in dark mode + if (is_color_dark(option->palette.color(QPalette::Window))) { + painter->save(); + painter->setBrush((option->state & State_Sunken) ? option->palette.base().color().lighter(320) : option->palette.base().color()); + painter->setRenderHint(QPainter::Antialiasing, true); + QPainterPath circle; + const QPointF circleCenter = option->rect.center() + QPoint(1, 1); + const qreal outlineRadius = (option->rect.width() + (option->rect.width() + 1) % 2) / 2.0 - 1; + circle.addEllipse(circleCenter, outlineRadius, outlineRadius); + painter->setPen(QPen(option->palette.window().color().lighter(320))); + if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) { + QColor highlightedOutline = option->palette.color(QPalette::Highlight).lighter(125); + painter->setPen(QPen(highlightedOutline)); + } + painter->drawPath(circle); + + if (option->state & (State_On )) { + circle = QPainterPath(); + const qreal checkmarkRadius = outlineRadius / 2.32; + circle.addEllipse(circleCenter, checkmarkRadius, checkmarkRadius); + QColor checkMarkColor = option->palette.text().color().lighter(120); + checkMarkColor.setAlpha(200); + painter->setPen(checkMarkColor); + checkMarkColor.setAlpha(180); + painter->setBrush(checkMarkColor); + painter->drawPath(circle); + } + painter->restore(); + return; + } else { baseStyle()->drawPrimitive(element, option, painter, widget); } + break; // }}} + + case PE_PanelItemViewItem: // {{{ + // Highlight the current, selected item with a different background in an item view if the highlight current item property is set + if (option->state & QStyle::State_HasFocus && (vopt = qstyleoption_cast(option)) && widget && widget->property("highlight_current_item").toBool()) { + QColor color = vopt->palette.color(QPalette::Normal, QPalette::Highlight); + QStyleOptionViewItem opt = QStyleOptionViewItem(*vopt); + if (is_color_dark(option->palette.color(QPalette::Window))) { + color = color.lighter(190); + } else { + color = color.lighter(125); + } + opt.palette.setColor(QPalette::Highlight, color); + return QProxyStyle::drawPrimitive(element, &opt, painter, widget); + } + break; // }}} + + case PE_IndicatorToolBarSeparator: // {{{ + // Make toolbar separators stand out a bit more in dark themes + { + QRect rect = option->rect; + const int margin = 6; + QColor bg = option->palette.color(QPalette::Window); + QColor first, second; + if (is_color_dark(bg)) { + first = bg.darker(115); + second = bg.lighter(115); + } else { + first = bg.darker(110); + second = bg.lighter(110); + } + if (option->state & State_Horizontal) { + const int offset = rect.width()/2; + painter->setPen(QPen(first)); + painter->drawLine(rect.bottomLeft().x() + offset, + rect.bottomLeft().y() - margin, + rect.topLeft().x() + offset, + rect.topLeft().y() + margin); + painter->setPen(QPen(second)); + painter->drawLine(rect.bottomLeft().x() + offset + 1, + rect.bottomLeft().y() - margin, + rect.topLeft().x() + offset + 1, + rect.topLeft().y() + margin); + } else { //Draw vertical separator + const int offset = rect.height()/2; + painter->setPen(QPen(first)); + painter->drawLine(rect.topLeft().x() + margin , + rect.topLeft().y() + offset, + rect.topRight().x() - margin, + rect.topRight().y() + offset); + painter->setPen(QPen(second)); + painter->drawLine(rect.topLeft().x() + margin , + rect.topLeft().y() + offset + 1, + rect.topRight().x() - margin, + rect.topRight().y() + offset + 1); + } + } + return; // }}} + + case PE_FrameFocusRect: // {{{ + if (!widget || !widget->property("frame_for_focus").toBool()) + break; + if (const QStyleOptionFocusRect *fropt = qstyleoption_cast(option)) { + if (!(fropt->state & State_KeyboardFocusChange)) break; + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->translate(0.5, 0.5); + painter->setPen(option->palette.color(QPalette::Text)); + painter->setBrush(Qt::transparent); + painter->drawRoundedRect(option->rect.adjusted(0, 0, -1, -1), 4, 4); + painter->restore(); + return; } - return QProxyStyle::styleHint(hint, option, widget, returnData); - } + break; // }}} + default: + break; + } + return QProxyStyle::drawPrimitive(element, option, painter, widget); +} - QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption * option = 0, const QWidget * widget = 0) const { - if (standardIcon == QStyle::SP_DialogCloseButton) { - bool is_dark_theme = QApplication::instance()->property("is_dark_theme").toBool(); - return QIcon(icon_map.value(QStyle::SP_CustomBase + (is_dark_theme ? 2 : 1))); +void CalibreStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { + switch(element) { + case CE_ItemViewItem: { + if (option->state & QStyle::State_HasFocus && (vopt = qstyleoption_cast(option)) && widget && widget->property("highlight_current_item").toBool()) { + if (is_color_dark(option->palette.color(QPalette::Window))) { + QStyleOptionViewItem opt = QStyleOptionViewItem(*vopt); + opt.palette.setColor(QPalette::HighlightedText, Qt::black); + QProxyStyle::drawControl(element, &opt, painter, widget); + return; + } } - if (icon_map.contains(standardIcon)) return QIcon(icon_map.value(standardIcon)); - return QProxyStyle::standardIcon(standardIcon, option, widget); - } + } break; - int pixelMetric(PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0) const { - switch (metric) { - case PM_TabBarTabVSpace: - return 8; // Make tab bars a little narrower, the value for the Fusion style is 12 - case PM_TreeViewIndentation: - return int(dpiScaled(12)); // Reduce indentation in tree views - default: - break; - } - return QProxyStyle::pixelMetric(metric, option, widget); - } - - void drawComplexControl(ComplexControl control, const QStyleOptionComplex * option, QPainter * painter, const QWidget * widget = 0) const { - const QStyleOptionToolButton *toolbutton = NULL; - switch (control) { - case CC_ToolButton: // {{{ - // We do not want an arrow if the toolbutton has an instant popup - toolbutton = qstyleoption_cast(option); - if (toolbutton && (toolbutton->features & QStyleOptionToolButton::HasMenu) && !(toolbutton->features & QStyleOptionToolButton::PopupDelay)) { - QStyleOptionToolButton opt = QStyleOptionToolButton(*toolbutton); - opt.features = toolbutton->features & ~QStyleOptionToolButton::HasMenu; - return QProxyStyle::drawComplexControl(control, &opt, painter, widget); + case CE_MenuItem: // {{{ + // Draw menu separators that work in both light and dark modes + if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast(option)) { + if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { + int w = 0; + const int margin = 5; + painter->save(); + if (!menuItem->text.isEmpty()) { + painter->setFont(menuItem->font); + proxy()->drawItemText(painter, menuItem->rect.adjusted(margin, 0, -margin, 0), Qt::AlignLeft | Qt::AlignVCenter, + menuItem->palette, menuItem->state & State_Enabled, menuItem->text, + QPalette::Text); + w = menuItem->fontMetrics.horizontalAdvance(menuItem->text) + margin; } - break; /// }}} - default: - break; + if (is_color_dark(menuItem->palette.color(QPalette::Window))) painter->setPen(Qt::gray); + else painter->setPen(QColor(0, 0, 0, 60).lighter(106)); + bool reverse = menuItem->direction == Qt::RightToLeft; + painter->drawLine(menuItem->rect.left() + margin + (reverse ? 0 : w), menuItem->rect.center().y(), + menuItem->rect.right() - margin - (reverse ? w : 0), menuItem->rect.center().y()); + painter->restore(); + return; + } } - return QProxyStyle::drawComplexControl(control, option, painter, widget); - } + break; // }}} - void drawPrimitive(PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget = 0) const { - const QStyleOptionViewItem *vopt = NULL; - switch (element) { - case PE_FrameTabBarBase: // {{{ - // dont draw line below tabs in dark mode as it looks bad - if (const QStyleOptionTabBarBase *tbb = qstyleoption_cast(option)) { - if (tbb->shape == QTabBar::RoundedNorth) { - QColor bg = option->palette.color(QPalette::Window); - if (is_color_dark(bg)) return; - } - } - break; // }}} - - case PE_IndicatorCheckBox: // {{{ - // Fix color used to draw checkbox outline in dark mode - if (is_color_dark(option->palette.color(QPalette::Window))) { - baseStyle()->drawPrimitive(element, option, painter, widget); - painter->save(); - painter->translate(0.5, 0.5); - QRect rect = option->rect; - rect = rect.adjusted(0, 0, -1, -1); - - painter->setPen(QPen(option->palette.color(QPalette::WindowText))); - if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) - painter->setPen(QPen(Qt::white)); - painter->drawRect(rect); - painter->restore(); - return; - } - break; // }}} - - case PE_IndicatorRadioButton: // {{{ - // Fix color used to draw radiobutton outline in dark mode - if (is_color_dark(option->palette.color(QPalette::Window))) { - painter->save(); - painter->setBrush((option->state & State_Sunken) ? option->palette.base().color().lighter(320) : option->palette.base().color()); - painter->setRenderHint(QPainter::Antialiasing, true); - QPainterPath circle; - const QPointF circleCenter = option->rect.center() + QPoint(1, 1); - const qreal outlineRadius = (option->rect.width() + (option->rect.width() + 1) % 2) / 2.0 - 1; - circle.addEllipse(circleCenter, outlineRadius, outlineRadius); - painter->setPen(QPen(option->palette.window().color().lighter(320))); - if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) { - QColor highlightedOutline = option->palette.color(QPalette::Highlight).lighter(125); - painter->setPen(QPen(highlightedOutline)); - } - painter->drawPath(circle); - - if (option->state & (State_On )) { - circle = QPainterPath(); - const qreal checkmarkRadius = outlineRadius / 2.32; - circle.addEllipse(circleCenter, checkmarkRadius, checkmarkRadius); - QColor checkMarkColor = option->palette.text().color().lighter(120); - checkMarkColor.setAlpha(200); - painter->setPen(checkMarkColor); - checkMarkColor.setAlpha(180); - painter->setBrush(checkMarkColor); - painter->drawPath(circle); - } - painter->restore(); - return; - } else { baseStyle()->drawPrimitive(element, option, painter, widget); } - break; // }}} - - case PE_PanelItemViewItem: // {{{ - // Highlight the current, selected item with a different background in an item view if the highlight current item property is set - if (option->state & QStyle::State_HasFocus && (vopt = qstyleoption_cast(option)) && widget && widget->property("highlight_current_item").toBool()) { - QColor color = vopt->palette.color(QPalette::Normal, QPalette::Highlight); - QStyleOptionViewItem opt = QStyleOptionViewItem(*vopt); - if (is_color_dark(option->palette.color(QPalette::Window))) { - color = color.lighter(190); - } else { - color = color.lighter(125); - } - opt.palette.setColor(QPalette::Highlight, color); - return QProxyStyle::drawPrimitive(element, &opt, painter, widget); - } - break; // }}} - - case PE_IndicatorToolBarSeparator: // {{{ - // Make toolbar separators stand out a bit more in dark themes - { - QRect rect = option->rect; - const int margin = 6; - QColor bg = option->palette.color(QPalette::Window); - QColor first, second; - if (is_color_dark(bg)) { - first = bg.darker(115); - second = bg.lighter(115); - } else { - first = bg.darker(110); - second = bg.lighter(110); - } - if (option->state & State_Horizontal) { - const int offset = rect.width()/2; - painter->setPen(QPen(first)); - painter->drawLine(rect.bottomLeft().x() + offset, - rect.bottomLeft().y() - margin, - rect.topLeft().x() + offset, - rect.topLeft().y() + margin); - painter->setPen(QPen(second)); - painter->drawLine(rect.bottomLeft().x() + offset + 1, - rect.bottomLeft().y() - margin, - rect.topLeft().x() + offset + 1, - rect.topLeft().y() + margin); - } else { //Draw vertical separator - const int offset = rect.height()/2; - painter->setPen(QPen(first)); - painter->drawLine(rect.topLeft().x() + margin , - rect.topLeft().y() + offset, - rect.topRight().x() - margin, - rect.topRight().y() + offset); - painter->setPen(QPen(second)); - painter->drawLine(rect.topLeft().x() + margin , - rect.topLeft().y() + offset + 1, - rect.topRight().x() - margin, - rect.topRight().y() + offset + 1); - } - } - return; // }}} - case PE_FrameFocusRect: // }}} - if (!widget || !widget->property("frame_for_focus").toBool()) - break; - if (const QStyleOptionFocusRect *fropt = qstyleoption_cast(option)) { - if (!(fropt->state & State_KeyboardFocusChange)) - break; - painter->save(); - painter->setRenderHint(QPainter::Antialiasing, true); - painter->translate(0.5, 0.5); - painter->setPen(option->palette.color(QPalette::Text)); - painter->setBrush(Qt::transparent); - painter->drawRoundedRect(option->rect.adjusted(0, 0, -1, -1), 4, 4); - painter->restore(); - return; - } - break; // }}} - default: - break; - } - return QProxyStyle::drawPrimitive(element, option, painter, widget); - } - - void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { - const QStyleOptionViewItem *vopt = NULL; - switch(element) { - case CE_ItemViewItem: { - if (option->state & QStyle::State_HasFocus && (vopt = qstyleoption_cast(option)) && widget && widget->property("highlight_current_item").toBool()) { - if (is_color_dark(option->palette.color(QPalette::Window))) { - QStyleOptionViewItem opt = QStyleOptionViewItem(*vopt); - opt.palette.setColor(QPalette::HighlightedText, Qt::black); - QProxyStyle::drawControl(element, &opt, painter, widget); - return; - } - } - } break; - case CE_MenuItem: // {{{ - // Draw menu separators that work in both light and dark modes - if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast(option)) { - if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { - int w = 0; - const int margin = 5; - painter->save(); - if (!menuItem->text.isEmpty()) { - painter->setFont(menuItem->font); - proxy()->drawItemText(painter, menuItem->rect.adjusted(margin, 0, -margin, 0), Qt::AlignLeft | Qt::AlignVCenter, - menuItem->palette, menuItem->state & State_Enabled, menuItem->text, - QPalette::Text); - w = menuItem->fontMetrics.horizontalAdvance(menuItem->text) + margin; - } - if (is_color_dark(menuItem->palette.color(QPalette::Window))) painter->setPen(Qt::gray); - else painter->setPen(QColor(0, 0, 0, 60).lighter(106)); - bool reverse = menuItem->direction == Qt::RightToLeft; - painter->drawLine(menuItem->rect.left() + margin + (reverse ? 0 : w), menuItem->rect.center().y(), - menuItem->rect.right() - margin - (reverse ? w : 0), menuItem->rect.center().y()); - painter->restore(); - return; - } - } - break; // }}} - - default: break; - } - QProxyStyle::drawControl(element, option, painter, widget); - } -}; + default: break; + } + QProxyStyle::drawControl(element, option, painter, widget); +} int load_style(const QHash &icon_map, int transient_scroller) { - QStyle *base_style = QStyleFactory::create(QString("Fusion")); - QApplication::setStyle(new CalibreStyle(base_style, icon_map, transient_scroller)); + QApplication::setStyle(new CalibreStyle(icon_map, transient_scroller)); return 0; } diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.h b/src/calibre/gui2/progress_indicator/QProgressIndicator.h index 9cc5459abf..76b07eb0b7 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.h +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #define arc_length_max 0.734f #define arc_length_min 0.02f @@ -87,6 +89,23 @@ private: QParallelAnimationGroup m_animation; }; +class CalibreStyle : public QProxyStyle { + private: + const QHash icon_map; + QByteArray desktop_environment; + QDialogButtonBox::ButtonLayout button_layout; + int transient_scroller; + + public: + CalibreStyle(const QHash &icmap, int transient_scroller); + virtual int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const; + virtual QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption * option = 0, const QWidget * widget = 0) const; + virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0) const; + virtual void drawComplexControl(ComplexControl control, const QStyleOptionComplex * option, QPainter * painter, const QWidget * widget = 0) const; + virtual void drawPrimitive(PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget = 0) const; + virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const; +}; + /*! \class QProgressIndicator \brief The QProgressIndicator class lets an application display a progress indicator to show that a lengthy task is under way.