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();
|
|||
|
}
|