instAdmin/Cpp/GisDes/SaRibbon/SARibbonBar/SARibbonPannelLayout.cpp
2024-10-29 22:24:50 +08:00

953 lines
34 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "SARibbonPannelLayout.h"
#include "SARibbonPannelOptionButton.h"
#include "SARibbonSeparatorWidget.h"
#include "SARibbonElementManager.h"
#include <QWidgetAction>
#include <QQueue>
#include "SARibbonPannel.h"
#include "SARibbonPannelItem.h"
#define SARibbonPannelLayout_DEBUG_PRINT 1
#define HELP_DRAW_RECT(p, rect) \
do { \
p.save(); \
QPen _pen(Qt::DashLine); \
_pen.setColor(Qt::blue); \
p.setPen(_pen); \
p.setBrush(QBrush()); \
p.drawRect(rect); \
p.restore(); \
} while (0)
SARibbonPannelLayout::SARibbonPannelLayout(QWidget* p) : QLayout(p), mColumnCount(0), mExpandFlag(false), mDirty(true)
{
setSpacing(1);
SARibbonPannel* tb = qobject_cast< SARibbonPannel* >(p);
if (!tb) {
return;
}
}
SARibbonPannelLayout::~SARibbonPannelLayout()
{
// 参考QToolBarLayout
while (!mItems.isEmpty()) {
SARibbonPannelItem* item = mItems.takeFirst();
if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action)) {
if (item->customWidget) {
widgetAction->releaseWidget(item->widget());
}
}
delete item;
}
}
/**
* @brief 通过action查找索引用于actionEvent添加action用
* @param action
* @return 没有查到返回-1
*/
int SARibbonPannelLayout::indexByAction(QAction* action) const
{
for (int i = 0; i < mItems.count(); ++i) {
if (mItems.at(i)->action == action) {
return (i);
}
}
return (-1);
}
/**
* @brief 获取ribbonpannel
* @return
*/
SARibbonPannel* SARibbonPannelLayout::ribbonPannel() const
{
return qobject_cast< SARibbonPannel* >(parentWidget());
}
void SARibbonPannelLayout::addItem(QLayoutItem* item)
{
Q_UNUSED(item);
qWarning("SARibbonPannelLayout::addItem(): please use addAction() instead");
return;
}
/**
* @brief SARibbonPannel主要通过此函数来添加action
* @param act
* @param rp 布局策略
*/
void SARibbonPannelLayout::insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp)
{
index = qMax(0, index);
index = qMin(mItems.count(), index);
SARibbonPannelItem* item = createItem(act, rp);
if (item) {
mItems.insert(index, item);
// 标记需要重新计算尺寸
invalidate();
}
}
/**
* @brief 添加操作action如果要去除传入nullptr指针即可SARibbonPannel不会对QAction的所有权进行管理
* @param action
* @note 要去除OptionAction直接传入nullptr即可
* @note SARibbonPannel不对QAction的destroy进行关联如果外部对action进行delete需要先传入nullptr给addOptionAction
*/
void SARibbonPannelLayout::setOptionAction(QAction* action)
{
SARibbonPannel* p = ribbonPannel();
if (!p) {
return;
}
if (action) {
// 创建option action
if (nullptr == mOptionActionBtn) {
mOptionActionBtn = RibbonSubElementFactory->createRibbonPannelOptionButton(p);
QObject::connect(mOptionActionBtn, &SARibbonToolButton::triggered, p, &SARibbonPannel::actionTriggered);
// 确保m_optionActionBtn在label之上
if (mTitleLabel) {
mTitleLabel->stackUnder(mOptionActionBtn);
}
}
mOptionActionBtn->setDefaultAction(action);
if (action->icon().isNull()) {
mOptionActionBtn->setIcon(QIcon(":/image/resource/ribbonPannelOptionButton.png"));
}
// 标记需要重新计算尺寸
invalidate();
} else {
// 取消option action
if (mOptionActionBtn) {
mOptionActionBtn->hide();
mOptionActionBtn->deleteLater();
mOptionActionBtn = nullptr;
// 标记需要重新计算尺寸
invalidate();
}
}
}
/**
* @brief 判断是否存在OptionAction
* @return 存在返回true
*/
bool SARibbonPannelLayout::isHaveOptionAction() const
{
return (mOptionActionBtn != nullptr);
}
QLayoutItem* SARibbonPannelLayout::itemAt(int index) const
{
if ((index < 0) || (index >= mItems.count())) {
return (nullptr);
}
return (mItems.at(index));
}
QLayoutItem* SARibbonPannelLayout::takeAt(int index)
{
if ((index < 0) || (index >= mItems.count())) {
return (nullptr);
}
SARibbonPannelItem* item = mItems.takeAt(index);
QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action);
if ((widgetAction != 0) && item->customWidget) {
widgetAction->releaseWidget(item->widget());
} else {
// destroy the QToolButton/QToolBarSeparator
item->widget()->hide();
item->widget()->deleteLater();
}
invalidate();
return (item);
}
int SARibbonPannelLayout::count() const
{
return (mItems.count());
}
bool SARibbonPannelLayout::isEmpty() const
{
return (mItems.isEmpty());
}
void SARibbonPannelLayout::invalidate()
{
mDirty = true;
QLayout::invalidate();
}
Qt::Orientations SARibbonPannelLayout::expandingDirections() const
{
return (Qt::Horizontal);
}
QSize SARibbonPannelLayout::minimumSize() const
{
return (mSizeHint);
}
QSize SARibbonPannelLayout::sizeHint() const
{
#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT
if (SARibbonPannel* pannel = ribbonPannel()) {
qDebug() << "| |-SARibbonPannelLayout sizeHint,sizeHint = " << m_sizeHint;
}
#endif
return (mSizeHint);
}
/**
* @brief 通过action获取SARibbonPannelItem
* @param action
* @return 如果没有返回nullptr
*/
SARibbonPannelItem* SARibbonPannelLayout::pannelItem(QAction* action) const
{
int index = indexByAction(action);
if (index >= 0) {
return (mItems[ index ]);
}
return (nullptr);
}
/**
* @brief 获取最后一个添加的item
* @return 如果没有返回nullptr
*/
SARibbonPannelItem* SARibbonPannelLayout::lastItem() const
{
if (mItems.isEmpty()) {
return (nullptr);
}
return (mItems.last());
}
/**
* @brief 获取最后生成的窗口
* @return 如果无窗口或者item为空返回nullptr
*/
QWidget* SARibbonPannelLayout::lastWidget() const
{
SARibbonPannelItem* item = lastItem();
if (item) {
return (item->widget());
}
return (nullptr);
}
/**
* @brief 移动两个item
* @param from
* @param to
* @note 移动完后所有都失效,需要重新布局
*/
void SARibbonPannelLayout::move(int from, int to)
{
if (from == to) {
return;
}
if (to < 0) {
to = 0;
}
if (to >= count()) {
to = count() - 1;
}
mItems.move(from, to);
invalidate();
}
/**
* @brief 判断是否需要重新布局
* @return
*/
bool SARibbonPannelLayout::isDirty() const
{
return (mDirty);
}
void SARibbonPannelLayout::updateGeomArray()
{
updateGeomArray(geometry());
}
/**
* @brief 布局所有action
*/
void SARibbonPannelLayout::doLayout()
{
#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT
if (SARibbonPannel* pannel = ribbonPannel()) {
qDebug() << "| |-SARibbonPannelLayout layoutActions,pannel name = " << pannel->pannelName();
}
#endif
if (isDirty()) {
updateGeomArray();
}
QList< QWidget* > showWidgets, hideWidgets;
SARibbonPannel* pannel = ribbonPannel();
for (SARibbonPannelItem* item : qAsConst(mItems)) {
if (item->isEmpty()) {
hideWidgets << item->widget();
} else {
// 在category发现item->setGeometry有点奇怪的现象这里统一使用item->widget->setgeo
// item->setGeometry(item->itemWillSetGeometry);
if (item->widget()) {
item->widget()->setGeometry(item->itemWillSetGeometry);
}
showWidgets << item->widget();
}
}
// 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘导致循环绘制非常影响效率
for (QWidget* w : qAsConst(showWidgets)) {
if (!w->isVisible())
w->show();
}
for (QWidget* w : qAsConst(hideWidgets)) {
if (w->isVisible())
w->hide();
}
// 布局label
if (mTitleLabel) {
if (isEnableShowPannelTitle()) {
mTitleLabel->setGeometry(mTitleLabelGeometry);
if (!mTitleLabel->isVisibleTo(pannel)) {
mTitleLabel->show();
}
} else {
if (mTitleLabel->isVisibleTo(pannel)) {
mTitleLabel->hide();
}
}
}
// 布局m_optionActionBtn
if (mOptionActionBtn) {
mOptionActionBtn->setGeometry(mOptionActionBtnGeometry);
mOptionActionBtn->setIconSize(QSize(mOptionActionBtnGeometry.width(), mOptionActionBtnGeometry.height()));
}
}
/**
* @brief 把action转换为item
*
* 此函数参考QToolBarItem *QToolBarLayout::createItem(QAction *action)
*
* 对于普通QAction此函数会创建SARibbonToolButtonSARibbonToolButton的类型参考SARibbonPannelItem::RowProportion
* @param action
* @param rp 行高占比情况
* @return 转换的SARibbonPannelItem
* @note 每个SARibbonPannelItem最终都会携带一个widget传入的是QWidgetAction的话会直接使用QWidgetAction带的widget
* 否则会内部生成一个SARibbonToolButton
*
*/
SARibbonPannelItem* SARibbonPannelLayout::createItem(QAction* action, SARibbonPannelItem::RowProportion rp)
{
bool customWidget = false;
QWidget* widget = nullptr;
SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget());
if (!pannel) {
// 在没有pannel这个函数会返回nullptr
return (nullptr);
}
if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action)) {
widget = widgetAction->requestWidget(pannel);
if (widget != nullptr) {
widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
customWidget = true; // 标记为true在移除的时候是不会对这个窗口进行删除false默认会进行删除如SARibbonSeparatorWidget和SARibbonToolButton
}
} else if (action->isSeparator()) {
SARibbonSeparatorWidget* sep = RibbonSubElementFactory->createRibbonSeparatorWidget(pannel);
widget = sep;
}
// 不是widget自动生成SARibbonToolbutton
if (!widget) {
SARibbonToolButton::RibbonButtonType buttonType = ((rp == SARibbonPannelItem::Large)
? SARibbonToolButton::LargeButton
: SARibbonToolButton::SmallButton);
SARibbonToolButton* button = RibbonSubElementFactory->createRibbonToolButton(pannel);
button->setFocusPolicy(Qt::NoFocus);
button->setButtonType(buttonType);
button->setDefaultAction(action);
button->setIconSize(mDefaultToolButtonIconSize);
// 属性设置
QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(action);
button->setPopupMode(popMode);
// 根据QAction的属性设置按钮的大小
QObject::connect(button, &SARibbonToolButton::triggered, pannel, &SARibbonPannel::actionTriggered);
widget = button;
}
// 这时总会有widget
widget->hide();
SARibbonPannelItem* result = new SARibbonPannelItem(widget);
result->rowProportion = rp;
result->customWidget = customWidget;
result->action = action;
return (result);
}
/**
* @brief 更新尺寸
*/
void SARibbonPannelLayout::updateGeomArray(const QRect& setrect)
{
SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget());
if (!pannel) {
return;
}
const int height = setrect.height();
const QMargins& mag = contentsMargins();
const int spacing = this->spacing();
const int spacingRow = 1; // 高度间距,也就是行间距,不同行之间的距离
int x = mag.left();
const int yBegin = mag.top();
int titleH = (mTitleHeight >= 0) ? mTitleHeight : 0; // 防止负数影响
int titleSpace = (mTitleHeight >= 0) ? mTitleSpace : 0; // 对于没有标题的情况spacing就不生效
if (!isEnableShowPannelTitle()) {
titleH = 0;
titleSpace = 0;
}
// 获取pannel的布局模式 3行或者2行
// rowcount 是ribbon的行有2行和3行两种
const short rowCount = (pannel->pannelLayoutMode() == SARibbonPannel::ThreeRowMode) ? 3 : 2;
// largeHeight是对应large占比的高度
const int largeHeight = height - mag.bottom() - mag.top() - titleH - titleSpace;
const int yTitleBegin = height - mag.bottom() - titleH;
mLargeHeight = largeHeight;
// 计算smallHeight的高度
const int smallHeight = (largeHeight - (rowCount - 1) * spacingRow) / rowCount;
// Medium行的y位置
const int yMediumRow0 = (2 == rowCount) ? yBegin : (yBegin + ((largeHeight - 2 * smallHeight) / 3));
const int yMediumRow1 = (2 == rowCount) ? (yBegin + smallHeight + spacingRow)
: (yBegin + ((largeHeight - 2 * smallHeight) / 3) * 2 + smallHeight);
// Small行的y位置
const int ySmallRow0 = yBegin;
const int ySmallRow1 = yBegin + smallHeight + spacingRow;
const int ySmallRow2 = yBegin + 2 * (smallHeight + spacingRow);
// row用于记录下个item应该属于第几行item->rowIndex用于记录当前处于第几行
// item->rowIndex主要用于SARibbonPannelItem::Medium
short row = 0;
int column = 0;
// 记录每列最大的宽度
int columMaxWidth = 0;
// 记录总宽度
int totalWidth = 0;
int itemCount = mItems.count();
#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT
QString debug_print__log__;
#endif
// 本列第一、二行占比
SARibbonPannelItem::RowProportion thisColumnRP0 = SARibbonPannelItem::None;
SARibbonPannelItem* lastGeomItem = nullptr; // 记录最后一个设置位置的item
for (int i = 0; i < itemCount; ++i) {
SARibbonPannelItem* item = mItems.at(i);
if (item->isEmpty()) {
// 如果是hide就直接跳过
item->rowIndex = -1;
item->columnIndex = -1;
continue;
}
QSize hint = item->sizeHint();
#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT
if (SARibbonToolButton* tb = qobject_cast< SARibbonToolButton* >(item->widget())) {
auto ss__ = tb->sizeHint();
debug_print__log__ += QString("| | |-[%1]SARibbonToolButton.sizeHint=(%2,%3),ButtonText=%4\n")
.arg(i)
.arg(ss__.width())
.arg(ss__.height())
.arg(tb->text());
}
#endif
Qt::Orientations exp = item->expandingDirections();
if (item->widget()) {
// 有窗口是水平扩展,则标记为扩展
if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) {
mExpandFlag = true;
}
}
SARibbonPannelItem::RowProportion rp = item->rowProportion;
if (SARibbonPannelItem::None == rp) {
// 为定义行占比但是垂直扩展就定义为Large占比否则就是small占比
if (exp & Qt::Vertical) {
rp = SARibbonPannelItem::Large;
} else {
rp = SARibbonPannelItem::Small;
}
}
// 开始根据占比和layoutmode来布局
switch (rp) {
case SARibbonPannelItem::Large: {
// 在Large如果不是处于新列的第一行就需要进行换列处理
// 把large一直设置在下一列的开始
if (row != 0) {
x += (columMaxWidth + spacing);
++column;
}
//
item->rowIndex = 0;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, yBegin, hint.width(), largeHeight);
columMaxWidth = hint.width();
// 换列x自动递增到下个坐标列数增加行数归零最大列宽归零
x += (columMaxWidth + spacing);
row = 0;
columMaxWidth = 0;
++column;
} break;
case SARibbonPannelItem::Medium: {
// 2行模式下Medium和small等价
if (2 == rowCount) {
if (0 == row) {
item->rowIndex = 0;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
thisColumnRP0 = SARibbonPannelItem::Medium;
columMaxWidth = hint.width();
// 下个row为1
row = 1;
// x不变
} else {
item->rowIndex = 1;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
// 和上个进行比较得到最长宽度
columMaxWidth = qMax(columMaxWidth, hint.width());
// 换列x自动递增到下个坐标列数增加行数归零最大列宽归零
x += (columMaxWidth + spacing);
row = 0;
columMaxWidth = 0;
++column;
}
} else {
// 3行模式
if (0 == row) {
item->rowIndex = 0;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
thisColumnRP0 = SARibbonPannelItem::Medium;
columMaxWidth = hint.width();
row = 1;
// x不变
} else if (1 == row) {
item->rowIndex = 1;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
columMaxWidth = qMax(columMaxWidth, hint.width());
// 换列x自动递增到下个坐标列数增加行数归零最大列宽归零
x += (columMaxWidth + spacing);
row = 0;
columMaxWidth = 0;
++column;
} else {
// 这种模式一般情况会发生在当前列前两行是Small添加了一个Medium
// 这时需要先换列
// 换列x自动递增到下个坐标列数增加行数归零最大列宽归零
x += (columMaxWidth + spacing);
++column;
// 换列后此时等价于0 == row
item->rowIndex = 0;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight);
thisColumnRP0 = SARibbonPannelItem::Medium;
columMaxWidth = hint.width();
row = 1;
}
}
} break;
case SARibbonPannelItem::Small: {
if (0 == row) {
// 第一行
item->rowIndex = 0;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight);
thisColumnRP0 = SARibbonPannelItem::Small;
columMaxWidth = hint.width();
// 下个row为1
row = 1;
// x不变
} else if (1 == row) {
// 第二行
item->rowIndex = 1;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight);
if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) {
// 三行模式并且第一行是Medium
item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight);
}
// 和上个进行比较得到最长宽度
columMaxWidth = qMax(columMaxWidth, hint.width());
// 这里要看两行还是三行,确定是否要换列
if (2 == rowCount) {
// 两行模式,换列
// 换列x自动递增到下个坐标列数增加行数归零最大列宽归零
x += (columMaxWidth + spacing);
row = 0;
columMaxWidth = 0;
++column;
} else {
// 三行模式,继续增加行数
row = 2;
// x不变
}
if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) {
// 三行模式并且第一行是Medium换列
// 换列x自动递增到下个坐标列数增加行数归零最大列宽归零
x += (columMaxWidth + spacing);
row = 0;
columMaxWidth = 0;
++column;
}
} else {
// 第三行
item->rowIndex = 2;
item->columnIndex = column;
item->itemWillSetGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight);
// 和上个进行比较得到最长宽度
columMaxWidth = qMax(columMaxWidth, hint.width());
// 换列x自动递增到下个坐标列数增加行数归零最大列宽归零
x += (columMaxWidth + spacing);
row = 0;
columMaxWidth = 0;
++column;
}
} break;
default:
// 不可能出现
break;
}
lastGeomItem = item;
}
// 最后一个元素,更新列数
// 2022-06-20 此句本来在循环里面,如果最后一个元素隐藏,会导致无法到达此判断导致异常
if (lastGeomItem) { // 最后一个元素更新totalWidth
if (lastGeomItem->columnIndex != column) {
// 说明最后一个元素处于最后位置触发了换列此时真实列数需要减1直接等于column索引
mColumnCount = column;
// 由于最后一个元素触发了换列x值是新一列的位置直接作为totalWidth要减去已经加入的spacing
totalWidth = x - spacing + mag.right();
} else {
// 说明最后一个元素处于非最后位置没有触发下一个换列此时真实列数等于column索引+1
mColumnCount = column + 1;
// 由于最后一个元素未触发换列需要计算totalWidth
totalWidth = x + columMaxWidth + mag.right();
}
}
// 在设置完所有窗口后,再设置扩展属性的窗口
if (totalWidth < setrect.width() && (setrect.width() - totalWidth) > 10) {
// 说明可以设置扩展属性的窗口
recalcExpandGeomArray(setrect);
}
// 布局label
bool isTitleWidthThanPannel = false;
if (isEnableShowPannelTitle()) {
mTitleLabelGeometry.setRect(mag.left(), yTitleBegin, setrect.width() - mag.left() - mag.right(), titleH);
// 这里要确认标题宽度是否大于totalWidth如果大于则要把标题的宽度作为totalwidth
QFontMetrics fm = mTitleLabel->fontMetrics();
int textWidth = SA_FONTMETRICS_WIDTH(fm, pannel->pannelName());
textWidth += 4;
if (totalWidth < textWidth) {
totalWidth = textWidth;
isTitleWidthThanPannel = true; // 说明标题的长度大于按钮布局的长度
}
}
// 布局optionActionButton
if (isHaveOptionAction()) {
QSize optBtnSize = optionActionButtonSize();
if (isEnableShowPannelTitle()) {
// 有标题
mOptionActionBtnGeometry.setRect(mTitleLabelGeometry.right() - mTitleLabelGeometry.height(),
mTitleLabelGeometry.y(),
mTitleLabelGeometry.height(),
mTitleLabelGeometry.height());
// 特殊情况如果pannel的标题长度大于totalWidth那么说明totalWidth比较短
// 这时候optionActionBtn的宽度要加上到标题宽度上
if (isTitleWidthThanPannel) {
// 由于文字是居中对齐因此要扩展2个按钮的宽度
totalWidth += (2 * titleH);
}
} else {
// 无标题
mOptionActionBtnGeometry.setRect(setrect.right() - optBtnSize.width() - mag.right(),
setrect.bottom() - optBtnSize.height() - mag.bottom(),
optBtnSize.width(),
optBtnSize.height());
totalWidth += optBtnSize.width();
}
}
// 刷新sizeHint
int heightHint = SARibbonPannel::pannelHeightHint(pannel->fontMetrics(), pannel->pannelLayoutMode(), titleH);
this->mSizeHint = QSize(totalWidth, heightHint);
#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT
qDebug() << "| |-SARibbonPannelLayout updateGeomArray(" << setrect << "),pannel name = " << pannel->pannelName()
<< "\n| | |-size hint =" << this->m_sizeHint //
<< "\n| | |-totalWidth=" << totalWidth //
<< "\n| | |-last x=" << x //
<< "\n| | |-columMaxWidth=" << columMaxWidth //
<< "\n| | |-spacing=" << spacing //
<< "\n| | |-mag=" << mag //
<< "\n| | |-largeHeight=" << largeHeight //
<< "\n| | |-smallHeight=" << smallHeight //
;
qDebug().noquote() << debug_print__log__;
#endif
}
void SARibbonPannelLayout::recalcExpandGeomArray(const QRect& setrect)
{
// 计算能扩展的尺寸
int expandwidth = setrect.width() - this->mSizeHint.width();
if (expandwidth <= 0) {
// 没有必要设置
return;
}
// 列扩展信息
struct _columnExpandInfo
{
int oldColumnWidth = 0; ///< 原来的列宽
int columnMaximumWidth = -1; ///< 列的最大宽度
int columnExpandedWidth = 0; ///< 扩展后列的宽度
QList< SARibbonPannelItem* > expandItems;
};
// 此变量用于记录可以水平扩展的列和控件,在布局结束后,如果还有空间,就把水平扩展的控件进行扩展
QMap< int, _columnExpandInfo > columnExpandInfo;
for (SARibbonPannelItem* item : qAsConst(mItems)) {
if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) {
// 只获取可见的
QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.find(item->columnIndex);
if (i == columnExpandInfo.end()) {
i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo());
}
i.value().expandItems.append(item);
}
}
if (columnExpandInfo.size() <= 0) {
// 没有需要扩展的就退出
return;
}
// 获取完可扩展的列和控件后,计算对应的列的尺寸
// 计算能扩展的尺寸
int oneColCanexpandWidth = expandwidth / columnExpandInfo.size();
for (QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) {
int& oldColumnWidth = i.value().oldColumnWidth;
int& columnMaximumWidth = i.value().columnMaximumWidth;
this->columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth);
if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) {
// 如果小于0说明没有这个列这种属于异常删除继续
// oldColumnWidth > columnMaximumWidth也是异常
i = columnExpandInfo.erase(i);
continue;
}
// 开始调整
int colwidth = oneColCanexpandWidth + oldColumnWidth; // 先扩展了
if (colwidth >= columnMaximumWidth) {
// 过最大宽度要求
i.value().columnExpandedWidth = columnMaximumWidth;
} else {
i.value().columnExpandedWidth = colwidth;
}
++i;
}
// 从新调整尺寸
// 由于会涉及其他列的变更,因此需要所有都遍历一下
for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) {
int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth;
for (SARibbonPannelItem* item : qAsConst(mItems)) {
if (item->isEmpty() || (item->columnIndex < i.key())) {
// 之前的列不用管
continue;
}
if (item->columnIndex == i.key()) {
// 此列的扩展
if (i.value().expandItems.contains(item)) {
// 此列需要扩展的item才扩展尺寸
item->itemWillSetGeometry.setWidth(i.value().columnExpandedWidth);
} else {
// 此列不扩展的模块保持原来的尺寸
continue;
}
} else {
// 后面的移动
item->itemWillSetGeometry.moveLeft(item->itemWillSetGeometry.x() + moveXLen);
}
}
}
#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT
qDebug() << "| |-SARibbonPannelLayout recalcExpandGeomArray(" << setrect
<< ") pannelName=" << ribbonPannel()->pannelName() //
<< ",expandwidth=" << expandwidth //
;
#endif
}
/**
* @brief 根据列数,计算窗口的宽度,以及最大宽度
* @param colindex
* @param width 如果传入没有这个列,返回-1
* @param maximum 如果传入没有这个列,返回-1
*/
void SARibbonPannelLayout::columnWidthInfo(int colindex, int& width, int& maximum) const
{
width = -1;
maximum = -1;
for (SARibbonPannelItem* item : mItems) {
if (!item->isEmpty() && (item->columnIndex == colindex)) {
width = qMax(width, item->itemWillSetGeometry.width());
maximum = qMax(maximum, item->widget()->maximumWidth());
}
}
}
SARibbonPannelLabel* SARibbonPannelLayout::pannelTitleLabel() const
{
return mTitleLabel;
}
void SARibbonPannelLayout::setToolButtonIconSize(const QSize& s)
{
mDefaultToolButtonIconSize = s;
}
QSize SARibbonPannelLayout::toolButtonIconSize() const
{
return mDefaultToolButtonIconSize;
}
/**
* @brief 获取optionAction 按钮尺寸
* @return
*/
QSize SARibbonPannelLayout::optionActionButtonSize() const
{
return (isEnableShowPannelTitle() ? QSize(12, 12) : QSize(mTitleHeight, mTitleHeight));
}
void SARibbonPannelLayout::setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel)
{
mTitleLabel = newTitleLabel;
// 确保m_optionActionBtn在label之上
if (mOptionActionBtn) {
if (mTitleLabel) {
mTitleLabel->stackUnder(mOptionActionBtn);
}
}
}
/**
* @brief 标题区域和按钮的间隔
* @return
*/
int SARibbonPannelLayout::pannelTitleSpace() const
{
return mTitleSpace;
}
/**
* @brief 设置标题区域和按钮的间隔
* @param newTitleSpace
*/
void SARibbonPannelLayout::setPannelTitleSpace(int newTitleSpace)
{
if (mTitleSpace == newTitleSpace) {
return;
}
mTitleSpace = newTitleSpace;
invalidate();
}
/**
* @brief 标题高度
* @return
*/
int SARibbonPannelLayout::pannelTitleHeight() const
{
return mTitleHeight;
}
/**
* @brief 设置标题高度
* @param newTitleHeight
*/
void SARibbonPannelLayout::setPannelTitleHeight(int newTitleHeight)
{
if (mTitleHeight == newTitleHeight) {
return;
}
mTitleHeight = newTitleHeight;
invalidate();
}
/**
* @brief 判断是否存在标题
* @return
*/
bool SARibbonPannelLayout::isEnableShowPannelTitle() const
{
return mEnableShowTitle;
}
/**
* @brief 设置显示标题
* @param on
*/
void SARibbonPannelLayout::setEnableShowPannelTitle(bool on)
{
if (mEnableShowTitle == on) {
return;
}
mEnableShowTitle = on;
invalidate();
}
/**
* @brief 大按钮的高度
* @return
*/
int SARibbonPannelLayout::largeButtonHeight() const
{
return mLargeHeight;
}
void SARibbonPannelLayout::setGeometry(const QRect& rect)
{
QRect old = geometry();
if (old == rect) {
return;
}
#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT
qDebug() << "| |----->SARibbonPannelLayout.setGeometry(" << rect << "(" << ribbonPannel()->pannelName() << ")=======";
#endif
QLayout::setGeometry(rect);
mDirty = false;
updateGeomArray(rect);
doLayout();
}