953 lines
34 KiB
C++
953 lines
34 KiB
C++
#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,此函数会创建SARibbonToolButton,SARibbonToolButton的类型参考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();
|
||
}
|