/**************************************************************************** ** Copyright (c) 2021, Fougue Ltd. ** All rights reserved. ** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt ****************************************************************************/ #include "theme.h" #include "../base/meta_enum.h" #include "../base/span.h" #include #include #include #include #include #include namespace Mayo { namespace Internal { static const QIcon& nullQIcon() { static const QIcon null; return null; } static QString cssFlatComboBox( const QString& urlPixDownArrow, const QString& urlPixDownArrowDisabled) { const QPalette appPalette = qApp->palette(); const QString css = QString( R"( QComboBox { border-style: solid; background: %1; padding: 2px 15px 2px 10px; } QComboBox:hover { border-style: solid; background: %2; padding: 2px 15px 2px 10px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 15px; border-left-width: 0px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } QComboBox::down-arrow { image: url(%3); } QComboBox::down-arrow:disabled { image: url(%4); } )" ).arg(appPalette.color(QPalette::Window).name(), appPalette.color(QPalette::Window).darker(110).name(), urlPixDownArrow, urlPixDownArrowDisabled); return css; } static QPixmap invertedPixmap(const QPixmap& pix) { QImage img = pix.toImage(); img.invertPixels(); return QPixmap::fromImage(img); } static QString iconFileName(Theme::Icon icn) { switch (icn) { case Theme::Icon::AddFile: return "add-file.svg"; case Theme::Icon::File: return "file.svg"; case Theme::Icon::OpenFiles: return "open-files.svg"; case Theme::Icon::Import: return "import.svg"; case Theme::Icon::Edit: return "edit.svg"; case Theme::Icon::Export: return "export.svg"; case Theme::Icon::Expand: return "expand.svg"; case Theme::Icon::Cross: return "cross.svg"; case Theme::Icon::Grid: return "grid.svg"; case Theme::Icon::Link: return "link.svg"; case Theme::Icon::Back: return "back.svg"; case Theme::Icon::Next: return "next.svg"; case Theme::Icon::Multiple: return "multiple.svg"; case Theme::Icon::Camera: return "camera.svg"; case Theme::Icon::LeftSidebar: return "left-sidebar.svg"; case Theme::Icon::BackSquare: return "back-square.svg"; case Theme::Icon::IndicatorDown: return "indicator-down_8.png"; case Theme::Icon::Reload: return "reload.svg"; case Theme::Icon::Stop: return "stop.svg"; case Theme::Icon::Gear: return "gear.svg"; case Theme::Icon::ZoomIn: return "zoom-in.svg"; case Theme::Icon::ZoomOut: return "zoom-out.svg"; case Theme::Icon::ClipPlane: return "clip-plane.svg"; case Theme::Icon::Measure: return "measure.svg"; case Theme::Icon::View3dIso: return "view-iso.svg"; case Theme::Icon::View3dLeft: return "view-left.svg"; case Theme::Icon::View3dRight: return "view-right.svg"; case Theme::Icon::View3dTop: return "view-top.svg"; case Theme::Icon::View3dBottom: return "view-bottom.svg"; case Theme::Icon::View3dFront: return "view-front.svg"; case Theme::Icon::View3dBack: return "view-back.svg"; case Theme::Icon::TurnClockwise: return "turn-cw.svg"; case Theme::Icon::TurnCounterClockwise: return "turn-ccw.svg"; case Theme::Icon::ItemMesh: return "item-mesh.svg"; case Theme::Icon::ItemXde: return "item-xde.svg"; case Theme::Icon::XdeAssembly: return "xde-assembly.svg"; case Theme::Icon::XdeSimpleShape: return "xde-simple-shape.svg"; } return QString(); } class ThemeClassic : public Theme { public: QColor color(Color role) const override { const QPalette appPalette = qApp->palette(); switch (role) { case Theme::Color::Palette_Base: return appPalette.color(QPalette::Base); case Theme::Color::Palette_Window: return appPalette.color(QPalette::Window); case Theme::Color::Palette_Button: case Theme::Color::ButtonFlat_Background: return appPalette.color(QPalette::Button); case Theme::Color::ButtonView3d_Background: { return appPalette.color(QPalette::Button); } case Theme::Color::ButtonFlat_Hover: return appPalette.color(QPalette::Button).darker(110); case Theme::Color::ButtonFlat_Checked: return appPalette.color(QPalette::Button).darker(125); case Theme::Color::ButtonView3d_Hover: case Theme::Color::ButtonView3d_Checked: return QColor(65, 200, 250); case Theme::Color::Graphic3d_AspectFillArea: return QColor(128, 200, 255); case Theme::Color::View3d_BackgroundGradientStart: return QColor(128, 148, 255); case Theme::Color::View3d_BackgroundGradientEnd: return Qt::white; case Theme::Color::RubberBandView3d_Line: return QColor(65, 200, 250); case Theme::Color::RubberBandView3d_Fill: return QColor(65, 200, 250).lighter(); case Theme::Color::MessageIndicator_InfoBackground: return QColor(128, 200, 255); case Theme::Color::MessageIndicator_InfoText: case Theme::Color::MessageIndicator_ErrorText: return appPalette.color(QPalette::WindowText); case Theme::Color::MessageIndicator_ErrorBackground: return QColor(225, 127, 127, 140); } return QColor(); } const QIcon& icon(Icon icn) const override { auto it = m_mapIcon.find(icn); return it != m_mapIcon.cend() ? it->second : nullQIcon(); } void setup() override { const QString icnBasePath = ":/images/themes/classic/"; for (const Icon icn : MetaEnum::values()) { const QString icnFileName = iconFileName(icn); m_mapIcon.emplace(icn, QIcon(QPixmap(icnBasePath + icnFileName))); } } void setupHeaderComboBox(QComboBox* cb) override { const QString urlDown(":/images/themes/classic/indicator-down_8.png"); const QString urlDownDisabled(":/images/themes/classic/indicator-down-disabled_8.png"); cb->setStyleSheet(cssFlatComboBox(urlDown, urlDownDisabled)); } private: std::unordered_map m_mapIcon; }; class ThemeDark : public Theme { public: QColor color(Color role) const override { const QPalette appPalette = qApp->palette(); switch (role) { case Theme::Color::Palette_Base: return appPalette.color(QPalette::Base); case Theme::Color::Palette_Window: return appPalette.color(QPalette::Window); case Theme::Color::Palette_Button: case Theme::Color::ButtonFlat_Background: return appPalette.color(QPalette::Button); case Theme::Color::ButtonFlat_Hover: return appPalette.color(QPalette::Button).darker(110); case Theme::Color::ButtonFlat_Checked: return appPalette.color(QPalette::Highlight); case Theme::Color::ButtonView3d_Background: return appPalette.color(QPalette::Button).lighter(125); case Theme::Color::ButtonView3d_Hover: return appPalette.color(QPalette::Button).lighter(160); case Theme::Color::ButtonView3d_Checked: return appPalette.color(QPalette::Highlight); case Theme::Color::Graphic3d_AspectFillArea: return appPalette.color(QPalette::Highlight); case Theme::Color::View3d_BackgroundGradientStart: return QColor(100, 100, 100); case Theme::Color::View3d_BackgroundGradientEnd: return QColor(200, 200, 200); case Theme::Color::RubberBandView3d_Line: return appPalette.color(QPalette::Highlight); case Theme::Color::RubberBandView3d_Fill: return appPalette.color(QPalette::Highlight).lighter(); case Theme::Color::MessageIndicator_InfoBackground: return appPalette.color(QPalette::Highlight); case Theme::Color::MessageIndicator_InfoText: case Theme::Color::MessageIndicator_ErrorText: return appPalette.color(QPalette::WindowText); case Theme::Color::MessageIndicator_ErrorBackground: return QColor(225, 127, 127, 140); } return QColor(); } const QIcon& icon(Icon icn) const override { auto it = m_mapIcon.find(icn); return it != m_mapIcon.cend() ? it->second : nullQIcon(); } void setup() override { auto fnIsNeutralIcon = [](Icon icn) { return icn == Icon::AddFile || icn == Icon::OpenFiles || icn == Icon::Stop; }; for (Icon icn : MetaEnum::values()) { const QString icnFileName = iconFileName(icn); const QString icnBasePath = !fnIsNeutralIcon(icn) ? ":/images/themes/dark/" : ":/images/themes/classic/"; QPixmap pix(icnBasePath + icnFileName); if (!fnIsNeutralIcon(icn)) { const bool invertColors = icn != Icon::XdeAssembly && icn != Icon::XdeSimpleShape; if (invertColors) pix = invertedPixmap(pix); } m_mapIcon.emplace(icn, pix); } qApp->setStyle(QStyleFactory::create("Fusion")); QPalette p = qApp->palette(); p.setColor(QPalette::Base, QColor(80, 80, 80)); p.setColor(QPalette::Window, QColor(37, 37, 37)); p.setColor(QPalette::Button, QColor(73, 73, 73)); p.setColor(QPalette::Text, Qt::white); p.setColor(QPalette::ButtonText, Qt::white); p.setColor(QPalette::WindowText, Qt::white); p.setColor(QPalette::HighlightedText, Qt::white); const QColor linkColor(115, 131, 191); p.setColor(QPalette::Link, linkColor); p.setColor(QPalette::LinkVisited, linkColor); const QColor disabledGray(40, 40, 40); const QColor disabledTextGray(128, 128, 128); p.setColor(QPalette::Disabled, QPalette::Window, disabledGray); p.setColor(QPalette::Disabled, QPalette::Base, disabledGray); p.setColor(QPalette::Disabled, QPalette::AlternateBase, disabledGray); p.setColor(QPalette::Disabled, QPalette::Button, disabledGray); p.setColor(QPalette::Disabled, QPalette::Text, disabledTextGray); p.setColor(QPalette::Disabled, QPalette::ButtonText, disabledTextGray); p.setColor(QPalette::Disabled, QPalette::WindowText, disabledTextGray); qApp->setPalette(p); const QString css = R"( QFrame[frameShape="5"] { color: gray; margin-top: 2px; margin-bottom: 2px; } QAbstractItemView { show-decoration-selected: 1; background: #252525; selection-background-color: #505050; } QAbstractItemView::item:hover { background: #383838; } QMenu { background: #252525; border: 1px solid rgb(100,100,100); } QMenu::item:selected { background: rgb(110,110,110); } QMenu::separator { background: rgb(110,110,110); height: 1px; } QLineEdit { background: #505050; } QTextEdit { background: #505050; } QSpinBox { background: #505050; } QDoubleSpinBox { background: #505050; } QToolButton:checked { background: #383838; } QToolButton:pressed { background: #383838; } QComboBox { background: #505050; } QGroupBox { border: 1px solid #808080; margin-top: 4ex; } QFileDialog { background: #505050; } QComboBox:editable { background: #505050; } QComboBox:disabled { background: rgb(40,40,40); } QProgressBar { background: #505050; } )"; qApp->setStyleSheet(css); } void setupHeaderComboBox(QComboBox* cb) override { const QString urlDown(":/images/themes/dark/indicator-down_8.png"); const QString urlDownDisabled(":/images/themes/classic/indicator-down-disabled_8.png"); cb->setStyleSheet(cssFlatComboBox(urlDown, urlDownDisabled)); } private: std::unordered_map m_mapIcon; }; } // namespace Internal Theme* createTheme(const QString& key) { if (key == "classic") return new Internal::ThemeClassic; if (key == "dark") return new Internal::ThemeDark; return nullptr; } } // namespace Mayo