2637 lines
80 KiB
C++
2637 lines
80 KiB
C++
#include "SARibbonBar.h"
|
||
#include <QPointer>
|
||
#include <QAction>
|
||
#include <QApplication>
|
||
#include <QDebug>
|
||
#include <QHoverEvent>
|
||
#include <QLinearGradient>
|
||
#include <QPainter>
|
||
#include <QResizeEvent>
|
||
#include <QSet>
|
||
#include <QStyleOptionMenuItem>
|
||
#include <QTimer>
|
||
#include <QVariant>
|
||
#include "SARibbonButtonGroupWidget.h"
|
||
#include "SARibbonElementManager.h"
|
||
#include "SARibbonQuickAccessBar.h"
|
||
#include "SARibbonStackedWidget.h"
|
||
#include "SARibbonTabBar.h"
|
||
#include "SARibbonApplicationButton.h"
|
||
|
||
#define HELP_DRAW_RECT(p, rect) \
|
||
do { \
|
||
p.save(); \
|
||
QPen _pen(Qt::DashDotDotLine); \
|
||
_pen.setColor(QColor(219, 26, 59)); \
|
||
p.setPen(_pen); \
|
||
p.setBrush(QBrush()); \
|
||
p.drawRect(rect); \
|
||
p.restore(); \
|
||
} while (0)
|
||
|
||
class _SAContextCategoryManagerData
|
||
{
|
||
public:
|
||
SARibbonContextCategory* contextCategory;
|
||
QList< int > tabPageIndex;
|
||
bool operator==(const SARibbonContextCategory* contextPage)
|
||
{
|
||
return (this->contextCategory == contextPage);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @todo 此处要修改,此方式容易异常
|
||
*/
|
||
class _SARibbonTabData
|
||
{
|
||
public:
|
||
SARibbonCategory* category;
|
||
int index;
|
||
_SARibbonTabData() : category(nullptr), index(-1)
|
||
{
|
||
}
|
||
};
|
||
Q_DECLARE_METATYPE(_SARibbonTabData)
|
||
|
||
class SARibbonBar::PrivateData
|
||
{
|
||
SA_RIBBON_DECLARE_PUBLIC(SARibbonBar)
|
||
public:
|
||
QPointer< QAbstractButton > mApplicationButton;
|
||
QPointer< SARibbonTabBar > mRibbonTabBar;
|
||
QPointer< SARibbonStackedWidget > mStackedContainerWidget;
|
||
QPointer< SARibbonButtonGroupWidget > mRightButtonGroup; ///< 在tab bar右边的按钮群
|
||
QPointer< SARibbonQuickAccessBar > mQuickAccessBar; ///< 快速响应栏
|
||
QAction* mMinimumCategoryButtonAction { nullptr }; ///< 隐藏面板按钮action
|
||
QList< _SAContextCategoryManagerData > mCurrentShowingContextCategory;
|
||
QList< SARibbonContextCategory* > mContextCategoryList; ///< 存放所有的上下文标签
|
||
QList< _SARibbonTabData > mHidedCategory;
|
||
int mIconRightBorderPosition { 1 }; ///< 标题栏x值得最小值,在有图标和快捷启动按钮,此值都需要变化
|
||
SARibbonBar::RibbonStyles mRibbonStyle { SARibbonBar::RibbonStyleLooseThreeRow }; ///< ribbon的风格
|
||
SARibbonBar::RibbonMode mCurrentRibbonMode { SARibbonBar::NormalRibbonMode }; ///< 记录当前模式
|
||
QSize mWindowButtonSize; ///< 由SARibbonMainWindow告诉的windowbutton的尺寸
|
||
QList< QColor > mContextCategoryColorList; ///< contextCategory的色系
|
||
int mContextCategoryColorListIndex { -1 }; ///< 记录contextCategory色系索引
|
||
QColor mTitleTextColor; ///< 标题文字颜色,默认无效,无效的情况下和SARibbonBar的qss:color属性一致
|
||
QColor mTabBarBaseLineColor { QColor(186, 201, 219) }; ///< tabbar 底部会绘制一条线条,定义线条颜色
|
||
QColor mContextCategoryTitleTextColor { Qt::black }; ///< 记录Context category的标题字体颜色
|
||
Qt::Alignment mTitleAligment { Qt::AlignCenter }; ///< 标题对齐方式
|
||
bool mIsTitleVisible { true }; ///< 标题是否显示
|
||
SARibbonAlignment mRibbonAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式
|
||
SARibbonPannel::PannelLayoutMode mDefaulePannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< 默认的PannelLayoutMode
|
||
bool mEnableShowPannelTitle { true }; ///< 是否运行pannel的标题栏显示
|
||
bool mIsTabOnTitle { false }; ///< 是否tab在标题栏上
|
||
int mTitleBarHeight { 30 }; ///< 标题栏高度
|
||
int mTabBarHeight { 28 }; ///< tabbar高度
|
||
int mPannelTitleHeight { 15 }; ///< pannel的标题栏默认高度
|
||
int mCategoryHeight { 60 }; ///< Category的高度
|
||
int mPannelSpacing { 0 }; ///< pannel的spacing
|
||
QSize mPannelToolButtonSize { 22, 22 }; ///< 记录pannel的默认图标大小
|
||
std::unique_ptr< int > mUserDefTitleBarHeight; ///< 用户定义的标题栏高度,正常不使用用户设定的高度,而是使用自动计算的高度
|
||
std::unique_ptr< int > mUserDefTabBarHeight; ///< 用户定义的tabbar高度,正常不使用用户设定的高度,而是使用自动计算的高度
|
||
std::unique_ptr< int > mUserDefCategoryHeight; ///< 用户定义的Category的高度,正常不使用用户设定的高度,而是使用自动计算的高度
|
||
public:
|
||
PrivateData(SARibbonBar* par) : q_ptr(par)
|
||
{
|
||
mContextCategoryColorList = SARibbonBar::defaultContextCategoryColorList();
|
||
}
|
||
void init();
|
||
int systemTabBarHeight() const;
|
||
// 计算tabbar高度
|
||
int calcTabBarHeight();
|
||
// 根据字体信息计算标题栏高度
|
||
int calcTitleBarHeight();
|
||
// 根据字体信息计算category的高度
|
||
int calcCategoryHeight();
|
||
// 计算tabbar高度
|
||
static int calcMainBarHeight(int tabHegith, int titleHeight, int categoryHeight, bool tabOnTitle, SARibbonBar::RibbonMode rMode);
|
||
// 获取当前最小模式下的高度
|
||
int getCurrentMinimumModeMainBarHeight() const;
|
||
// 获取当前正常模式下的高度
|
||
int getCurrentNormalModeMainBarHeight() const;
|
||
// 重置尺寸
|
||
void resetSize();
|
||
// 获取标题栏高度
|
||
int titleBarHeight() const;
|
||
// tabbar 高度
|
||
int tabBarHeigth() const;
|
||
// category高度
|
||
int categoryHeight() const;
|
||
// 更新推荐的尺寸值
|
||
void updateHintSize();
|
||
|
||
void setApplicationButton(QAbstractButton* btn);
|
||
|
||
bool isContainContextCategoryInList(SARibbonContextCategory* contextCategory);
|
||
|
||
void setMinimumMode();
|
||
|
||
void setNormalMode();
|
||
|
||
QColor getContextCategoryColor();
|
||
|
||
void updateTabData();
|
||
|
||
/**
|
||
* @brief 通过输入高度计算iconSize
|
||
* @param h
|
||
* @return
|
||
*/
|
||
static QSize calcIconSizeByHeight(int h);
|
||
};
|
||
|
||
void SARibbonBar::PrivateData::init()
|
||
{
|
||
mApplicationButton = RibbonSubElementFactory->createRibbonApplicationButton(q_ptr);
|
||
q_ptr->connect(mApplicationButton.data(), &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked);
|
||
mRibbonTabBar = RibbonSubElementFactory->createRibbonTabBar(q_ptr);
|
||
mRibbonTabBar->setObjectName(QStringLiteral("objSARibbonTabBar"));
|
||
mRibbonTabBar->setDrawBase(false);
|
||
q_ptr->connect(mRibbonTabBar.data(), &QTabBar::currentChanged, q_ptr, &SARibbonBar::onCurrentRibbonTabChanged);
|
||
q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabClicked);
|
||
q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabBarDoubleClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabDoubleClicked);
|
||
q_ptr->connect(mRibbonTabBar.data(), &QTabBar::tabMoved, q_ptr, &SARibbonBar::onTabMoved);
|
||
//
|
||
mStackedContainerWidget = RibbonSubElementFactory->createRibbonStackedWidget(q_ptr);
|
||
mStackedContainerWidget->setObjectName(QStringLiteral("objSARibbonStackedContainerWidget"));
|
||
q_ptr->connect(mStackedContainerWidget.data(), &SARibbonStackedWidget::hidWindow, q_ptr, &SARibbonBar::onStackWidgetHided);
|
||
// 捕获事件,在popmode时必须用到
|
||
mStackedContainerWidget->installEventFilter(q_ptr);
|
||
//
|
||
mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(q_ptr);
|
||
mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar"));
|
||
mQuickAccessBar->setIcon(q_ptr->windowIcon());
|
||
//
|
||
mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(q_ptr);
|
||
//
|
||
setNormalMode();
|
||
}
|
||
|
||
int SARibbonBar::PrivateData::systemTabBarHeight() const
|
||
{
|
||
return q_ptr->style()->pixelMetric(QStyle::PM_TabBarBaseHeight)
|
||
+ q_ptr->style()->pixelMetric(QStyle::PM_TabBarTabHSpace)
|
||
+ q_ptr->style()->pixelMetric(QStyle::PM_TabBarTabOverlap);
|
||
}
|
||
|
||
/**
|
||
* @brief 估算tabbar的高度
|
||
* @param fm
|
||
* @return
|
||
*/
|
||
int SARibbonBar::PrivateData::calcTabBarHeight()
|
||
{
|
||
int defaultHeight = systemTabBarHeight();
|
||
int fontHeight = q_ptr->fontMetrics().lineSpacing(); // 不要用height,像宋体这种字体,height=12,lineSpacing=14,有些就无法显示
|
||
int defaultHeight2 = fontHeight * 1.6;
|
||
if (defaultHeight2 < fontHeight + 10) {
|
||
defaultHeight2 = fontHeight + 10; // 主要为了满足office2021主题,tab下有个4px的横杠
|
||
}
|
||
int r = qMax(defaultHeight, defaultHeight2);
|
||
if (r < 20) {
|
||
r = 20;
|
||
}
|
||
return r;
|
||
}
|
||
|
||
/**
|
||
* @brief 估算标题栏的高度
|
||
* @param fm
|
||
* @return
|
||
*/
|
||
int SARibbonBar::PrivateData::calcTitleBarHeight()
|
||
{
|
||
int defaultHeight = q_ptr->style()->pixelMetric(QStyle::PM_TitleBarHeight);
|
||
int defaultHeight2 = q_ptr->fontMetrics().height() * 1.8;
|
||
int r = qMax(defaultHeight, defaultHeight2);
|
||
if (r < 25) {
|
||
r = 25;
|
||
}
|
||
return r;
|
||
}
|
||
|
||
/**
|
||
* @brief 估算category的高度
|
||
* @note 经过对照,1.6行高和office的高度比较接近
|
||
* @param fm
|
||
* @param s
|
||
* @return
|
||
*/
|
||
int SARibbonBar::PrivateData::calcCategoryHeight()
|
||
{
|
||
int textH = q_ptr->fontMetrics().lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行
|
||
if (SARibbonPannel::ThreeRowMode == mDefaulePannelLayoutMode) {
|
||
// 5.5=(3*1.6+1) (三行),1是给panneltitle预留的
|
||
return textH * 4.8 + mPannelTitleHeight;
|
||
} else {
|
||
// 3=2*1.6
|
||
return textH * 3.2 + mPannelTitleHeight;
|
||
}
|
||
return (textH * 4.8 + mPannelTitleHeight);
|
||
}
|
||
|
||
/**
|
||
* @brief 计算总高
|
||
*
|
||
* @note 此总高是普通模式下的总高
|
||
*
|
||
* @param tabHegith
|
||
* @param titleHeight
|
||
* @param categoryHeight
|
||
* @return
|
||
*/
|
||
int SARibbonBar::PrivateData::calcMainBarHeight(int tabHegith,
|
||
int titleHeight,
|
||
int categoryHeight,
|
||
bool tabOnTitle,
|
||
SARibbonBar::RibbonMode rMode)
|
||
{
|
||
if (rMode == MinimumRibbonMode) {
|
||
// 最小模式,没有categoryHeight
|
||
if (tabOnTitle) {
|
||
return titleHeight;
|
||
} else {
|
||
return titleHeight + tabHegith;
|
||
}
|
||
} else {
|
||
if (tabOnTitle) {
|
||
return titleHeight + categoryHeight;
|
||
} else {
|
||
return tabHegith + titleHeight + categoryHeight;
|
||
}
|
||
}
|
||
return tabHegith + titleHeight + categoryHeight;
|
||
}
|
||
|
||
int SARibbonBar::PrivateData::getCurrentMinimumModeMainBarHeight() const
|
||
{
|
||
return calcMainBarHeight(tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::MinimumRibbonMode);
|
||
}
|
||
|
||
int SARibbonBar::PrivateData::getCurrentNormalModeMainBarHeight() const
|
||
{
|
||
return calcMainBarHeight(tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::NormalRibbonMode);
|
||
}
|
||
|
||
/**
|
||
* @brief 重新计算尺寸
|
||
*/
|
||
void SARibbonBar::PrivateData::resetSize()
|
||
{
|
||
updateHintSize();
|
||
const int th = tabBarHeigth();
|
||
int mainBarHeight = calcMainBarHeight(th, titleBarHeight(), categoryHeight(), mIsTabOnTitle, mCurrentRibbonMode);
|
||
// 处于最小模式下时,bar的高度为tabbar的bottom,这个调整必须在resize event之后
|
||
q_ptr->setFixedHeight(mainBarHeight);
|
||
}
|
||
|
||
/**
|
||
* @brief 标题栏高度
|
||
* @return
|
||
*/
|
||
int SARibbonBar::PrivateData::titleBarHeight() const
|
||
{
|
||
if (mUserDefTitleBarHeight) {
|
||
return *mUserDefTitleBarHeight;
|
||
}
|
||
return mTitleBarHeight;
|
||
}
|
||
|
||
/**
|
||
* @brief tab高度
|
||
* @return
|
||
*/
|
||
int SARibbonBar::PrivateData::tabBarHeigth() const
|
||
{
|
||
if (mUserDefTabBarHeight) {
|
||
return *mUserDefTabBarHeight;
|
||
}
|
||
return mTabBarHeight;
|
||
}
|
||
|
||
/**
|
||
* @brief category高度
|
||
* @return
|
||
*/
|
||
int SARibbonBar::PrivateData::categoryHeight() const
|
||
{
|
||
if (mUserDefCategoryHeight) {
|
||
return *mUserDefCategoryHeight;
|
||
} else {
|
||
return mCategoryHeight;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 更新推荐的尺寸值
|
||
*/
|
||
void SARibbonBar::PrivateData::updateHintSize()
|
||
{
|
||
mTitleBarHeight = calcTitleBarHeight();
|
||
// mTabBarHeight有大于0的值说明用户设置了,就使用用户设置的值
|
||
mTabBarHeight = calcTabBarHeight();
|
||
mCategoryHeight = calcCategoryHeight();
|
||
}
|
||
|
||
void SARibbonBar::PrivateData::setApplicationButton(QAbstractButton* btn)
|
||
{
|
||
if (mApplicationButton) {
|
||
mApplicationButton->hide();
|
||
mApplicationButton->deleteLater();
|
||
}
|
||
if (btn) {
|
||
if (btn->parent() != q_ptr) {
|
||
btn->setParent(q_ptr);
|
||
}
|
||
btn->move(0, q_ptr->titleBarHeight());
|
||
btn->show();
|
||
q_ptr->connect(btn, &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked);
|
||
}
|
||
mApplicationButton = btn;
|
||
}
|
||
|
||
bool SARibbonBar::PrivateData::isContainContextCategoryInList(SARibbonContextCategory* contextCategory)
|
||
{
|
||
for (int i = 0; i < mCurrentShowingContextCategory.size(); ++i) {
|
||
if (mCurrentShowingContextCategory[ i ] == contextCategory) {
|
||
return (true);
|
||
}
|
||
}
|
||
return (false);
|
||
}
|
||
|
||
void SARibbonBar::PrivateData::setMinimumMode()
|
||
{
|
||
mCurrentRibbonMode = SARibbonBar::MinimumRibbonMode;
|
||
mStackedContainerWidget->setPopupMode();
|
||
mStackedContainerWidget->setFocusPolicy(Qt::NoFocus);
|
||
mStackedContainerWidget->clearFocus();
|
||
mRibbonTabBar->setFocus();
|
||
mStackedContainerWidget->hide();
|
||
resetSize();
|
||
}
|
||
|
||
void SARibbonBar::PrivateData::setNormalMode()
|
||
{
|
||
mCurrentRibbonMode = SARibbonBar::NormalRibbonMode;
|
||
mStackedContainerWidget->setNormalMode();
|
||
mStackedContainerWidget->setFocus();
|
||
mStackedContainerWidget->show();
|
||
resetSize();
|
||
}
|
||
|
||
QColor SARibbonBar::PrivateData::getContextCategoryColor()
|
||
{
|
||
if (mContextCategoryColorList.isEmpty()) {
|
||
mContextCategoryColorListIndex = -1;
|
||
return (QColor());
|
||
}
|
||
++mContextCategoryColorListIndex;
|
||
if ((mContextCategoryColorListIndex >= mContextCategoryColorList.size()) || (mContextCategoryColorListIndex < 0)) {
|
||
mContextCategoryColorListIndex = 0;
|
||
}
|
||
return (mContextCategoryColorList.at(mContextCategoryColorListIndex));
|
||
}
|
||
|
||
void SARibbonBar::PrivateData::updateTabData()
|
||
{
|
||
int tabcount = mRibbonTabBar->count();
|
||
|
||
for (int i = 0; i < tabcount; ++i) {
|
||
QVariant var = mRibbonTabBar->tabData(i);
|
||
if (var.isValid()) {
|
||
_SARibbonTabData p = var.value< _SARibbonTabData >();
|
||
p.index = i;
|
||
mRibbonTabBar->setTabData(i, QVariant::fromValue(p));
|
||
}
|
||
}
|
||
// 刷新完tabdata信息也要接着刷新ContextCategory信息
|
||
for (_SAContextCategoryManagerData& cd : mCurrentShowingContextCategory) {
|
||
cd.tabPageIndex.clear();
|
||
for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) {
|
||
SARibbonCategory* category = cd.contextCategory->categoryPage(i);
|
||
for (int t = 0; t < tabcount; ++t) {
|
||
QVariant v = mRibbonTabBar->tabData(t);
|
||
if (v.isValid()) {
|
||
_SARibbonTabData d = v.value< _SARibbonTabData >();
|
||
if (d.category == category) {
|
||
cd.tabPageIndex.append(t);
|
||
}
|
||
} else {
|
||
cd.tabPageIndex.append(-1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
QSize SARibbonBar::PrivateData::calcIconSizeByHeight(int h)
|
||
{
|
||
if (h - 8 >= 20) {
|
||
return QSize(h - 8, h - 8);
|
||
}
|
||
return QSize(h - 4, h - 4);
|
||
}
|
||
|
||
//===================================================
|
||
// SARibbonBar
|
||
//===================================================
|
||
|
||
/**
|
||
* @brief SARibbonBar构造函数
|
||
* @param parent
|
||
*/
|
||
SARibbonBar::SARibbonBar(QWidget* parent) : QMenuBar(parent), d_ptr(new SARibbonBar::PrivateData(this))
|
||
{
|
||
d_ptr->init();
|
||
ensurePolished();
|
||
setNativeMenuBar(false);
|
||
// #ifdef Q_OS_MACOS
|
||
// setNativeMenuBar(false);
|
||
// #endif
|
||
// #ifdef Q_OS_LINUX
|
||
// setNativeMenuBar(false);
|
||
// #endif
|
||
if (parent) {
|
||
connect(parent, &QWidget::windowTitleChanged, this, &SARibbonBar::onWindowTitleChanged);
|
||
connect(parent, &QWidget::windowIconChanged, this, &SARibbonBar::onWindowIconChanged);
|
||
}
|
||
setRibbonStyle(RibbonStyleLooseThreeRow);
|
||
}
|
||
|
||
SARibbonBar::~SARibbonBar()
|
||
{
|
||
}
|
||
|
||
/**
|
||
* @brief 判断样式是否为2行
|
||
* @param s
|
||
* @return 2行返回true,返回false代表当前是3行
|
||
*/
|
||
bool SARibbonBar::isTwoRowStyle(SARibbonBar::RibbonStyles s)
|
||
{
|
||
return s.testFlag(SARibbonBar::RibbonStyleTwoRow);
|
||
}
|
||
|
||
/**
|
||
* @brief 判断样式是否为3行
|
||
* @param s
|
||
* @return 3行返回true,返回false代表当前是2行
|
||
*/
|
||
bool SARibbonBar::isThreeRowStyle(RibbonStyles s)
|
||
{
|
||
return s.testFlag(SARibbonBar::RibbonStyleThreeRow);
|
||
}
|
||
|
||
/**
|
||
* @brief 判断是否是宽松样式
|
||
* @param s
|
||
* @return 宽松样式(带标题栏)返回true,否则就是紧凑样式
|
||
*/
|
||
bool SARibbonBar::isLooseStyle(SARibbonBar::RibbonStyles s)
|
||
{
|
||
return s.testFlag(SARibbonBar::RibbonStyleLoose);
|
||
}
|
||
|
||
/**
|
||
* @brief 判断是否是紧凑样式
|
||
* @param s
|
||
* @return 紧凑样式(不带标题栏)返回true,否则就是宽松样式
|
||
*/
|
||
bool SARibbonBar::isCompactStyle(RibbonStyles s)
|
||
{
|
||
return s.testFlag(SARibbonBar::RibbonStyleCompact);
|
||
}
|
||
|
||
/**
|
||
* @brief 获取版本信息
|
||
* @return {SA_RIBBON_BAR_VERSION_MAJ}.{SA_RIBBON_BAR_VERSION_MIN}.{SA_RIBBON_BAR_VERSION_PAT}
|
||
*/
|
||
QString SARibbonBar::versionString()
|
||
{
|
||
return QString("%1.%2.%3").arg(SA_RIBBON_BAR_VERSION_MAJ).arg(SA_RIBBON_BAR_VERSION_MIN).arg(SA_RIBBON_BAR_VERSION_PAT);
|
||
}
|
||
|
||
/**
|
||
@brief 获取默认的上下文标签颜色列表
|
||
@return
|
||
*/
|
||
QList< QColor > SARibbonBar::defaultContextCategoryColorList()
|
||
{
|
||
QList< QColor > res;
|
||
res //
|
||
<< QColor(206, 232, 252) // 蓝
|
||
<< QColor(253, 238, 179) // 黄
|
||
<< QColor(212, 255, 174) // 绿
|
||
<< QColor(255, 196, 214) // 红
|
||
<< QColor(255, 216, 153) // 橙
|
||
<< QColor(255, 224, 243) // 玫红
|
||
;
|
||
return res;
|
||
}
|
||
|
||
/**
|
||
* @brief 提供高分屏的支持静态函数
|
||
*
|
||
* @note 此函数需要在main函数,QApplication生成之前调用
|
||
* @code
|
||
* int main(int argc, char* argv[]){
|
||
* SARibbonBar::initHighDpi();
|
||
* QApplication a(argc, argv);
|
||
* ...
|
||
* }
|
||
* @endcode
|
||
*/
|
||
void SARibbonBar::initHighDpi()
|
||
{
|
||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||
#endif
|
||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||
// 在有些时候,无法显示分割线,是由于PassThrough导致的
|
||
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief 返回applicationButton
|
||
* @return 默认的applicationButton是@ref SARibbonApplicationButton 生成,通过@ref setApplicationButton 可设置为其他button
|
||
*/
|
||
QAbstractButton* SARibbonBar::applicationButton()
|
||
{
|
||
return (d_ptr->mApplicationButton);
|
||
}
|
||
|
||
/**
|
||
* @brief 设置applicationButton,如果想隐藏,可以传入nullptr
|
||
*
|
||
* 默认会有一个SARibbonApplicationButton,如果想取消,可传入nullptr,或者自定义的button也可以传入
|
||
*
|
||
* @note applicationButton的所有权归SARibbonBar所有,不要在外部对applicationButton进行delete操作
|
||
* @param btn applicationButton指针,可以传入@ref SARibbonApplicationButton,
|
||
* SA已经对SARibbonApplicationButton进行了样式设置
|
||
*/
|
||
void SARibbonBar::setApplicationButton(QAbstractButton* btn)
|
||
{
|
||
d_ptr->setApplicationButton(btn);
|
||
if (btn) {
|
||
if (btn->objectName().isEmpty()) {
|
||
btn->setObjectName(QStringLiteral("SARibbonApplicationButton"));
|
||
}
|
||
btn->setVisible(true);
|
||
// btn->setGeometry(applicationButtonGeometry());
|
||
}
|
||
// 无论设置为什么都触发resize
|
||
QApplication::postEvent(this, new QResizeEvent(size(), size()));
|
||
}
|
||
|
||
/**
|
||
* @brief 返回tabbar
|
||
* @return SARibbonTabBar指针
|
||
* @sa SARibbonTabBar
|
||
*/
|
||
SARibbonTabBar* SARibbonBar::ribbonTabBar()
|
||
{
|
||
return (d_ptr->mRibbonTabBar);
|
||
}
|
||
|
||
/**
|
||
* @brief 添加一个标签
|
||
* 如果需要删除,直接delete即可,SARibbonBar会对其进行处理
|
||
* @param title 标签名字,默认情况下SARibbonCategory的object name也被设置为title
|
||
* @return 返回一个窗口容器,在Category里可以添加其他控件
|
||
* @sa SARibbonCategory
|
||
*/
|
||
SARibbonCategory* SARibbonBar::addCategoryPage(const QString& title)
|
||
{
|
||
SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this);
|
||
|
||
category->setObjectName(title);
|
||
category->setCategoryName(title);
|
||
addCategoryPage(category);
|
||
return (category);
|
||
}
|
||
|
||
/**
|
||
* @brief 添加一个标签
|
||
* @param category
|
||
*/
|
||
void SARibbonBar::addCategoryPage(SARibbonCategory* category)
|
||
{
|
||
if (nullptr == category) {
|
||
return;
|
||
}
|
||
int index = d_ptr->mRibbonTabBar->count();
|
||
insertCategoryPage(category, index);
|
||
}
|
||
|
||
/**
|
||
* @brief qtdesigner专用
|
||
* @param category
|
||
*/
|
||
void SARibbonBar::addCategoryPage(QWidget* category)
|
||
{
|
||
SARibbonCategory* c = qobject_cast< SARibbonCategory* >(category);
|
||
|
||
if (c) {
|
||
addCategoryPage(c);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 添加一个category,category的位置在index,如果当前category数量少于index,将插入到最后
|
||
* @param title category的标题
|
||
* @param index category的位置
|
||
* @return
|
||
*/
|
||
SARibbonCategory* SARibbonBar::insertCategoryPage(const QString& title, int index)
|
||
{
|
||
SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this);
|
||
|
||
category->setObjectName(title);
|
||
category->setCategoryName(title);
|
||
insertCategoryPage(category, index);
|
||
return (category);
|
||
}
|
||
|
||
/**
|
||
* @brief 插入一个category
|
||
* @param category SARibbonCategory指针
|
||
* @param index 插入的位置,如果超出范围,将默认插入到最后
|
||
*/
|
||
void SARibbonBar::insertCategoryPage(SARibbonCategory* category, int index)
|
||
{
|
||
if (nullptr == category) {
|
||
return;
|
||
}
|
||
category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode);
|
||
category->setPannelSpacing(d_ptr->mPannelSpacing);
|
||
category->setPannelToolButtonIconSize(d_ptr->mPannelToolButtonSize);
|
||
int i = d_ptr->mRibbonTabBar->insertTab(index, category->categoryName());
|
||
|
||
_SARibbonTabData tabdata;
|
||
|
||
tabdata.category = category;
|
||
tabdata.index = i;
|
||
d_ptr->mRibbonTabBar->setTabData(i, QVariant::fromValue(tabdata));
|
||
d_ptr->mStackedContainerWidget->insertWidget(index, category);
|
||
// 更新index信息
|
||
d_ptr->updateTabData();
|
||
QApplication::postEvent(this, new QResizeEvent(size(), size()));
|
||
|
||
connect(category, &QWidget::windowTitleChanged, this, &SARibbonBar::onCategoryWindowTitleChanged);
|
||
connect(category, &SARibbonCategory::actionTriggered, this, &SARibbonBar::actionTriggered);
|
||
}
|
||
|
||
/**
|
||
* @brief 通过名字查找Category
|
||
* @param title Category的名字,既标签的标题
|
||
* @return 如果没有找到,将返回nullptr,如果有重名,将返回第一个查询到的名字,因此,尽量避免重名标签
|
||
* @note 由于翻译等原因,可能title会变化,因此如果想通过固定内容查找category,应该使用 @ref categoryByObjectName
|
||
* @see categoryByObjectName
|
||
*/
|
||
SARibbonCategory* SARibbonBar::categoryByName(const QString& title) const
|
||
{
|
||
int c = d_ptr->mStackedContainerWidget->count();
|
||
|
||
for (int i = 0; i < c; ++i) {
|
||
SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i));
|
||
if (w) {
|
||
if (w->categoryName() == title) {
|
||
return (w);
|
||
}
|
||
}
|
||
}
|
||
return (nullptr);
|
||
}
|
||
|
||
/**
|
||
* @brief 通过ObjectName查找Category
|
||
* @param objname
|
||
* @return 如果没有找到,将返回nullptr,如果有同样的ObjectName,将返回第一个查询到的名字,因此,尽量避免ObjectName重名
|
||
* @see categoryByName
|
||
*/
|
||
SARibbonCategory* SARibbonBar::categoryByObjectName(const QString& objname) const
|
||
{
|
||
int c = d_ptr->mStackedContainerWidget->count();
|
||
|
||
for (int i = 0; i < c; ++i) {
|
||
SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i));
|
||
if (w) {
|
||
if (w->objectName() == objname) {
|
||
return (w);
|
||
}
|
||
}
|
||
}
|
||
return (nullptr);
|
||
}
|
||
|
||
/**
|
||
* @brief 通过索引找到category,如果超过索引范围,会返回nullptr
|
||
* @param index 索引
|
||
* @return 如果超过索引范围,会返回nullptr
|
||
* @note 如果此时有上下文标签,上下文的标签也会返回
|
||
* @note 通过索引查找的category必须是visible状态的category,如果通过@ref hideCategory 隐藏的标签,通过索引是找不到的
|
||
* @note 通过@ref categoryByObjectName 可以找到所有加入过的标签,
|
||
* 如果想得到ribbonbar管理的所有标签,可以通过函数@ref categoryPages 得到
|
||
* @see categoryIndex categoryByObjectName categoryByName
|
||
*/
|
||
SARibbonCategory* SARibbonBar::categoryByIndex(int index) const
|
||
{
|
||
QVariant var = d_ptr->mRibbonTabBar->tabData(index);
|
||
|
||
if (var.isValid()) {
|
||
_SARibbonTabData p = var.value< _SARibbonTabData >();
|
||
return (p.category);
|
||
}
|
||
return (nullptr);
|
||
}
|
||
|
||
/**
|
||
* @brief 隐藏category,并不会删除或者取走,只是隐藏
|
||
* @param category
|
||
*/
|
||
void SARibbonBar::hideCategory(SARibbonCategory* category)
|
||
{
|
||
int tabcount = d_ptr->mRibbonTabBar->count();
|
||
|
||
for (int i = 0; i < tabcount; ++i) {
|
||
QVariant var = d_ptr->mRibbonTabBar->tabData(i);
|
||
if (var.isValid()) {
|
||
_SARibbonTabData p = var.value< _SARibbonTabData >();
|
||
if (p.category == category) {
|
||
d_ptr->mHidedCategory.append(p);
|
||
d_ptr->mRibbonTabBar->removeTab(i); // 仅仅把tab移除
|
||
// 注意Category隐藏后,contex的位置就会发生变化,需要更新
|
||
d_ptr->updateTabData();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 显示被隐藏的category
|
||
* @param category
|
||
*/
|
||
void SARibbonBar::showCategory(SARibbonCategory* category)
|
||
{
|
||
for (auto i = d_ptr->mHidedCategory.begin(); i != d_ptr->mHidedCategory.end(); ++i) {
|
||
if (i->category == category) {
|
||
// 说明要显示
|
||
int index = d_ptr->mRibbonTabBar->insertTab(i->index, i->category->categoryName());
|
||
i->index = index;
|
||
d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(*i));
|
||
d_ptr->mHidedCategory.erase(i); // 移除
|
||
// 更新index信息
|
||
d_ptr->updateTabData();
|
||
raiseCategory(category);
|
||
return;
|
||
}
|
||
}
|
||
raiseCategory(category);
|
||
}
|
||
|
||
/**
|
||
* @brief 判断这个category是否在显示状态,也就是tabbar有这个category
|
||
* @param category
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isCategoryVisible(const SARibbonCategory* c) const
|
||
{
|
||
int tabindex = categoryIndex(c);
|
||
|
||
return (tabindex >= 0);
|
||
}
|
||
|
||
/**
|
||
* @brief 获取category的索引
|
||
* @param c
|
||
* @return 如果找不到,返回-1
|
||
*/
|
||
int SARibbonBar::categoryIndex(const SARibbonCategory* c) const
|
||
{
|
||
// category的顺序不能以stackedwidget为准,因为存在contextcategory,contextcategory正常是不显示的
|
||
int tabcount = d_ptr->mRibbonTabBar->count();
|
||
|
||
for (int i = 0; i < tabcount; ++i) {
|
||
QVariant var = d_ptr->mRibbonTabBar->tabData(i);
|
||
if (var.isValid()) {
|
||
_SARibbonTabData p = var.value< _SARibbonTabData >();
|
||
if (p.category == c) {
|
||
return (i);
|
||
}
|
||
}
|
||
}
|
||
return (-1);
|
||
}
|
||
|
||
/**
|
||
* @brief 移动一个Category从from index到to index
|
||
* @param from
|
||
* @param to
|
||
*/
|
||
void SARibbonBar::moveCategory(int from, int to)
|
||
{
|
||
d_ptr->mRibbonTabBar->moveTab(from, to);
|
||
// 这时要刷新所有tabdata的index信息
|
||
d_ptr->updateTabData();
|
||
// 这里会触发tabMoved信号,在tabMoved信号中调整stacked里窗口的位置
|
||
}
|
||
|
||
/**
|
||
* @brief 获取当前显示的所有的SARibbonCategory,包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回
|
||
*
|
||
* @return
|
||
*/
|
||
QList< SARibbonCategory* > SARibbonBar::categoryPages(bool getAll) const
|
||
{
|
||
int c = d_ptr->mStackedContainerWidget->count();
|
||
QList< SARibbonCategory* > res;
|
||
|
||
for (int i = 0; i < c; ++i) {
|
||
SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i));
|
||
if (w) {
|
||
if (!getAll && w->isContextCategory()) {
|
||
// 不是getall且是上下文时跳过
|
||
continue;
|
||
}
|
||
res.append(w);
|
||
}
|
||
}
|
||
return (res);
|
||
}
|
||
|
||
/**
|
||
* @brief 移除SARibbonCategory
|
||
*
|
||
* SARibbonBar不会delete SARibbonCategory*,但这个SARibbonCategory会脱离SARibbonBar的管理
|
||
* 表现在tabbar会移除,面板会移除,使用此函数后可以对SARibbonCategory进行delete
|
||
* @param category
|
||
*/
|
||
void SARibbonBar::removeCategory(SARibbonCategory* category)
|
||
{
|
||
int index = tabIndex(category);
|
||
bool isupdate = false;
|
||
if (index >= 0) {
|
||
d_ptr->mRibbonTabBar->removeTab(index);
|
||
isupdate = true;
|
||
}
|
||
d_ptr->mStackedContainerWidget->removeWidget(category);
|
||
// 同时验证这个category是否是contexcategory里的
|
||
|
||
for (SARibbonContextCategory* c : qAsConst(d_ptr->mContextCategoryList)) {
|
||
c->takeCategory(category);
|
||
}
|
||
// 这时要刷新所有tabdata的index信息
|
||
if (isupdate) {
|
||
d_ptr->updateTabData();
|
||
}
|
||
// 移除完后需要重绘
|
||
repaint();
|
||
QApplication::postEvent(this, new QResizeEvent(size(), size()));
|
||
}
|
||
|
||
/**
|
||
* @brief 添加上下文标签
|
||
*
|
||
* 上下文标签是特殊时候触发的标签,需要用户手动触发
|
||
*
|
||
* 调用@ref SARibbonContextCategory::addCategoryPage 可在上下文标签中添加SARibbonCategory,
|
||
* 在上下文标签添加的SARibbonCategory,只有在上下文标签显示的时候才会显示
|
||
* @param title 上下文标签的标题,在Office模式下会显示,在wps模式下不显示。默认情况下SARibbonContextCategory的object name也被设置为title
|
||
* @param color 上下文标签的颜色,如果指定为空QColor(),将会使用SARibbonBar的默认色系
|
||
* @param id 上下文标签的id,以便进行查找
|
||
* @return 返回上下文标签指针
|
||
* @note SARibbonBar拥有SARibbonContextCategory的管理权,用户避免在外部直接delete,如果要删除,调用@ref destroyContextCategory 函数
|
||
*/
|
||
SARibbonContextCategory* SARibbonBar::addContextCategory(const QString& title, const QColor& color, const QVariant& id)
|
||
{
|
||
SARibbonContextCategory* context = RibbonSubElementFactory->createRibbonContextCategory(this);
|
||
|
||
context->setObjectName(title);
|
||
context->setContextTitle(title);
|
||
context->setId(id);
|
||
context->setContextColor(color.isValid() ? color : d_ptr->getContextCategoryColor());
|
||
addContextCategory(context);
|
||
return (context);
|
||
}
|
||
|
||
/**
|
||
* @brief 添加上下文标签
|
||
* @param context
|
||
*/
|
||
void SARibbonBar::addContextCategory(SARibbonContextCategory* context)
|
||
{
|
||
if (nullptr == context) {
|
||
return;
|
||
}
|
||
connect(context, &SARibbonContextCategory::categoryPageAdded, this, &SARibbonBar::onContextsCategoryPageAdded);
|
||
connect(context, &SARibbonContextCategory::categoryTitleChanged, this, &SARibbonBar::onContextsCategoryCategoryNameChanged);
|
||
// remove并没有绑定,主要是remove后在stacked里也不会显示,remove且delete的话,stacked里也会删除
|
||
d_ptr->mContextCategoryList.append(context);
|
||
}
|
||
|
||
/**
|
||
* @brief 显示上下文标签
|
||
* @param context 上下文标签指针
|
||
*/
|
||
void SARibbonBar::showContextCategory(SARibbonContextCategory* context)
|
||
{
|
||
if (isContextCategoryVisible(context)) {
|
||
return;
|
||
}
|
||
_SAContextCategoryManagerData contextCategoryData;
|
||
|
||
contextCategoryData.contextCategory = context;
|
||
for (int i = 0; i < context->categoryCount(); ++i) {
|
||
SARibbonCategory* category = context->categoryPage(i);
|
||
// 此句如果模式重复设置不会进行多余操作
|
||
category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode);
|
||
// 切换模式后会改变高度,上下文标签显示时要保证显示出来
|
||
int index = d_ptr->mRibbonTabBar->addTab(category->categoryName());
|
||
contextCategoryData.tabPageIndex.append(index);
|
||
|
||
_SARibbonTabData tabdata;
|
||
tabdata.category = category;
|
||
tabdata.index = index;
|
||
d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(tabdata));
|
||
}
|
||
d_ptr->mCurrentShowingContextCategory.append(contextCategoryData);
|
||
// 由于上下文都是在最后追加,不需要调用updateTabData();
|
||
QApplication::postEvent(this, new QResizeEvent(size(), size()));
|
||
}
|
||
|
||
/**
|
||
* @brief 隐藏上下文标签
|
||
* @param context 上下文标签指针
|
||
*/
|
||
void SARibbonBar::hideContextCategory(SARibbonContextCategory* context)
|
||
{
|
||
bool needResize = false;
|
||
|
||
for (int i = 0; i < d_ptr->mCurrentShowingContextCategory.size(); ++i) {
|
||
if (d_ptr->mCurrentShowingContextCategory[ i ].contextCategory == context) {
|
||
const QList< int >& indexs = d_ptr->mCurrentShowingContextCategory[ i ].tabPageIndex;
|
||
for (int j = indexs.size() - 1; j >= 0; --j) {
|
||
d_ptr->mRibbonTabBar->removeTab(indexs[ j ]);
|
||
}
|
||
// 注意,再删除ContextCategory后,tab的序号就会改变,这时,这个tab后面的都要调整它的序号
|
||
needResize = true;
|
||
d_ptr->mCurrentShowingContextCategory.removeAt(i);
|
||
// 移除了ContextCategory后需要break
|
||
break;
|
||
}
|
||
}
|
||
if (needResize) {
|
||
d_ptr->updateTabData();
|
||
QApplication::postEvent(this, new QResizeEvent(size(), size()));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 判断上下文是否在显示状态
|
||
* @param context
|
||
* @return 在显示状态返回true
|
||
* @sa setContextCategoryVisible
|
||
*/
|
||
bool SARibbonBar::isContextCategoryVisible(SARibbonContextCategory* context)
|
||
{
|
||
return (d_ptr->isContainContextCategoryInList(context));
|
||
}
|
||
|
||
/**
|
||
* @brief 设置上下文标签的显示状态
|
||
*
|
||
* 上下文标签的当前显示状态可通过 @ref isContextCategoryVisible 进行判断
|
||
* @param context 上下文标签
|
||
* @param visible 显示状态,true为显示
|
||
*/
|
||
void SARibbonBar::setContextCategoryVisible(SARibbonContextCategory* context, bool visible)
|
||
{
|
||
if (nullptr == context) {
|
||
return;
|
||
}
|
||
if (visible) {
|
||
showContextCategory(context);
|
||
} else {
|
||
hideContextCategory(context);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 获取所有的上下文标签
|
||
* @return 返回上下文标签列表
|
||
*/
|
||
QList< SARibbonContextCategory* > SARibbonBar::contextCategoryList() const
|
||
{
|
||
return (d_ptr->mContextCategoryList);
|
||
}
|
||
|
||
/**
|
||
* @brief 销毁上下文标签,上下文标签的SARibbonCategory也会随之销毁
|
||
* @param context 需要销毁的上下文标签指针
|
||
*/
|
||
void SARibbonBar::destroyContextCategory(SARibbonContextCategory* context)
|
||
{
|
||
if (nullptr == context) {
|
||
return;
|
||
}
|
||
//! 1、如果上下文标签显示中,先隐藏
|
||
if (isContextCategoryVisible(context)) {
|
||
hideContextCategory(context);
|
||
}
|
||
//! 2、删除上下文标签的相关内容
|
||
d_ptr->mContextCategoryList.removeAll(context);
|
||
//!
|
||
QList< SARibbonCategory* > res = context->categoryList();
|
||
|
||
for (SARibbonCategory* c : qAsConst(res)) {
|
||
c->hide();
|
||
c->deleteLater();
|
||
}
|
||
context->deleteLater();
|
||
QApplication::postEvent(this, new QResizeEvent(size(), size()));
|
||
}
|
||
|
||
/**
|
||
* @brief 设置为最小/正常模式
|
||
*
|
||
* 隐藏模式下,只会显示tabbar,不会显示内容,默认状态是显示模式
|
||
*
|
||
* 默认下双击tabbar会切换隐藏显示模式,如果想禁用此功能,可以重载 @ref onCurrentRibbonTabDoubleClicked
|
||
* 函数,不对函数进行任何处理即可
|
||
*
|
||
* @param isMinimum 参数为true时,切换为Minimum模式
|
||
* @see 此函数会改变@ref RibbonState 状态,通过@ref currentRibbonState 函数可以查看当前状态
|
||
*/
|
||
void SARibbonBar::setMinimumMode(bool isMinimum)
|
||
{
|
||
#ifdef SA_RIBBON_DEBUG_HELP_DRAW
|
||
qDebug() << "SARibbonBar::setHideMode " << isMinimum;
|
||
#endif
|
||
if (isMinimum) {
|
||
d_ptr->setMinimumMode();
|
||
} else {
|
||
d_ptr->setNormalMode();
|
||
}
|
||
QResizeEvent resizeEvent(size(), size());
|
||
QApplication::sendEvent(this, &resizeEvent);
|
||
// 发射信号
|
||
Q_EMIT ribbonModeChanged(isMinimum ? MinimumRibbonMode : NormalRibbonMode);
|
||
}
|
||
|
||
///
|
||
/// \brief 当前ribbon是否是隐藏模式
|
||
/// \return
|
||
///
|
||
bool SARibbonBar::isMinimumMode() const
|
||
{
|
||
return (d_ptr->mStackedContainerWidget->isPopupMode());
|
||
}
|
||
|
||
///
|
||
/// \brief 设置显示隐藏ribbon按钮
|
||
///
|
||
void SARibbonBar::showMinimumModeButton(bool isShow)
|
||
{
|
||
if (isShow) {
|
||
activeRightButtonGroup();
|
||
|
||
d_ptr->mMinimumCategoryButtonAction = new QAction(this);
|
||
d_ptr->mMinimumCategoryButtonAction->setIcon(
|
||
style()->standardIcon(isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton,
|
||
nullptr));
|
||
connect(d_ptr->mMinimumCategoryButtonAction, &QAction::triggered, this, [ this ]() {
|
||
this->setMinimumMode(!isMinimumMode());
|
||
this->d_ptr->mMinimumCategoryButtonAction->setIcon(
|
||
style()->standardIcon(isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton,
|
||
nullptr));
|
||
});
|
||
d_ptr->mRightButtonGroup->addAction(d_ptr->mMinimumCategoryButtonAction);
|
||
|
||
} else {
|
||
if (nullptr != d_ptr->mMinimumCategoryButtonAction) {
|
||
d_ptr->mMinimumCategoryButtonAction->deleteLater();
|
||
d_ptr->mMinimumCategoryButtonAction = nullptr;
|
||
}
|
||
}
|
||
QResizeEvent resizeEvent(size(), size());
|
||
|
||
QApplication::sendEvent(this, &resizeEvent);
|
||
}
|
||
|
||
/**
|
||
* @brief 是否显示隐藏ribbon按钮
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::haveShowMinimumModeButton() const
|
||
{
|
||
return (nullptr != d_ptr->mMinimumCategoryButtonAction);
|
||
}
|
||
|
||
/**
|
||
@brief 隐藏ribbon对应的action
|
||
@return
|
||
*/
|
||
QAction* SARibbonBar::minimumModeAction() const
|
||
{
|
||
return d_ptr->mMinimumCategoryButtonAction;
|
||
}
|
||
|
||
/**
|
||
* @brief 当前ribbon的状态(正常|最小化)
|
||
* @return
|
||
*/
|
||
SARibbonBar::RibbonMode SARibbonBar::currentRibbonState() const
|
||
{
|
||
return (d_ptr->mCurrentRibbonMode);
|
||
}
|
||
|
||
/**
|
||
@brief tabBar的高度
|
||
@return
|
||
*/
|
||
int SARibbonBar::tabBarHeight() const
|
||
{
|
||
return d_ptr->tabBarHeigth();
|
||
// return d_ptr->mRibbonTabBar->height();
|
||
}
|
||
|
||
/**
|
||
* @brief 设置tabbar的高度
|
||
*
|
||
* 用户调用setTabBarHeight后,将使用用户设定的高度,而不使用自动计算的高度,这时tabbar高度不会跟随字体等信息重新计算
|
||
*
|
||
* @note 注意,在RibbonStyleCompact**模式下,tabbar高度要保证小于等于titlebar高度,否则会显示异常
|
||
* @note 此函数不会自动刷新,如果需要刷新调用此函数后需要调用@ref updateRibbonGeometry
|
||
* @param h
|
||
*/
|
||
void SARibbonBar::setTabBarHeight(int h, bool resizeByNow)
|
||
{
|
||
if (nullptr == d_ptr->mUserDefTabBarHeight) {
|
||
d_ptr->mUserDefTabBarHeight = std::make_unique< int >(h);
|
||
} else {
|
||
*(d_ptr->mUserDefTabBarHeight) = h;
|
||
}
|
||
if (resizeByNow) {
|
||
updateRibbonGeometry();
|
||
}
|
||
}
|
||
|
||
/**
|
||
@brief 返回标题栏高度
|
||
@sa setTitleBarHeight
|
||
@return
|
||
*/
|
||
int SARibbonBar::titleBarHeight() const
|
||
{
|
||
return d_ptr->titleBarHeight();
|
||
}
|
||
|
||
/**
|
||
@brief 设置标题栏的高度
|
||
@sa titleBarHeight
|
||
@note 此操作会发射@ref titleBarHeightChanged 信号
|
||
@param h
|
||
*/
|
||
void SARibbonBar::setTitleBarHeight(int h, bool resizeByNow)
|
||
{
|
||
int oldHeight = d_ptr->mTitleBarHeight;
|
||
if (nullptr == d_ptr->mUserDefTitleBarHeight) {
|
||
d_ptr->mUserDefTitleBarHeight = std::make_unique< int >(h);
|
||
} else {
|
||
*(d_ptr->mUserDefTitleBarHeight) = h;
|
||
//
|
||
oldHeight = *(d_ptr->mUserDefTitleBarHeight);
|
||
}
|
||
|
||
if (resizeByNow) {
|
||
updateRibbonGeometry();
|
||
}
|
||
Q_EMIT titleBarHeightChanged(oldHeight, h);
|
||
}
|
||
|
||
/**
|
||
* @brief category的高度
|
||
* @return
|
||
*/
|
||
int SARibbonBar::categoryHeight() const
|
||
{
|
||
return d_ptr->mStackedContainerWidget->height();
|
||
}
|
||
|
||
/**
|
||
* @brief 设置category的高度
|
||
* @param h
|
||
* @param resizeByNow
|
||
*/
|
||
void SARibbonBar::setCategoryHeight(int h, bool resizeByNow)
|
||
{
|
||
if (nullptr == d_ptr->mUserDefCategoryHeight) {
|
||
d_ptr->mUserDefCategoryHeight = std::make_unique< int >(h);
|
||
} else {
|
||
*(d_ptr->mUserDefCategoryHeight) = h;
|
||
}
|
||
resizeStackedContainerWidget();
|
||
if (resizeByNow) {
|
||
updateRibbonGeometry();
|
||
}
|
||
}
|
||
|
||
void SARibbonBar::onWindowTitleChanged(const QString& title)
|
||
{
|
||
Q_UNUSED(title);
|
||
update();
|
||
}
|
||
|
||
void SARibbonBar::onWindowIconChanged(const QIcon& i)
|
||
{
|
||
if (quickAccessBar()) {
|
||
quickAccessBar()->setIcon(i);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief category的名字发生改变触发
|
||
* @param title
|
||
*/
|
||
void SARibbonBar::onCategoryWindowTitleChanged(const QString& title)
|
||
{
|
||
// 全部更新一遍
|
||
Q_UNUSED(title);
|
||
updateCategoryTitleToTabName();
|
||
}
|
||
|
||
///
|
||
/// \brief ribbon的显示界面隐藏
|
||
///
|
||
void SARibbonBar::onStackWidgetHided()
|
||
{
|
||
}
|
||
|
||
/**
|
||
* @brief 标签切换触发槽函数
|
||
* @param index
|
||
*/
|
||
void SARibbonBar::onCurrentRibbonTabChanged(int index)
|
||
{
|
||
QVariant var = d_ptr->mRibbonTabBar->tabData(index);
|
||
SARibbonCategory* category = nullptr;
|
||
|
||
if (var.isValid()) {
|
||
_SARibbonTabData p = var.value< _SARibbonTabData >();
|
||
category = p.category;
|
||
}
|
||
if (!category) {
|
||
return;
|
||
}
|
||
if (d_ptr->mStackedContainerWidget->currentWidget() != category) {
|
||
d_ptr->mStackedContainerWidget->setCurrentWidget(category);
|
||
}
|
||
Q_EMIT currentRibbonTabChanged(index);
|
||
if (isMinimumMode()) {
|
||
d_ptr->mRibbonTabBar->clearFocus();
|
||
if (!d_ptr->mStackedContainerWidget->isVisible()) {
|
||
if (d_ptr->mStackedContainerWidget->isPopupMode()) {
|
||
// 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开
|
||
QHoverEvent ehl(QEvent::HoverLeave,
|
||
d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()),
|
||
d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()));
|
||
QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl);
|
||
resizeStackedContainerWidget();
|
||
d_ptr->mStackedContainerWidget->setFocus();
|
||
// exec之前先发射信息号,否则会被exec阻塞
|
||
d_ptr->mStackedContainerWidget->show();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief ribbon tab bar单击
|
||
*
|
||
* 此实现必须在eventfilter中传递stackedwidget的QEvent::MouseButtonDblClick事件到tabbar中,否则会导致双击变两次单击
|
||
*
|
||
* 单击事件仅用于响应点击同一个tab时
|
||
* @param index
|
||
*/
|
||
void SARibbonBar::onCurrentRibbonTabClicked(int index)
|
||
{
|
||
if (index != d_ptr->mRibbonTabBar->currentIndex()) {
|
||
// 点击的标签不一致通过changed槽去处理
|
||
return;
|
||
}
|
||
if (this->isMinimumMode()) {
|
||
if (!this->d_ptr->mStackedContainerWidget->isVisible()) {
|
||
if (this->d_ptr->mStackedContainerWidget->isPopupMode()) {
|
||
// 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开
|
||
QHoverEvent ehl(QEvent::HoverLeave,
|
||
d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()),
|
||
d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()));
|
||
QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl);
|
||
// 弹出前都调整一下位置,避免移动后位置异常
|
||
resizeStackedContainerWidget();
|
||
this->d_ptr->mStackedContainerWidget->setFocus();
|
||
this->d_ptr->mStackedContainerWidget->exec();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief ribbon tab bar双击
|
||
*
|
||
* 默认情况下双击会切换最小和正常模式
|
||
* @param index
|
||
*/
|
||
void SARibbonBar::onCurrentRibbonTabDoubleClicked(int index)
|
||
{
|
||
Q_UNUSED(index);
|
||
setMinimumMode(!isMinimumMode());
|
||
}
|
||
|
||
void SARibbonBar::onContextsCategoryPageAdded(SARibbonCategory* category)
|
||
{
|
||
Q_ASSERT_X(category != nullptr, "onContextsCategoryPageAdded", "add nullptr page");
|
||
d_ptr->mStackedContainerWidget->addWidget(category); // 这里stackedWidget用append,其他地方都应该使用insert
|
||
}
|
||
|
||
/**
|
||
* @brief 上下文标签管理的标签的名字发生变换
|
||
* @param category
|
||
* @param title
|
||
*/
|
||
void SARibbonBar::onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title)
|
||
{
|
||
Q_UNUSED(category);
|
||
Q_UNUSED(title);
|
||
updateCategoryTitleToTabName();
|
||
}
|
||
|
||
/**
|
||
* @brief 标签移动的信号
|
||
* @param from
|
||
* @param to
|
||
*/
|
||
void SARibbonBar::onTabMoved(int from, int to)
|
||
{
|
||
const QSignalBlocker blocker(d_ptr->mStackedContainerWidget);
|
||
// 调整stacked widget的顺序,调整顺序是为了调用categoryPages函数返回的QList<SARibbonCategory *>顺序和tabbar一致
|
||
d_ptr->mStackedContainerWidget->moveWidget(from, to);
|
||
}
|
||
|
||
/**
|
||
* @brief 根据SARibbonCategory*指针查找tabbar的index
|
||
*
|
||
* @param c SARibbonCategory对应的QObject指针
|
||
* @return 如果没有找到,返回-1
|
||
* @note 此函数不会调用SARibbonCategory*的任何方法,因此可以在SARibbonCategory的destroyed槽中调用
|
||
*/
|
||
int SARibbonBar::tabIndex(SARibbonCategory* obj)
|
||
{
|
||
const int size = d_ptr->mRibbonTabBar->count();
|
||
|
||
for (int i = 0; i < size; ++i) {
|
||
QVariant v = d_ptr->mRibbonTabBar->tabData(i);
|
||
if (v.isValid()) {
|
||
_SARibbonTabData innervalue = v.value< _SARibbonTabData >();
|
||
if (innervalue.category == obj) {
|
||
return (i);
|
||
}
|
||
}
|
||
}
|
||
// 如果找不到就从stackedwidget中找
|
||
|
||
return (-1);
|
||
}
|
||
|
||
void SARibbonBar::resizeAll()
|
||
{
|
||
if (isLooseStyle()) {
|
||
resizeInLooseStyle();
|
||
} else {
|
||
resizeInCompactStyle();
|
||
}
|
||
update();
|
||
}
|
||
|
||
/**
|
||
* @brief 把ribbonbar的内容,同步进各个category中
|
||
* @param autoUpdate
|
||
*/
|
||
void SARibbonBar::synchronousCategoryData(bool autoUpdate)
|
||
{
|
||
iterate([ this ](SARibbonCategory* c) -> bool {
|
||
c->setEnableShowPannelTitle(this->isEnableShowPannelTitle());
|
||
c->setPannelTitleHeight(this->pannelTitleHeight());
|
||
c->setCategoryAlignment(this->ribbonAlignment());
|
||
c->setPannelLayoutMode(this->pannelLayoutMode());
|
||
return true;
|
||
});
|
||
|
||
//! 直接给一个resizeevent,让所有刷新
|
||
if (autoUpdate) {
|
||
QResizeEvent* e = new QResizeEvent(size(), QSize());
|
||
QApplication::postEvent(this, e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
@brief 激活tabbar右边的按钮群
|
||
@return 返回右边的按钮群指针
|
||
*/
|
||
SARibbonButtonGroupWidget* SARibbonBar::activeRightButtonGroup()
|
||
{
|
||
if (nullptr == d_ptr->mRightButtonGroup) {
|
||
d_ptr->mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(this);
|
||
}
|
||
d_ptr->mRightButtonGroup->show();
|
||
return d_ptr->mRightButtonGroup;
|
||
}
|
||
|
||
/**
|
||
@brief 返回右边的按钮群指针
|
||
@return 如果没有创建,返回nullptr
|
||
*/
|
||
SARibbonButtonGroupWidget* SARibbonBar::rightButtonGroup()
|
||
{
|
||
return d_ptr->mRightButtonGroup;
|
||
}
|
||
|
||
/**
|
||
@brief 激活QuickAccessBar
|
||
@return
|
||
*/
|
||
SARibbonQuickAccessBar* SARibbonBar::activeQuickAccessBar()
|
||
{
|
||
if (nullptr == d_ptr->mQuickAccessBar) {
|
||
d_ptr->mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(this);
|
||
d_ptr->mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar"));
|
||
d_ptr->mQuickAccessBar->setIcon(windowIcon());
|
||
}
|
||
d_ptr->mQuickAccessBar->show();
|
||
return d_ptr->mQuickAccessBar;
|
||
}
|
||
|
||
SARibbonQuickAccessBar* SARibbonBar::quickAccessBar()
|
||
{
|
||
return (d_ptr->mQuickAccessBar);
|
||
}
|
||
|
||
/**
|
||
* @brief 设置ribbonbar的风格,此函数会重新设置所有元素,包括button的布局方式,
|
||
* 尤其是从三行变到两行的过程,重设的内容较多
|
||
* @note 此函数会自动触发ResizeEvent,不需要手动调用
|
||
* @note 默认情况下2行模式不换行,如果想在两行模式换行,在调用SARibbonBar::setRibbonStyle后,再SARibbonToolButton::setEnableWordWrap(true)
|
||
* ,同时再调用updateRibbonElementGeometry()刷新布局
|
||
* @param v 样式,见@ref SARibbonBar::RibbonStyle
|
||
*/
|
||
void SARibbonBar::setRibbonStyle(SARibbonBar::RibbonStyles v)
|
||
{
|
||
// 先幅值给变量
|
||
d_ptr->mRibbonStyle = v;
|
||
#if SA_DEBUG_PRINT_SIZE_HINT
|
||
qDebug() << "setRibbonStyle(" << v << ")" //
|
||
<< "\n isThreeRowStyle=" << isThreeRowStyle() //
|
||
<< "\n isTwoRowStyle=" << isTwoRowStyle() //
|
||
<< "\n isLooseStyle=" << isLooseStyle() //
|
||
<< "\n isCompactStyle=" << isCompactStyle() //
|
||
;
|
||
#endif
|
||
// 执行判断
|
||
setEnableWordWrap(isThreeRowStyle(v));
|
||
setTabOnTitle(isCompactStyle());
|
||
d_ptr->mQuickAccessBar->setEnableShowIcon(isLooseStyle());
|
||
setEnableShowPannelTitle(isThreeRowStyle(v));
|
||
setPannelLayoutMode(isThreeRowStyle(v) ? SARibbonPannel::ThreeRowMode : SARibbonPannel::TwoRowMode);
|
||
|
||
// 此函数会调用setFixedHeight
|
||
synchronousCategoryData(false); // 这里不急着刷新,下面会继续刷新
|
||
d_ptr->resetSize();
|
||
|
||
Q_EMIT ribbonStyleChanged(d_ptr->mRibbonStyle);
|
||
}
|
||
|
||
/**
|
||
@brief 返回当前ribbon的风格
|
||
@return 返回当前ribbon的风格
|
||
*/
|
||
SARibbonBar::RibbonStyles SARibbonBar::currentRibbonStyle() const
|
||
{
|
||
return (d_ptr->mRibbonStyle);
|
||
}
|
||
|
||
/**
|
||
@brief 切换到对应标签
|
||
@param index 标签索引
|
||
*/
|
||
void SARibbonBar::setCurrentIndex(int index)
|
||
{
|
||
d_ptr->mRibbonTabBar->setCurrentIndex(index);
|
||
// onCurrentRibbonTabChanged(index);
|
||
}
|
||
|
||
/**
|
||
* @brief 返回当前的tab索引
|
||
* @return 当前的索引
|
||
*/
|
||
int SARibbonBar::currentIndex()
|
||
{
|
||
return (d_ptr->mRibbonTabBar->currentIndex());
|
||
}
|
||
|
||
/**
|
||
* @brief 确保标签显示出来,tab并切换到对应页
|
||
* @param category 标签指针
|
||
*/
|
||
void SARibbonBar::raiseCategory(SARibbonCategory* category)
|
||
{
|
||
int index = d_ptr->mStackedContainerWidget->indexOf(category);
|
||
|
||
if (index >= 0) {
|
||
setCurrentIndex(index);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 判断当前的样式是否为2行
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isTwoRowStyle() const
|
||
{
|
||
return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::TwoRowMode);
|
||
}
|
||
|
||
/**
|
||
* @brief 判断当前的样式是否为3行
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isThreeRowStyle() const
|
||
{
|
||
return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::ThreeRowMode);
|
||
}
|
||
|
||
/**
|
||
* @brief 判断当前的样式是否为宽松样式
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isLooseStyle() const
|
||
{
|
||
return (SARibbonBar::isLooseStyle(currentRibbonStyle()));
|
||
}
|
||
|
||
/**
|
||
* @brief 判断当前的样式是否为紧凑样式
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isCompactStyle() const
|
||
{
|
||
return (SARibbonBar::isCompactStyle(currentRibbonStyle()));
|
||
}
|
||
|
||
/**
|
||
* @brief 设置标题的文字颜色
|
||
*
|
||
* 标题时mainwindow的windowTitle,如果要设置标题,直接调用SARibbonMainWindow::setWindowTitle 进行设置
|
||
*
|
||
* 如果不设置标题颜色,默认是SARibbonBar的qss的color属性
|
||
* @param clr
|
||
* @note 此函数不会刷新,刷新请自行调用repaint
|
||
*/
|
||
void SARibbonBar::setWindowTitleTextColor(const QColor& clr)
|
||
{
|
||
d_ptr->mTitleTextColor = clr;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取标题的文字颜色
|
||
* @return 如果返回的是无效颜色,!QColor::isValid(),说明没有手动设置颜色,颜色将跟随SARibbonBar的qss的文字颜色
|
||
*/
|
||
QColor SARibbonBar::windowTitleTextColor() const
|
||
{
|
||
return d_ptr->mTitleTextColor;
|
||
}
|
||
|
||
/**
|
||
* @brief tabbar 底部会绘制一条线条,此接口定义线条颜色
|
||
* @param clr
|
||
*/
|
||
void SARibbonBar::setTabBarBaseLineColor(const QColor& clr)
|
||
{
|
||
d_ptr->mTabBarBaseLineColor = clr;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取tabbar底部基线颜色
|
||
* @return
|
||
*/
|
||
QColor SARibbonBar::tabBarBaseLineColor() const
|
||
{
|
||
return d_ptr->mTabBarBaseLineColor;
|
||
}
|
||
|
||
/**
|
||
* @brief 更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局
|
||
*
|
||
* @note 此函数调用较慢,避免在高速要求场合调用
|
||
*/
|
||
void SARibbonBar::updateRibbonGeometry()
|
||
{
|
||
d_ptr->resetSize();
|
||
iterate([](SARibbonCategory* c) -> bool {
|
||
c->updateItemGeometry();
|
||
return true;
|
||
});
|
||
// !主题变更后,tabbar的长度需要进行刷新
|
||
|
||
//! 直接给一个resizeevent,让所有刷新
|
||
QResizeEvent* e = new QResizeEvent(size(), QSize());
|
||
QApplication::postEvent(this, e);
|
||
}
|
||
|
||
/**
|
||
* @brief SARibbonPannel的布局模式
|
||
* @return
|
||
*/
|
||
SARibbonPannel::PannelLayoutMode SARibbonBar::pannelLayoutMode() const
|
||
{
|
||
return d_ptr->mDefaulePannelLayoutMode;
|
||
}
|
||
|
||
/**
|
||
* @brief SARibbonPannel的布局模式设置
|
||
* @param m
|
||
*/
|
||
void SARibbonBar::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m)
|
||
{
|
||
d_ptr->mDefaulePannelLayoutMode = m;
|
||
iterate([ m ](SARibbonCategory* c) -> bool {
|
||
c->setPannelLayoutMode(m);
|
||
return true;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* @brief 设置tab在title上面,这样可以省略title区域
|
||
* @param on
|
||
*/
|
||
void SARibbonBar::setTabOnTitle(bool on)
|
||
{
|
||
if (d_ptr->mIsTabOnTitle != on) {
|
||
d_ptr->mIsTabOnTitle = on;
|
||
d_ptr->resetSize();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief tab是否在title上面
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isTabOnTitle() const
|
||
{
|
||
return d_ptr->mIsTabOnTitle;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置标题的对齐方式
|
||
* @param al
|
||
*/
|
||
void SARibbonBar::setWindowTitleAligment(Qt::Alignment al)
|
||
{
|
||
d_ptr->mTitleAligment = al;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取标题的对齐方式
|
||
* @return
|
||
*/
|
||
Qt::Alignment SARibbonBar::windowTitleAligment() const
|
||
{
|
||
return d_ptr->mTitleAligment;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置ribbonbar的按钮文字允许换行
|
||
* @param on
|
||
*/
|
||
void SARibbonBar::setEnableWordWrap(bool on)
|
||
{
|
||
SARibbonToolButton::setEnableWordWrap(on);
|
||
updateRibbonGeometry();
|
||
}
|
||
|
||
/**
|
||
* @brief 判断是否允许换行
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isEnableWordWrap() const
|
||
{
|
||
return SARibbonToolButton::isEnableWordWrap();
|
||
}
|
||
|
||
/**
|
||
* @brief pannel标题栏的高度
|
||
* @return
|
||
*/
|
||
int SARibbonBar::pannelTitleHeight() const
|
||
{
|
||
return d_ptr->mPannelTitleHeight;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置pannel的高度
|
||
* @param h
|
||
*/
|
||
void SARibbonBar::setPannelTitleHeight(int h)
|
||
{
|
||
d_ptr->mPannelTitleHeight = h;
|
||
iterate([ h ](SARibbonCategory* c) -> bool {
|
||
c->setPannelTitleHeight(h);
|
||
return true;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* @brief 是否pannel显示标题栏
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isEnableShowPannelTitle() const
|
||
{
|
||
return d_ptr->mEnableShowPannelTitle;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置显示pannel标题
|
||
* @param on
|
||
*/
|
||
void SARibbonBar::setEnableShowPannelTitle(bool on)
|
||
{
|
||
d_ptr->mEnableShowPannelTitle = on;
|
||
iterate([ on ](SARibbonCategory* c) -> bool {
|
||
c->setEnableShowPannelTitle(on);
|
||
return true;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* @brief 设置pannel的spacing
|
||
* @param n
|
||
*/
|
||
void SARibbonBar::setPannelSpacing(int n)
|
||
{
|
||
d_ptr->mPannelSpacing = n;
|
||
// 同步当前被SARibbonBar管理的SARibbonCategory的PannelSpacing
|
||
iterate([ n ](SARibbonCategory* category) -> bool {
|
||
if (category) {
|
||
category->setPannelSpacing(n);
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* @brief pannel的spacing
|
||
* @return
|
||
*/
|
||
int SARibbonBar::pannelSpacing() const
|
||
{
|
||
return d_ptr->mPannelSpacing;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置pannel按钮的icon尺寸,large action不受此尺寸影响
|
||
* @param s
|
||
*/
|
||
void SARibbonBar::setPannelToolButtonIconSize(const QSize& s)
|
||
{
|
||
d_ptr->mPannelToolButtonSize = s;
|
||
// 同步当前被SARibbonBar管理的SARibbonCategory的PannelSpacing
|
||
iterate([ s ](SARibbonCategory* category) -> bool {
|
||
if (category) {
|
||
category->setPannelToolButtonIconSize(s);
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* @brief pannel按钮的icon尺寸,large action不受此尺寸影响
|
||
* @return
|
||
*/
|
||
QSize SARibbonBar::pannelToolButtonIconSize() const
|
||
{
|
||
return d_ptr->mPannelToolButtonSize;
|
||
}
|
||
|
||
/**
|
||
* @brief ribbonbar内部的StackedWidget
|
||
* 所有的category都放置在StackedWidget中
|
||
* @return
|
||
*/
|
||
SARibbonStackedWidget* SARibbonBar::ribbonStackedWidget()
|
||
{
|
||
return d_ptr->mStackedContainerWidget;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置是否显示标题
|
||
* @param on
|
||
*/
|
||
void SARibbonBar::setTitleVisible(bool on)
|
||
{
|
||
d_ptr->mIsTitleVisible = on;
|
||
}
|
||
|
||
/**
|
||
* @brief 判断标题是否显示
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::isTitleVisible() const
|
||
{
|
||
return d_ptr->mIsTitleVisible;
|
||
}
|
||
|
||
/**
|
||
@brief 设置上下文标签的颜色列表
|
||
|
||
上下文标签显示的时候,会从颜色列表中取颜色进行标签的渲染
|
||
@param cls
|
||
*/
|
||
void SARibbonBar::setContextCategoryColorList(const QList< QColor >& cls)
|
||
{
|
||
d_ptr->mContextCategoryColorList = cls;
|
||
if (d_ptr->mContextCategoryColorList.isEmpty()) {
|
||
d_ptr->mContextCategoryColorList = defaultContextCategoryColorList();
|
||
}
|
||
d_ptr->mContextCategoryColorListIndex = 0;
|
||
// 这时需要对已经显示的contextCategoryData的颜色进行重新设置
|
||
for (SARibbonContextCategory* c : d_ptr->mContextCategoryList) {
|
||
c->setContextColor(d_ptr->getContextCategoryColor());
|
||
}
|
||
}
|
||
|
||
/**
|
||
@brief 获取上下文标签的颜色列表
|
||
@return
|
||
*/
|
||
QList< QColor > SARibbonBar::contextCategoryColorList() const
|
||
{
|
||
return d_ptr->mContextCategoryColorList;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置contextCategory 标题的颜色
|
||
* @param clr
|
||
*/
|
||
void SARibbonBar::setContextCategoryTitleTextColor(const QColor& clr)
|
||
{
|
||
d_ptr->mContextCategoryTitleTextColor = clr;
|
||
}
|
||
|
||
/**
|
||
* @brief contextCategory 标题的颜色
|
||
* @return
|
||
*/
|
||
QColor SARibbonBar::contextCategoryTitleTextColor() const
|
||
{
|
||
return d_ptr->mContextCategoryTitleTextColor;
|
||
}
|
||
|
||
/**
|
||
@brief 设置ribbon的对齐方式
|
||
@param al
|
||
*/
|
||
void SARibbonBar::setRibbonAlignment(SARibbonAlignment al)
|
||
{
|
||
d_ptr->mRibbonAlignment = al;
|
||
synchronousCategoryData();
|
||
}
|
||
|
||
/**
|
||
@brief ribbon的对齐方式
|
||
@return
|
||
*/
|
||
SARibbonAlignment SARibbonBar::ribbonAlignment() const
|
||
{
|
||
return d_ptr->mRibbonAlignment;
|
||
}
|
||
|
||
/**
|
||
* @brief 此函数会遍历SARibbonBar下的所有Category,执行函数指针bool(SARibbonCategory*)
|
||
* @param fp 函数指针返回false则停止迭代
|
||
* @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代
|
||
*/
|
||
bool SARibbonBar::iterate(FpCategoryIterate fp)
|
||
{
|
||
const QList< SARibbonCategory* > cs = categoryPages(true);
|
||
for (SARibbonCategory* c : cs) {
|
||
if (!fp(c)) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief 此函数会遍历SARibbonBar下的所有Category,并迭代所有的pannel,执行函数指针bool(SARibbonPannel*)
|
||
* @param fp 函数指针返回false则停止迭代
|
||
* @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代
|
||
*/
|
||
bool SARibbonBar::iterate(FpPannelIterate fp)
|
||
{
|
||
const QList< SARibbonCategory* > cs = categoryPages(true);
|
||
for (SARibbonCategory* c : cs) {
|
||
if (!c->iterate(fp)) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置边角widget可见性,对于mdi窗口,会出现TopLeftCorner和TopRightCorner两个corner widget
|
||
* @param on
|
||
* @param c
|
||
*/
|
||
void SARibbonBar::setCornerWidgetVisible(bool on, Qt::Corner c)
|
||
{
|
||
if (QWidget* w = cornerWidget(c)) {
|
||
w->setVisible(on);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief SARibbonBar::eventFilter
|
||
* @param obj
|
||
* @param e
|
||
* @return
|
||
*/
|
||
bool SARibbonBar::eventFilter(QObject* obj, QEvent* e)
|
||
{
|
||
if (obj) {
|
||
// 调整多文档时在窗口模式下的按钮更新
|
||
if ((obj == cornerWidget(Qt::TopLeftCorner)) || (obj == cornerWidget(Qt::TopRightCorner))) {
|
||
if ((QEvent::UpdateLater == e->type()) || (QEvent::MouseButtonRelease == e->type())
|
||
|| (QEvent::WindowActivate == e->type())) {
|
||
QApplication::postEvent(this, new QResizeEvent(size(), size()));
|
||
}
|
||
} else if (obj == d_ptr->mStackedContainerWidget) {
|
||
// 在stack 是popup模式时,点击的是stackedContainerWidget区域外的时候,如果是在ribbonTabBar上点击
|
||
// 那么忽略掉这次点击,把点击下发到ribbonTabBar,这样可以避免stackedContainerWidget在点击ribbonTabBar后
|
||
// 隐藏又显示,出现闪烁
|
||
if ((QEvent::MouseButtonPress == e->type()) || (QEvent::MouseButtonDblClick == e->type())) {
|
||
if (d_ptr->mStackedContainerWidget->isPopupMode()) {
|
||
QMouseEvent* mouseEvent = static_cast< QMouseEvent* >(e);
|
||
if (!d_ptr->mStackedContainerWidget->rect().contains(mouseEvent->pos())) {
|
||
QWidget* clickedWidget = QApplication::widgetAt(mouseEvent->globalPos());
|
||
if (clickedWidget == d_ptr->mRibbonTabBar) {
|
||
const QPoint targetPoint = clickedWidget->mapFromGlobal(mouseEvent->globalPos());
|
||
QMouseEvent* evPress = new QMouseEvent(mouseEvent->type(),
|
||
targetPoint,
|
||
mouseEvent->globalPos(),
|
||
mouseEvent->button(),
|
||
mouseEvent->buttons(),
|
||
mouseEvent->modifiers());
|
||
QApplication::postEvent(clickedWidget, evPress);
|
||
return (true);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return (QMenuBar::eventFilter(obj, e));
|
||
}
|
||
|
||
/**
|
||
* @brief 根据情况重置tabbar的宽度,主要针对wps模式
|
||
*/
|
||
int SARibbonBar::calcMinTabBarWidth() const
|
||
{
|
||
// 20200831
|
||
// tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整
|
||
// 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出
|
||
// mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow
|
||
// 计算tab所占用的宽度
|
||
const QMargins& mg = d_ptr->mRibbonTabBar->tabMargin();
|
||
return d_ptr->mRibbonTabBar->sizeHint().width() + (mg.left() + mg.right());
|
||
}
|
||
|
||
/**
|
||
* @brief 正常模式下的高度
|
||
*
|
||
* 有可能SARibbonBar::height和mainBarHeight不相等,这种情况发生在RibbonState::MinimumRibbonMode状态下
|
||
* @return 高度
|
||
*/
|
||
int SARibbonBar::normalModeMainBarHeight() const
|
||
{
|
||
return d_ptr->getCurrentNormalModeMainBarHeight();
|
||
}
|
||
|
||
/**
|
||
@brief 最小模式下的高度
|
||
@return
|
||
*/
|
||
int SARibbonBar::minimumModeMainBarHeight() const
|
||
{
|
||
return d_ptr->getCurrentMinimumModeMainBarHeight();
|
||
}
|
||
|
||
/**
|
||
* @brief 更新所有的category title对应的tab名
|
||
*
|
||
* 此函数会对所有的category的名字和tab进行匹配,如果匹配不上会重新设置tab名
|
||
*/
|
||
void SARibbonBar::updateCategoryTitleToTabName()
|
||
{
|
||
SARibbonTabBar* tab = d_ptr->mRibbonTabBar;
|
||
for (int i = 0; i < tab->count(); ++i) {
|
||
// 鉴于tab不会很多,不考虑效率问题
|
||
QVariant var = tab->tabData(i);
|
||
if (var.isValid()) {
|
||
_SARibbonTabData p = var.value< _SARibbonTabData >();
|
||
if (p.category && p.category->categoryName() != tab->tabText(i)) {
|
||
tab->setTabText(i, p.category->categoryName());
|
||
}
|
||
}
|
||
}
|
||
repaint();
|
||
}
|
||
|
||
/**
|
||
* @brief 告知WindowButtonGroup的尺寸
|
||
* @param s
|
||
*/
|
||
void SARibbonBar::setWindowButtonGroupSize(const QSize& s)
|
||
{
|
||
d_ptr->mWindowButtonSize = s;
|
||
}
|
||
|
||
void SARibbonBar::paintEvent(QPaintEvent* e)
|
||
{
|
||
Q_UNUSED(e);
|
||
if (isLooseStyle()) {
|
||
paintInLooseStyle();
|
||
} else {
|
||
paintInCompactStyle();
|
||
}
|
||
#ifdef SA_RIBBON_DEBUG_HELP_DRAW
|
||
QPainter p(this);
|
||
HELP_DRAW_RECT(p, m_d->quickAccessBar->geometry());
|
||
HELP_DRAW_RECT(p, m_d->ribbonTabBar->geometry());
|
||
HELP_DRAW_RECT(p, m_d->stackedContainerWidget->geometry());
|
||
#endif
|
||
}
|
||
|
||
void SARibbonBar::paintInLooseStyle()
|
||
{
|
||
QPainter p(this);
|
||
|
||
//!绘制tabbar下的基线,这个函数仅仅对office2013主题有用,大部分主题都不绘制基线
|
||
paintTabbarBaseLine(p);
|
||
|
||
//! 显示上下文标签
|
||
p.save();
|
||
QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory;
|
||
// bool isCurrentSelectContextCategoryPage = false;
|
||
|
||
QPoint contextCategoryRegion(width(), -1);
|
||
QMargins border = contentsMargins();
|
||
|
||
for (int i = 0; i < contextCategoryDataList.size(); ++i) {
|
||
QRect contextTitleRect;
|
||
QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex;
|
||
QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor();
|
||
if (!indexs.isEmpty()) {
|
||
contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first());
|
||
QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last());
|
||
contextTitleRect.setRight(endRect.right());
|
||
contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y());
|
||
contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1); // 减1像素,避免tabbar基线覆盖
|
||
contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin();
|
||
// 把区域顶部扩展到窗口顶部
|
||
contextTitleRect.setTop(border.top());
|
||
// 绘制
|
||
paintContextCategoryTab(p, contextCategoryDataList[ i ].contextCategory->contextTitle(), contextTitleRect, clr);
|
||
// 更新上下文标签的范围,用于控制标题栏的显示
|
||
if (contextTitleRect.left() < contextCategoryRegion.x()) {
|
||
contextCategoryRegion.setX(contextTitleRect.left());
|
||
}
|
||
if (contextTitleRect.right() > contextCategoryRegion.y()) {
|
||
contextCategoryRegion.setY(contextTitleRect.right());
|
||
}
|
||
}
|
||
// isCurrentSelectContextCategoryPage = indexs.contains(d_ptr->mRibbonTabBar->currentIndex());
|
||
// if (isCurrentSelectContextCategoryPage) {
|
||
// QPen pen;
|
||
// pen.setColor(clr);
|
||
// pen.setWidth(1);
|
||
// p.setPen(pen);
|
||
// p.setBrush(Qt::NoBrush);
|
||
// p.drawRect(d_ptr->mStackedContainerWidget->geometry());
|
||
// isCurrentSelectContextCategoryPage = false;
|
||
// }
|
||
}
|
||
p.restore();
|
||
//! 显示标题等
|
||
QWidget* parWindow = parentWidget();
|
||
|
||
if (parWindow) {
|
||
QRect titleRegion;
|
||
if (contextCategoryRegion.y() < 0) {
|
||
titleRegion.setRect(d_ptr->mQuickAccessBar->geometry().right() + 1,
|
||
border.top(),
|
||
width() - d_ptr->mIconRightBorderPosition - border.right()
|
||
- d_ptr->mWindowButtonSize.width() - d_ptr->mQuickAccessBar->geometry().right() - 1,
|
||
titleBarHeight());
|
||
} else {
|
||
int leftwidth = contextCategoryRegion.x() - d_ptr->mQuickAccessBar->geometry().right()
|
||
- d_ptr->mIconRightBorderPosition;
|
||
int rightwidth = width() - contextCategoryRegion.y() - d_ptr->mWindowButtonSize.width();
|
||
// if (width() - contextCategoryRegion.y() > contextCategoryRegion.x()-x) {
|
||
if (rightwidth > leftwidth) {
|
||
// 说明右边的区域大一点,标题显示在右,显示在右边需要减去windowbutton宽度
|
||
titleRegion.setRect(contextCategoryRegion.y(), border.top(), rightwidth, titleBarHeight());
|
||
} else {
|
||
// 说明左边的大一点
|
||
titleRegion.setRect(d_ptr->mIconRightBorderPosition + d_ptr->mQuickAccessBar->geometry().right(),
|
||
border.top(),
|
||
leftwidth,
|
||
titleBarHeight());
|
||
}
|
||
}
|
||
#ifdef SA_RIBBON_DEBUG_HELP_DRAW
|
||
p.save();
|
||
p.setBrush(QColor(255, 0, 0, 120));
|
||
p.drawRect(titleRegion);
|
||
p.restore();
|
||
#endif
|
||
paintWindowTitle(p, parWindow->windowTitle(), titleRegion);
|
||
}
|
||
}
|
||
|
||
void SARibbonBar::paintInCompactStyle()
|
||
{
|
||
QPainter p(this);
|
||
//!
|
||
paintTabbarBaseLine(p);
|
||
//! 显示上下文标签
|
||
p.save();
|
||
QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory;
|
||
QMargins border = contentsMargins();
|
||
for (int i = 0; i < contextCategoryDataList.size(); ++i) {
|
||
QRect contextTitleRect;
|
||
QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex;
|
||
QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor();
|
||
if (!indexs.isEmpty()) {
|
||
contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first());
|
||
QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last());
|
||
contextTitleRect.setRight(endRect.right());
|
||
contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y());
|
||
contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1);
|
||
contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin();
|
||
// 把区域顶部扩展到窗口顶部
|
||
contextTitleRect.setTop(border.top());
|
||
// 绘制
|
||
paintContextCategoryTab(p, QString(), contextTitleRect, clr);
|
||
}
|
||
}
|
||
p.restore();
|
||
//! 显示标题等
|
||
|
||
QWidget* parWindow = parentWidget();
|
||
|
||
if (parWindow) {
|
||
int start = d_ptr->mRibbonTabBar->x() + d_ptr->mRibbonTabBar->width();
|
||
int width = d_ptr->mQuickAccessBar->x() - start;
|
||
if (width > 20) {
|
||
QRect titleRegion(start, border.top(), width, titleBarHeight());
|
||
#ifdef SA_RIBBON_DEBUG_HELP_DRAW
|
||
p.save();
|
||
p.setBrush(QColor(255, 0, 0, 120));
|
||
p.drawRect(titleRegion);
|
||
p.restore();
|
||
#endif
|
||
paintWindowTitle(p, parWindow->windowTitle(), titleRegion);
|
||
}
|
||
}
|
||
}
|
||
|
||
void SARibbonBar::resizeStackedContainerWidget()
|
||
{
|
||
QMargins border = contentsMargins();
|
||
const QRect& ribbonTabBarGeometry = d_ptr->mRibbonTabBar->geometry();
|
||
|
||
int x = border.left();
|
||
int y = ribbonTabBarGeometry.bottom() + 1;
|
||
int w = width() - border.left() - border.right();
|
||
int h = d_ptr->categoryHeight();
|
||
if (d_ptr->mStackedContainerWidget->isPopupMode()) {
|
||
// 弹出模式时,位置为全局位置
|
||
QPoint absPosition = mapToGlobal(QPoint(x, y));
|
||
x = absPosition.x();
|
||
y = absPosition.y();
|
||
}
|
||
d_ptr->mStackedContainerWidget->setFixedSize(QSize(w, h));
|
||
d_ptr->mStackedContainerWidget->setGeometry(x, y, w, h);
|
||
}
|
||
|
||
/**
|
||
* @brief 刷新所有ContextCategoryManagerData,这个在单独一个Category删除时调用
|
||
*/
|
||
void SARibbonBar::updateContextCategoryManagerData()
|
||
{
|
||
const int c = d_ptr->mRibbonTabBar->count();
|
||
|
||
for (_SAContextCategoryManagerData& cd : d_ptr->mCurrentShowingContextCategory) {
|
||
cd.tabPageIndex.clear();
|
||
for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) {
|
||
SARibbonCategory* category = cd.contextCategory->categoryPage(i);
|
||
for (int t = 0; t < c; ++t) {
|
||
QVariant v = d_ptr->mRibbonTabBar->tabData(t);
|
||
if (v.isValid()) {
|
||
_SARibbonTabData d = v.value< _SARibbonTabData >();
|
||
if (d.category == category) {
|
||
cd.tabPageIndex.append(t);
|
||
}
|
||
} else {
|
||
cd.tabPageIndex.append(-1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 绘制上下文标签的背景
|
||
* @param painter 绘图QPainter
|
||
* @param title 上下文标签的title
|
||
* @param contextRect 上下文标签的绘制区域
|
||
* @param color 上下文标签赋予的颜色
|
||
*/
|
||
void SARibbonBar::paintContextCategoryTab(QPainter& painter, const QString& title, const QRect& contextRect, const QColor& color)
|
||
{
|
||
// 绘制上下文标签
|
||
// 首先有5像素的实体粗线位于顶部
|
||
QMargins border = contentsMargins();
|
||
painter.save();
|
||
painter.setPen(Qt::NoPen);
|
||
painter.setBrush(color);
|
||
painter.drawRect(contextRect);
|
||
const int contextLineWidth = 5;
|
||
// 绘制很线
|
||
QColor gColor = color.darker();
|
||
painter.fillRect(QRect(contextRect.x(), contextRect.y(), contextRect.width(), contextLineWidth), gColor);
|
||
|
||
// 只有在office模式下才需要绘制标题
|
||
if (isLooseStyle()) {
|
||
if (!title.isEmpty()) {
|
||
QRect textRect = QRect(contextRect.x(),
|
||
contextRect.y() + contextLineWidth,
|
||
contextRect.width(),
|
||
contextRect.height() - contextLineWidth - d_ptr->mRibbonTabBar->height());
|
||
painter.setPen(contextCategoryTitleTextColor());
|
||
painter.drawText(textRect, Qt::AlignCenter, title);
|
||
}
|
||
}
|
||
painter.restore();
|
||
}
|
||
|
||
void SARibbonBar::resizeEvent(QResizeEvent* e)
|
||
{
|
||
Q_UNUSED(e);
|
||
if (isLooseStyle()) {
|
||
resizeInLooseStyle();
|
||
} else {
|
||
resizeInCompactStyle();
|
||
}
|
||
update();
|
||
}
|
||
|
||
/**
|
||
* @brief 重写moveevent是为了在移动时调整isPopupMode状态下的stackedContainerWidget位置
|
||
* @param event
|
||
*/
|
||
void SARibbonBar::moveEvent(QMoveEvent* e)
|
||
{
|
||
if (d_ptr->mStackedContainerWidget) {
|
||
if (d_ptr->mStackedContainerWidget->isPopupMode()) {
|
||
// 弹出模式时,窗口发生了移动,同步调整StackedContainerWidget的位置
|
||
resizeStackedContainerWidget();
|
||
}
|
||
}
|
||
QMenuBar::moveEvent(e);
|
||
}
|
||
|
||
/**
|
||
* @brief 跟踪字体改变
|
||
* @param event
|
||
*/
|
||
void SARibbonBar::changeEvent(QEvent* e)
|
||
{
|
||
if (nullptr == e) {
|
||
return;
|
||
}
|
||
switch (e->type()) {
|
||
case QEvent::FontChange: {
|
||
QFont f = font();
|
||
QList< QWidget* > listWidgets = findChildren< QWidget* >();
|
||
for (QWidget* w : listWidgets) {
|
||
w->setFont(f);
|
||
}
|
||
updateRibbonGeometry();
|
||
} break;
|
||
case QEvent::StyleChange: {
|
||
updateRibbonGeometry();
|
||
} break;
|
||
default:
|
||
break;
|
||
}
|
||
QMenuBar::changeEvent(e);
|
||
}
|
||
|
||
bool SARibbonBar::event(QEvent* e)
|
||
{
|
||
switch (e->type()) {
|
||
case QEvent::Show:
|
||
// 第一次显示刷新
|
||
updateRibbonGeometry();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return QMenuBar::event(e);
|
||
}
|
||
|
||
void SARibbonBar::resizeInLooseStyle()
|
||
{
|
||
synchronousCategoryData(false);
|
||
|
||
QMargins border = contentsMargins();
|
||
int x = border.left();
|
||
int y = border.top();
|
||
|
||
// cornerWidget - TopLeftCorner
|
||
const int validTitleBarHeight = d_ptr->titleBarHeight();
|
||
const int tabH = d_ptr->tabBarHeigth();
|
||
// tabbar的标签不会因为tabbar的高度而铺满,tabbar的标签是固定高度的,tabbar拉高只会把底部露出来,因此,systab是理论合理的高度
|
||
// 布局corner widget
|
||
x += d_ptr->mIconRightBorderPosition + 5;
|
||
if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) {
|
||
if (connerL->isVisibleTo(this)) {
|
||
QSize connerSize = connerL->sizeHint();
|
||
if (connerSize.height() < validTitleBarHeight) {
|
||
int detal = (validTitleBarHeight - connerSize.height()) / 2;
|
||
connerL->setGeometry(x, y + detal, connerSize.width(), connerSize.height());
|
||
} else {
|
||
connerL->setGeometry(x, y, connerSize.width(), validTitleBarHeight);
|
||
}
|
||
x = connerL->geometry().right() + 5;
|
||
}
|
||
}
|
||
// quick access bar定位
|
||
if (d_ptr->mQuickAccessBar) {
|
||
if (d_ptr->mQuickAccessBar->isVisibleTo(this)) {
|
||
if (d_ptr->mQuickAccessBar->height() != validTitleBarHeight) {
|
||
d_ptr->mQuickAccessBar->setFixedHeight(validTitleBarHeight);
|
||
}
|
||
QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint();
|
||
// 上下留1px的边线
|
||
d_ptr->mQuickAccessBar->setGeometry(x, y + 1, quickAccessBarSize.width(), validTitleBarHeight);
|
||
}
|
||
}
|
||
// 第二行,开始布局applicationButton,tabbar,tabBarRightSizeButtonGroupWidget,TopRightCorner
|
||
x = border.left();
|
||
y += validTitleBarHeight; // 此时,y值在titlebar下面
|
||
// applicationButton 定位
|
||
if (d_ptr->mApplicationButton) {
|
||
if (d_ptr->mApplicationButton->isVisibleTo(this)) {
|
||
// 保证
|
||
d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH);
|
||
x = d_ptr->mApplicationButton->geometry().right();
|
||
}
|
||
}
|
||
// top right是一定要配置的,对于多文档窗口,子窗口的缩放等按钮就是通过这个窗口实现,
|
||
// 由于这个窗口一定要在最右,因此先对这个窗口进行布局
|
||
// cornerWidget - TopRightCorner
|
||
// 获取最右边的位置
|
||
int endX = width() - border.right();
|
||
|
||
if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) {
|
||
if (connerW->isVisibleTo(this)) {
|
||
QSize connerSize = connerW->sizeHint();
|
||
endX -= connerSize.width();
|
||
if (connerSize.height() < tabH) {
|
||
int detal = (tabH - connerSize.height()) / 2;
|
||
connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height());
|
||
} else {
|
||
connerW->setGeometry(endX, y, connerSize.width(), tabH);
|
||
}
|
||
}
|
||
}
|
||
// applicationButton和TopRightCorner完成定位,才可以定位tab bar
|
||
// tab bar 定位
|
||
|
||
// tabBar 右边的附加按钮组,这里一般会附加一些类似登录等按钮组
|
||
// 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible()
|
||
if (d_ptr->mRightButtonGroup) {
|
||
QSize wSize = d_ptr->mRightButtonGroup->sizeHint();
|
||
endX -= wSize.width();
|
||
// 上下留1px的边线
|
||
d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), tabH - 2);
|
||
}
|
||
// 最后确定tabbar宽度
|
||
int tabBarAllowedWidth = endX - x;
|
||
if (ribbonAlignment() == SARibbonAlignment::AlignLeft) {
|
||
d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH);
|
||
} else {
|
||
// 居中对齐的情况下,Tab要居中显示
|
||
// 得到tab的推荐尺寸
|
||
int mintabBarWidth = calcMinTabBarWidth();
|
||
if (mintabBarWidth >= tabBarAllowedWidth) {
|
||
// 这时tabbar没有居中对齐的必要性,空间位置不够了
|
||
d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH);
|
||
} else {
|
||
// 说明tabbar的宽度有居中的可能性
|
||
int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2;
|
||
d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH);
|
||
}
|
||
}
|
||
resizeStackedContainerWidget();
|
||
}
|
||
|
||
void SARibbonBar::resizeInCompactStyle()
|
||
{
|
||
synchronousCategoryData(false);
|
||
QMargins border = contentsMargins();
|
||
int x = border.left();
|
||
int y = border.top();
|
||
|
||
const int validTitleBarHeight = titleBarHeight();
|
||
|
||
// 先布局右边内容
|
||
// cornerWidget - TopRightCorner
|
||
int endX = width() - border.right() - d_ptr->mWindowButtonSize.width();
|
||
|
||
if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) {
|
||
if (connerW->isVisibleTo(this)) {
|
||
QSize connerSize = connerW->sizeHint();
|
||
endX -= connerSize.width();
|
||
if (connerSize.height() < validTitleBarHeight) {
|
||
int detal = (validTitleBarHeight - connerSize.height()) / 2;
|
||
connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height());
|
||
} else {
|
||
connerW->setGeometry(endX, y, connerSize.width(), validTitleBarHeight);
|
||
}
|
||
}
|
||
}
|
||
|
||
// tabBar 右边的附加按钮组
|
||
// 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible()
|
||
if (d_ptr->mRightButtonGroup) {
|
||
QSize wSize = d_ptr->mRightButtonGroup->sizeHint();
|
||
endX -= wSize.width();
|
||
// 上下留1px的边线
|
||
d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), validTitleBarHeight - 2);
|
||
}
|
||
// quick access bar定位
|
||
if (d_ptr->mQuickAccessBar) {
|
||
if (d_ptr->mQuickAccessBar->isVisibleTo(this)) {
|
||
QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint();
|
||
endX -= quickAccessBarSize.width();
|
||
// 上下留1px的边线
|
||
d_ptr->mQuickAccessBar->setGeometry(endX, y + 1, quickAccessBarSize.width(), validTitleBarHeight - 2);
|
||
}
|
||
}
|
||
// cornerWidget - TopLeftCorner
|
||
if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) {
|
||
if (connerL->isVisibleTo(this)) {
|
||
QSize connerSize = connerL->sizeHint();
|
||
endX -= connerSize.width();
|
||
if (connerSize.height() < validTitleBarHeight) {
|
||
int detal = (validTitleBarHeight - connerSize.height()) / 2;
|
||
connerL->setGeometry(endX, y + detal, connerSize.width(), connerSize.height());
|
||
} else {
|
||
connerL->setGeometry(endX, y, connerSize.width(), validTitleBarHeight);
|
||
}
|
||
}
|
||
}
|
||
|
||
// tab 的y值需要重新计算
|
||
// 紧凑模式下,tabbarHeight不生效
|
||
int tabH = tabBarHeight();
|
||
if (tabH > validTitleBarHeight) {
|
||
// 这种直接把tabH设置为validTitleBarHeight
|
||
tabH = validTitleBarHeight;
|
||
}
|
||
|
||
y = y + validTitleBarHeight - tabH; // 如果tabH较小,则下以,让tab底部和title的底部对齐
|
||
|
||
// applicationButton 定位,与TabBar同高
|
||
if (d_ptr->mApplicationButton) {
|
||
if (d_ptr->mApplicationButton->isVisibleTo(this)) {
|
||
d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH);
|
||
x = d_ptr->mApplicationButton->geometry().right() + 2;
|
||
}
|
||
}
|
||
// tab bar 定位 wps模式下applicationButton的右边就是tab bar
|
||
int tabBarAllowedWidth = endX - x;
|
||
// 20200831
|
||
// tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整
|
||
// 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出
|
||
// mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow
|
||
// 计算tab所占用的宽度
|
||
int mintabBarWidth = calcMinTabBarWidth();
|
||
|
||
if (ribbonAlignment() == SARibbonAlignment::AlignLeft) {
|
||
if (mintabBarWidth < tabBarAllowedWidth) {
|
||
tabBarAllowedWidth = mintabBarWidth;
|
||
}
|
||
d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH);
|
||
} else {
|
||
// 居中对齐
|
||
if (mintabBarWidth >= tabBarAllowedWidth) {
|
||
// 这时tabbar没有居中对齐的必要性,空间位置不够了
|
||
d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH);
|
||
} else {
|
||
// 说明tabbar的宽度有居中的可能性
|
||
int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2;
|
||
d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH);
|
||
}
|
||
}
|
||
// 调整整个stackedContainer
|
||
resizeStackedContainerWidget();
|
||
}
|
||
|
||
/**
|
||
* @brief 绘制tabbar下的基准线,这个方法仅仅在office2013模式下需要
|
||
* @param painter
|
||
*/
|
||
void SARibbonBar::paintTabbarBaseLine(QPainter& painter)
|
||
{
|
||
if (!d_ptr->mTabBarBaseLineColor.isValid()) {
|
||
return;
|
||
}
|
||
painter.save();
|
||
// 在tabbar下绘制一条线
|
||
const int lineY = d_ptr->mRibbonTabBar->geometry().bottom();
|
||
QPen pen(d_ptr->mTabBarBaseLineColor);
|
||
QMargins border = contentsMargins();
|
||
|
||
pen.setWidth(1);
|
||
pen.setStyle(Qt::SolidLine);
|
||
painter.setPen(pen);
|
||
painter.drawLine(QPoint(border.left(), lineY), QPoint(width() - border.right() - 1, lineY));
|
||
painter.restore();
|
||
}
|
||
|
||
///
|
||
/// \brief 绘制标题栏
|
||
/// \param painter
|
||
/// \param title 标题
|
||
/// \param contextCategoryRegion 当前显示的上下文标签的范围,上下文标签是可以遮挡标题栏的,因此需要知道上下文标签的范围
|
||
/// x表示左边界,y表示右边界
|
||
///
|
||
void SARibbonBar::paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion)
|
||
{
|
||
// 如果标题不显示直接跳出
|
||
if (!isTitleVisible()) {
|
||
return;
|
||
}
|
||
painter.save();
|
||
if (d_ptr->mTitleTextColor.isValid()) {
|
||
painter.setPen(d_ptr->mTitleTextColor);
|
||
}
|
||
painter.drawText(titleRegion, d_ptr->mTitleAligment, title);
|
||
painter.restore();
|
||
}
|
||
|
||
#if SA_DEBUG_PRINT_SARIBBONBAR
|
||
QDebug operator<<(QDebug debug, const SARibbonBar& ribbon)
|
||
{
|
||
QDebugStateSaver saver(debug);
|
||
QFontMetrics fm = ribbon.fontMetrics();
|
||
debug.nospace() << "SARibbonBar(" << ribbon.versionString() << ")" //
|
||
<< "\nribbon font metrics info:" //
|
||
<< "\n - lineSpacing:" << fm.lineSpacing() //
|
||
<< "\n - height:" << fm.height() //
|
||
<< "\n - em:" << fm.boundingRect("M").width() //
|
||
<< "\n - ex:" << fm.boundingRect("X").height() //
|
||
<< "\nribbon info:" //
|
||
<< "\n -mTitleBarHeight=" << ribbon.d_ptr->mTitleBarHeight //
|
||
<< "\n -mTabBarHeight=" << ribbon.d_ptr->mTabBarHeight //
|
||
<< "\n -mPannelTitleHeight=" << ribbon.d_ptr->mPannelTitleHeight //
|
||
<< "\n -mCategoryHeight=" << ribbon.d_ptr->mCategoryHeight //
|
||
<< "\n -mIsTabOnTitle=" << ribbon.d_ptr->mIsTabOnTitle //
|
||
<< "\n -mEnableShowPannelTitle=" << ribbon.d_ptr->mEnableShowPannelTitle //
|
||
<< "\n -mWindowButtonSize=" << ribbon.d_ptr->mWindowButtonSize //
|
||
<< "\n -mIconRightBorderPosition=" << ribbon.d_ptr->mIconRightBorderPosition //
|
||
;
|
||
|
||
return debug;
|
||
}
|
||
#endif
|