Qt Creator源码分析系列——UI界面:FancyActionBar类

该篇文章内容主要集中Qt Creator软件界面部分代码的分析。从分析插件中的coreplugin中的fancyactionbar模块开始,项目文件在路径\qt-creator-master\qt-creator-master\src\plugins\coreplugin\下。

在这里插入图片描述

FancyActionBar类

class FancyActionBar : public QWidget
{
    Q_OBJECT
public:
    FancyActionBar(QWidget *parent = nullptr);
    void paintEvent(QPaintEvent *event) override;
    void insertAction(int index, QAction *action);
    void addProjectSelector(QAction *action);
    QLayout *actionsLayout() const;
    QSize minimumSizeHint() const override;
    void setIconsOnly(bool iconsOnly);
private:
    QVBoxLayout *m_actionsLayout;
    bool m_iconsOnly = false;
};

先看构造函数,构造垂直布局QVBoxLayout赋给FancyActionBar类的成员变量m_actionsLayout。而actionsLayout函数用于返回FancyActionBar类的成员变量m_actionsLayout。

FancyActionBar::FancyActionBar(QWidget *parent) : QWidget(parent)
{
    setObjectName("actionbar");
    m_actionsLayout = new QVBoxLayout;
    m_actionsLayout->setContentsMargins(0, 0, 0, 0);
    m_actionsLayout->setSpacing(0);
    setLayout(m_actionsLayout);
    setContentsMargins(0, 2, 0, 8);
}
QLayout *FancyActionBar::actionsLayout() const
{
    return m_actionsLayout;
}

成员函数addProjectSelector函数接收QAction指针类型的形参,函数体内调用insertAction函数。insertAction额外接收一个int型的形参。在insertActon函数中,新建一个FancyToolButton类型的按钮,并为该button设置对象名。调用根据是否只有图标的标志为FancyToolButton类型的button设置相应的标志。最后将该button加入垂直布局。

void FancyActionBar::addProjectSelector(QAction *action)
{
    insertAction(0, action);
}
void FancyActionBar::insertAction(int index, QAction *action)
{
    auto *button = new FancyToolButton(action, this);
    if (!action->objectName().isEmpty())
        button->setObjectName(action->objectName() + ".Button"); // used for UI introduction
    button->setIconsOnly(m_iconsOnly);
    m_actionsLayout->insertWidget(index, button);
}

在这里插入图片描述
在位置index处插入widget部件,并设置拉伸因子拉伸和对齐方式对齐。 如果index为负,则在末尾添加小部件。拉伸因子仅适用于QBoxLayout的方向,相对此QBoxLayout中的其他框和部件。 具有较高拉伸因子的小部件和盒子将增长更多。
如果拉伸因子为0,并且QBoxLayout中的其他拉伸因子都不大于零,则根据所涉及的每个小部件的QWidget:sizePolicy()分配空间。对齐方式是通过alignment指定的。 默认对齐方式为0,这意味着小部件将填充整个单元格。
因此addProjectSelector函数每次都在index为零处添加按钮。

FancyActionBar::setIconsOnly(bool iconsOnly)用于设置FancyActionBar上布局管理的widget类的IconsOnly属性。

void FancyActionBar::setIconsOnly(bool iconsOnly)
{
    m_iconsOnly = iconsOnly;
    for (int i = 0, c = m_actionsLayout->count(); i < c; ++i) {
        if (auto *button = qobject_cast<FancyToolButton*>(m_actionsLayout->itemAt(i)->widget()))
            button->setIconsOnly(iconsOnly);
    }
    setContentsMargins(0, iconsOnly ? 7 : 2, 0, iconsOnly ? 2 : 8);
}

在这里插入图片描述
如果对象是类型T(或子类),则返回转换为类型T的给定对象。 否则返回0。如果object为0,那么它也将返回0。类T必须(直接或间接)继承QObject,并使用Q_OBJECT宏进行声明。

绘制事件

void FancyActionBar::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    const QRectF borderRect = QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5);
    if (creatorTheme()->flag(Theme::FlatToolBars)) {
        // this paints the background of the bottom portion of the
        // left tab bar
        painter.fillRect(event->rect(), StyleHelper::baseColor());
        painter.setPen(creatorTheme()->color(Theme::FancyToolBarSeparatorColor));
        painter.drawLine(borderRect.topLeft(), borderRect.topRight());
    } else {
        painter.setPen(StyleHelper::sidebarShadow());
        painter.drawLine(borderRect.topLeft(), borderRect.topRight());
        painter.setPen(StyleHelper::sidebarHighlight());
        painter.drawLine(borderRect.topLeft() + QPointF(1, 1),
                         borderRect.topRight() + QPointF(0, 1));
    }
}

在这里插入图片描述

FancyToolButton类

FancyToolButton类继承自QToolButton类

class FancyToolButton : public QToolButton
{
    Q_OBJECT
    Q_PROPERTY(qreal fader READ fader WRITE setFader)
public:
    FancyToolButton(QAction *action, QWidget *parent = nullptr);
    void paintEvent(QPaintEvent *event) override;
    bool event(QEvent *e) override;
    QSize sizeHint() const override;
    QSize minimumSizeHint() const override;
    qreal fader() const { return m_fader; }
    void setFader(qreal value)
    {
        m_fader = value;
        update();
    }
    void setIconsOnly(bool iconsOnly);
    static void hoverOverlay(QPainter *painter, const QRect &spanRect);
private:
    void actionChanged();
    qreal m_fader = 0;
    bool m_iconsOnly = false;
};

先看构造函数,

FancyToolButton::FancyToolButton(QAction *action, QWidget *parent) : QToolButton(parent)
{
    setDefaultAction(action);
    // 将QAction::changed信号,连接到FancyToolButton::actionChanged槽
    connect(action, &QAction::changed, this, &FancyToolButton::actionChanged);
    actionChanged();
    setAttribute(Qt::WA_Hover, true);
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
}

在这里插入图片描述
将默认操作设置为action。如果工具按钮具有默认操作,则该操作定义按钮的属性,例如文本,图标,工具提示等。

void FancyToolButton::actionChanged()
{
    // the default action changed in some way, e.g. it might got hidden
    // since we inherit a tool button we won't get invisible, so do this here
    if (QAction *action = defaultAction())
        setVisible(action->isVisible());
}

新建工程测试FancyToolButton类

我们新建一个工程,并将FancyToolButton类的源文件和头文件导入。注释掉文件中依赖其他类的语句。比如注释掉event中的QEvent::ToolTip等,注释掉paintEvent、sizeHint和hoverOverlay函数。
实现和效果如下所示:
在这里插入图片描述
在这里插入图片描述
将sizeHint中依赖的两个常量写入到程序中,如下图所示。由此可见,sizeHint和minimumSizeHint都是QWidget的重载的函数,用于返回窗口相应的推荐大小的函数。
在这里插入图片描述

QSize FancyToolButton::sizeHint() const
{
	// 如果只有图标,直接返回推荐值大小
    if (m_iconsOnly) {
        //return {Core::Constants::MODEBAR_ICONSONLY_BUTTON_SIZE, Core::Constants::MODEBAR_ICONSONLY_BUTTON_SIZE};
        return {38,38};
    }

	// 调整图标大小,并根据defaultAction的titledAction和heading属性更新大小
    QSizeF buttonSize = iconSize().expandedTo(QSize(64, 38));
    if (defaultAction() && defaultAction()->property("titledAction").toBool()) {
        QFont boldFont(font());
        //boldFont.setPointSizeF(StyleHelper::sidebarFontSize());
        boldFont.setPointSizeF(7.5);
        boldFont.setBold(true);
        const QFontMetrics fm(boldFont);
        const qreal lineHeight = fm.height();
        const QString projectName = defaultAction()->property("heading").toString();
        buttonSize += QSizeF(0, 10);
        if (!projectName.isEmpty())
            buttonSize += QSizeF(0, lineHeight + 2);

        buttonSize += QSizeF(0, lineHeight * 2 + 2);
    }
    return buttonSize.toSize();
}

在项目中添加HostOsInfo类、Theme类和StyleHelper类,然后测试paintEvent函数:

void FancyToolButton::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter painter(this);

    // draw borders
    if (!HostOsInfo::isMacHost() // Mac UIs usually don't hover
        && m_fader > 0 && isEnabled() && !isDown() && !isChecked()) {
        painter.save();
        if (creatorTheme()->flag(Theme::FlatToolBars)) {
            const QColor hoverColor = creatorTheme()->color(Theme::FancyToolButtonHoverColor);
            QColor fadedHoverColor = hoverColor;
            fadedHoverColor.setAlpha(int(m_fader * hoverColor.alpha()));
            painter.fillRect(rect(), fadedHoverColor);
        } else {
            painter.setOpacity(m_fader);
            FancyToolButton::hoverOverlay(&painter, rect());
        }
        painter.restore();
    } else if (isDown() || isChecked()) {
        painter.save();
        const QColor selectedColor = creatorTheme()->color(Theme::FancyToolButtonSelectedColor);
        if (creatorTheme()->flag(Theme::FlatToolBars)) {
            painter.fillRect(rect(), selectedColor);
        } else {
            QLinearGradient grad(rect().topLeft(), rect().topRight());
            grad.setColorAt(0, Qt::transparent);
            grad.setColorAt(0.5, selectedColor);
            grad.setColorAt(1, Qt::transparent);
            painter.fillRect(rect(), grad);
            painter.setPen(QPen(grad, 1.0));
            const QRectF borderRectF(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5));
            painter.drawLine(borderRectF.topLeft(), borderRectF.topRight());
            painter.drawLine(borderRectF.topLeft(), borderRectF.topRight());
            painter.drawLine(borderRectF.topLeft() + QPointF(0, 1),
                             borderRectF.topRight() + QPointF(0, 1));
            painter.drawLine(borderRectF.bottomLeft(), borderRectF.bottomRight());
            painter.drawLine(borderRectF.bottomLeft(), borderRectF.bottomRight());
        }
        painter.restore();
    }

    const QIcon::Mode iconMode = isEnabled()
                                     ? ((isDown() || isChecked()) ? QIcon::Active : QIcon::Normal)
                                     : QIcon::Disabled;
    QRect iconRect(0, 0, Constants::MODEBAR_ICON_SIZE, Constants::MODEBAR_ICON_SIZE);

    const bool isTitledAction = defaultAction() && defaultAction()->property("titledAction").toBool();
    // draw popup texts
    if (isTitledAction && !m_iconsOnly) {
        QFont normalFont(painter.font());
        QRect centerRect = rect();
        normalFont.setPointSizeF(StyleHelper::sidebarFontSize());
        QFont boldFont(normalFont);
        boldFont.setBold(true);
        const QFontMetrics fm(normalFont);
        const QFontMetrics boldFm(boldFont);
        const int lineHeight = boldFm.height();
        const int textFlags = Qt::AlignVCenter | Qt::AlignHCenter;

        const QString projectName = defaultAction()->property("heading").toString();
        if (!projectName.isNull())
            centerRect.adjust(0, lineHeight + 4, 0, 0);

        centerRect.adjust(0, 0, 0, -lineHeight * 2 - 4);

        iconRect.moveCenter(centerRect.center());
        StyleHelper::drawIconWithShadow(icon(), iconRect, &painter, iconMode);
        painter.setFont(normalFont);

        QPoint textOffset = centerRect.center()
                            - QPoint(iconRect.width() / 2, iconRect.height() / 2);
        textOffset = textOffset - QPoint(0, lineHeight + 3);
        const QRectF r(0, textOffset.y(), rect().width(), lineHeight);
        painter.setPen(creatorTheme()->color(isEnabled() ? Theme::PanelTextColorLight
                                                         : Theme::IconsDisabledColor));

        // draw project name
        const int margin = 6;
        const qreal availableWidth = r.width() - margin;
        const QString ellidedProjectName = fm.elidedText(projectName,
                                                         Qt::ElideMiddle,
                                                         int(availableWidth));
        painter.drawText(r, textFlags, ellidedProjectName);

        // draw build configuration name
        textOffset = iconRect.center() + QPoint(iconRect.width() / 2, iconRect.height() / 2);
        QRectF buildConfigRect[2];
        buildConfigRect[0] = QRectF(0, textOffset.y() + 4, rect().width(), lineHeight);
        buildConfigRect[1] = QRectF(0, textOffset.y() + 4 + lineHeight, rect().width(), lineHeight);
        painter.setFont(boldFont);
        QVector<QString> splitBuildConfiguration(2);
        const QString buildConfiguration = defaultAction()->property("subtitle").toString();
        //if (boldFm.horizontalAdvance(buildConfiguration) <= availableWidth)
            // text fits in one line
            splitBuildConfiguration[0] = buildConfiguration;
        //else
            //splitBuildConfiguration = splitInTwoLines(buildConfiguration, boldFm, availableWidth);

        // draw the two text lines for the build configuration
        painter.setPen(
            creatorTheme()->color(isEnabled()
                                      // Intentionally using the "Unselected" colors,
                                      // because the text color won't change in the pressed
                                      // state as they would do on the mode buttons.
                                      ? Theme::FancyTabWidgetEnabledUnselectedTextColor
                                      : Theme::FancyTabWidgetDisabledUnselectedTextColor));

        for (int i = 0; i < 2; ++i) {
            const QString &buildConfigText = splitBuildConfiguration[i];
            if (buildConfigText.isEmpty())
                continue;
            painter.drawText(buildConfigRect[i], textFlags, buildConfigText);
        }

    } else {
        iconRect.moveCenter(rect().center());
        StyleHelper::drawIconWithShadow(icon(), iconRect, &painter, iconMode);
    }

    // pop up arrow next to icon
    if (isTitledAction && isEnabled() && !icon().isNull()) {
        QStyleOption opt;
        opt.initFrom(this);
        opt.rect = rect().adjusted(rect().width() -
                                  (m_iconsOnly ? 6 : 16), 0, -(m_iconsOnly ? 0 : 8), 0);
        StyleHelper::drawArrow(QStyle::PE_IndicatorArrowRight, &painter, &opt);
    }
}

测试结果:
在这里插入图片描述
由此可见单单一个小按钮组件也关联到很多类,后续的系列将更加关注基础的类。先测试好基础类,然后搭建上层的类。
HostOsInfo类
Theme类相关内容请看:Qt Creator源码分析系列——UI界面:Theme类

发布了134 篇原创文章 · 获赞 141 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/asmartkiller/article/details/104332280