// Copyright (C) 2023 Tasuku Suzuki // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qtcwidgets.h" #include "hostosinfo.h" #include "icon.h" #include "networkaccessmanager.h" #include "qtdesignsystemstyle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Utils { using namespace StyleHelper; using namespace StyleHelper::SpacingTokens; const qreal disabledIconOpacity = 0.3; enum WidgetState { WidgetStateDefault, WidgetStateChecked, WidgetStateHovered, }; static const TextFormat &buttonTF(QtcButton::Role role, WidgetState state) { static const TextFormat largePrimaryTF { .themeColor = Theme::Token_Text_On_Accent, .uiElement = StyleHelper::UiElement::UiElementH5, .drawTextFlags = Qt::AlignCenter | Qt::TextDontClip | Qt::TextShowMnemonic, }; static const TextFormat largeSecondaryTF { .themeColor = Theme::Token_Text_Default, .uiElement = largePrimaryTF.uiElement, .drawTextFlags = largePrimaryTF.drawTextFlags, }; static const TextFormat mediumPrimaryTF { .themeColor = largePrimaryTF.themeColor, .uiElement = StyleHelper::UiElement::UiElementButtonMedium, .drawTextFlags = largePrimaryTF.drawTextFlags, }; static const TextFormat mediumSecondaryTF { .themeColor = largeSecondaryTF.themeColor, .uiElement = mediumPrimaryTF.uiElement, .drawTextFlags = mediumPrimaryTF.drawTextFlags, }; static const TextFormat smallPrimaryTF { .themeColor = largePrimaryTF.themeColor, .uiElement = StyleHelper::UiElement::UiElementButtonSmall, .drawTextFlags = largePrimaryTF.drawTextFlags, }; static const TextFormat smallSecondaryTF { .themeColor = largeSecondaryTF.themeColor, .uiElement = smallPrimaryTF.uiElement, .drawTextFlags = smallPrimaryTF.drawTextFlags, }; static const TextFormat smallListDefaultTF { .themeColor = Theme::Token_Text_Default, .uiElement = StyleHelper::UiElement::UiElementIconStandard, .drawTextFlags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip | Qt::TextShowMnemonic, }; static const TextFormat smallListCheckedTF = smallListDefaultTF; static const TextFormat smallLinkDefaultTF { .themeColor = Theme::Token_Text_Default, .uiElement = StyleHelper::UiElement::UiElementIconStandard, .drawTextFlags = smallListDefaultTF.drawTextFlags, }; static const TextFormat smallLinkHoveredTF { .themeColor = Theme::Token_Text_Accent, .uiElement = smallLinkDefaultTF.uiElement, .drawTextFlags = smallLinkDefaultTF.drawTextFlags, }; static const TextFormat tagDefaultTF { .themeColor = Theme::Token_Text_Muted, .uiElement = StyleHelper::UiElement::UiElementLabelMedium, }; static const TextFormat tagHoverTF { .themeColor = Theme::Token_Text_Default, .uiElement = tagDefaultTF.uiElement, }; switch (role) { case QtcButton::LargePrimary: return largePrimaryTF; case QtcButton::LargeSecondary: case QtcButton::LargeTertiary: case QtcButton::LargeGhost: return largeSecondaryTF; case QtcButton::MediumPrimary: return mediumPrimaryTF; case QtcButton::MediumSecondary: case QtcButton::MediumTertiary: case QtcButton::MediumGhost: return mediumSecondaryTF; case QtcButton::SmallPrimary: return smallPrimaryTF; case QtcButton::SmallSecondary: case QtcButton::SmallTertiary: case QtcButton::SmallGhost: return smallSecondaryTF; case QtcButton::SmallList: return (state == WidgetStateDefault) ? smallListDefaultTF : smallListCheckedTF; case QtcButton::SmallLink: return (state == WidgetStateDefault) ? smallLinkDefaultTF : smallLinkHoveredTF; case QtcButton::Tag: return (state == WidgetStateDefault) ? tagDefaultTF : tagHoverTF; } return mediumPrimaryTF; } QtcButton::QtcButton(const QString &text, Role role, QWidget *parent) : QAbstractButton(parent) , m_role(role) { setText(text); setAttribute(Qt::WA_Hover); updateMargins(); if (m_role == SmallList) setCheckable(true); else if (m_role == SmallLink) setCursor(Qt::PointingHandCursor); } QSize QtcButton::minimumSizeHint() const { int maxTextWidth = 0; for (WidgetState state : {WidgetStateDefault, WidgetStateChecked, WidgetStateHovered} ) { const TextFormat &tf = buttonTF(m_role, state); const QFontMetrics fm(tf.font()); const QSize textS = fm.size(Qt::TextShowMnemonic, text()); maxTextWidth = qMax(maxTextWidth, textS.width()); } const TextFormat &tf = buttonTF(m_role, WidgetStateDefault); const QMargins margins = contentsMargins(); return {margins.left() + maxTextWidth + margins.right(), margins.top() + tf.lineHeight() + margins.bottom()}; } static int iconLabelGap(QtcButton::Role role) { switch (role) { case QtcButton::LargePrimary: case QtcButton::LargeSecondary: case QtcButton::LargeTertiary: case QtcButton::LargeGhost: return GapHM; case QtcButton::MediumPrimary: case QtcButton::MediumSecondary: case QtcButton::MediumTertiary: case QtcButton::MediumGhost: case QtcButton::SmallList: case QtcButton::SmallLink: return GapHXs; case QtcButton::SmallPrimary: case QtcButton::SmallSecondary: case QtcButton::SmallTertiary: case QtcButton::SmallGhost: case QtcButton::Tag: return GapHXs; } Q_UNREACHABLE_RETURN(-1); } void QtcButton::paintEvent(QPaintEvent *event) { // Without pixmap // +------------------------+-----------+------------------------+ // | |(PaddingVM)| | // | +-----------+ | // |([PaddingHXL|PaddingHM])|