logplus/Workflow/WFWidget/src/PaiHeaderView.cpp

396 lines
12 KiB
C++
Raw Normal View History

2026-01-16 17:18:41 +08:00
/**
* @file PaiHeaderView.cpp
* @date 2012-11-09
*/
#include <QPainter>
#include <QApplication>
#include <QMouseEvent>
#include <QToolButton>
#include <QMenu>
#include <QCheckBox>
#include "PaiHeaderView.h"
#include "PaiSettings.h"
const int SORT_ARRAW_LEN = 30; // 可以排序时右侧的盘需模式指示标记宽度
const int LAST_SECTION_ADD_SPACE_LEN = 15; //最后一列加点空隙不让pSectionSelectTBtn遮挡
#define LOGICAL_INDEX "logic index" //QCheckBox用于记录自身所属逻辑列数
using namespace pai::gui;
PaiHeaderView::PaiHeaderView(Qt::Orientation orientation, QWidget *pParent)
: QHeaderView(orientation, pParent),
m_SectionSelectableVisible(false)
{
m_pSectionSelectToolButton = new QToolButton(this);
// 该ToolButton底色需与HeaderView底色保持一致
m_pSectionSelectToolButton->setStyleSheet(QString::fromUtf8("background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, \n"
"stop:0 #FEFEFF, stop:1 #E9F2F8);\n"
"border-left: 1px solid #C9D5DC;\n"
"border-right: 0px;\n"
"border-top: 1px solid #C9D5DC;\n"
"border-bottom: 1px solid #C9D5DC;"));
m_pSectionSelectToolButton->setIcon(QIcon(":/add_01.png"));
m_pSectionSelectToolButton->setVisible(m_SectionSelectableVisible);
// setClickable(true);
setStretchLastSection(true); // PAI风格默认
connect(this, SIGNAL(sectionResized(int, int, int)),
this, SLOT(AdjustSectionSuitable(int, int, int)));
connect(m_pSectionSelectToolButton, SIGNAL(clicked()),
this, SLOT(ShowSectionVisibleMenu()));
}
PaiHeaderView::~PaiHeaderView()
{
}
void PaiHeaderView::SetSectionVisibleSelectable(bool selectable)
{
m_SectionSelectableVisible = selectable;
m_pSectionSelectToolButton->setVisible(m_SectionSelectableVisible);
}
void PaiHeaderView::paintSection(QPainter *pPainter, const QRect & rect, int logicalIndex) const
{
// 当显示列选择功能时,需单独绘制最后一列,以使标题信息不被选择按钮挡住
if(m_SectionSelectableVisible && (logicalIndex == LastVisibleSection()))
{
int adjustSize = m_pSectionSelectToolButton->width()-1; // -1px来保证做测边界只有一个边被显示
// 分两部分绘制,此处为前半部分
QHeaderView::paintSection(pPainter, rect.adjusted(0, 0, -adjustSize, 0), logicalIndex);
// 后半部分,其实就是不绘制任何图像
QStyleOptionHeader opt;
initStyleOption(&opt);
opt.sortIndicator = QStyleOptionHeader::None;
opt.rect = rect.adjusted(rect.width() - adjustSize, 0, 0, 0);
opt.section = logicalIndex;
opt.position = QStyleOptionHeader::End;
QApplication::style()->drawControl(QStyle::CE_Header, &opt, pPainter, this);
}
else // 其它列绘制
{
QHeaderView::paintSection(pPainter, rect, logicalIndex);
}
// 调整CheckBox位置
if(m_ColumnCheckBoxMap.contains(logicalIndex))
{
QCheckBox *pCheckBox = m_ColumnCheckBoxMap.value(logicalIndex);
int x = rect.x(); //靠左
int y = (rect.height() - pCheckBox->height()) / 2; //垂直居中
pCheckBox->move(x, y);
}
}
void PaiHeaderView::resizeEvent(QResizeEvent *pEvent)
{
QHeaderView::resizeEvent(pEvent);
// 使列选择按钮始终在表格右上角
if(m_SectionSelectableVisible)
{
m_pSectionSelectToolButton->resize(height(), height());
m_pSectionSelectToolButton->move(width()-height(), 0);
}
}
void PaiHeaderView::mousePressEvent(QMouseEvent *pEvent)
{
QHeaderView::mousePressEvent(pEvent);
if(Qt::RightButton == pEvent->button())
{
emit TitleRightClicked(logicalIndexAt(pEvent->pos()), pEvent->globalPos());
}
}
void PaiHeaderView::ShowSectionVisibleMenu()
{
QMenu contextMenu;
for(int i = 0; i < count(); ++i)
{
QString colText = model()->headerData(i, Qt::Horizontal).toString();
if(!m_UnselectableSections.contains(colText))
{
QAction *pVisibleAct = contextMenu.addAction(colText, this, SLOT(ReverseSectionVisible()));
pVisibleAct->setCheckable(true);
pVisibleAct->setChecked(!isSectionHidden(i));
pVisibleAct->setData(i); // 以该值来标记action所代表的列
}
}
contextMenu.exec(mapToGlobal(m_pSectionSelectToolButton->geometry().bottomLeft()));
}
void PaiHeaderView::CheckBoxStateChanged()
{
QCheckBox *pCheckBox = dynamic_cast<QCheckBox *>(sender());
if(pCheckBox != NULL)
{
emit CheckStateChanged(pCheckBox->property(LOGICAL_INDEX).toInt(), pCheckBox->checkState());
}
}
void PaiHeaderView::ReverseSectionVisible()
{
QAction *pAct = qobject_cast<QAction*>(sender());
if(pAct)
{
int index = LastVisibleSection(); // 显示列索引变为逻辑索引
if((index >= 0) && (index < count()))
{
int size = SectionSuitableWidth(index);
if(sectionSize(index) > size)
{
resizeSection(index, size);
}
}
int sectionIndex = pAct->data().toInt();
if((sectionIndex >= 0) && (sectionIndex < count())) // 判断逻辑索引是否超出范围
{
SetSectionHidden(sectionIndex, !isSectionHidden(sectionIndex)); // 交换显示状态
// 隐藏一列后将最后一列重新resize下不让pSectionSelectTBtn遮挡住
resizeSection (LastVisibleSection(), SectionSuitableWidth(LastVisibleSection()));
}
}
}
void PaiHeaderView::RememberCurrentSections()
{
if(!m_SectionSelectableVisible)
{
return;
}
if(parent() == NULL)
{
return;
}
// 生成对应的KEY
QString objName = parent()->objectName();
if(objName.isEmpty())
{
return;
}
QString key = objName.append("_VISIBLE_SECTIONS");
// 将当前显示的列索引记录到sections中
QStringList sections;
int cn = count();
for(int i = 0; i < cn; i++)
{
if(!isSectionHidden(i))
{
sections.append(QString::number(i));
}
}
PaiSettings settings;
settings.setValue(key, sections.join(","));
}
void PaiHeaderView::SetColumnCheckable(const int logicalIndex, const bool checkable, QCheckBox *pCheckBox)
{
if(checkable)
{
pCheckBox->setParent(this);
pCheckBox->setProperty(LOGICAL_INDEX, QString::number(logicalIndex));
pCheckBox->setFixedSize(20, 20);
connect(pCheckBox, SIGNAL(stateChanged(int)), this, SLOT(CheckBoxStateChanged()));
m_ColumnCheckBoxMap.insert(logicalIndex, pCheckBox);
}
else
{
//从Map移除并返回NULL
if(m_ColumnCheckBoxMap.contains(logicalIndex))
{
delete m_ColumnCheckBoxMap.value(logicalIndex);
m_ColumnCheckBoxMap.remove(logicalIndex);
}
}
update();
}
void PaiHeaderView::AdjustCheckBoxMap(bool insert, int column)
{
QMap<int, QCheckBox*> newMap;
if(insert)
{
//如果为插入操作,则将插入所在序列后的列序号+1
foreach(int index , m_ColumnCheckBoxMap.keys())
{
if(index < column)
{
newMap.insert(index, m_ColumnCheckBoxMap.value(index));
}
else
{
newMap.insert(index + 1, m_ColumnCheckBoxMap.value(index));
// 调整CheckBox对应的逻辑列
QCheckBox *pCheckBox = newMap.value(index + 1);
if(pCheckBox != NULL)
{
pCheckBox->setProperty(LOGICAL_INDEX, QString::number(index + 1));
}
}
}
}
else
{
// 如果为删除操作,则将删除所在序列后的列序号-1
foreach(int index , m_ColumnCheckBoxMap.keys())
{
if(index < column)
{
newMap.insert(index, m_ColumnCheckBoxMap.value(index));
}
else if(index == column)
{
// 如果此行为删除行则将CheckBox解析掉
QCheckBox *pCheckBox = m_ColumnCheckBoxMap.value(index);
if(pCheckBox != NULL)
{
delete pCheckBox;
pCheckBox = NULL;
}
}
else
{
newMap.insert(index - 1, m_ColumnCheckBoxMap.value(index));
// 调整CheckBox对应的逻辑列
QCheckBox *pCheckBox = newMap.value(index - 1);
if(pCheckBox != NULL)
{
pCheckBox->setProperty(LOGICAL_INDEX, QString::number(index - 1));
}
}
}
}
// 替换为调整后的Map
m_ColumnCheckBoxMap = newMap;
update();
}
void PaiHeaderView::RecallMemberedSections()
{
if(!m_SectionSelectableVisible)
{
return;
}
if(parent() == NULL)
{
return;
}
QString objName = parent()->objectName();
if(objName.isEmpty())
{
return;
}
// 生成当前表格显示列记录的KEY
QString key = objName.append("_VISIBLE_SECTIONS");
PaiSettings setting;
if(setting.contains(key))
{
int count = this->count();
// 余下可隐藏的默认显示列也设置其为隐藏
for(int i = 0; i < count; i++)
{
QString colText = model()->headerData(i, Qt::Horizontal).toString();
if(!m_UnselectableSections.contains(colText))
{
SetSectionHidden(i, true);
}
}
QString value = setting.value(key).toString();
if(value.isEmpty())
{
return;
}
QStringList indexs = value.split(",");
int size = indexs.size();
bool ok;
for(int i = 0; i < size; i++)
{
int index = indexs.at(i).toInt(&ok, 10);
if(ok && (index < count) && (index >= 0))
{
SetSectionHidden(index, false);
}
}
}
}
void PaiHeaderView::SetSectionHidden(int logicalIndex, bool hide)
{
setSectionHidden(logicalIndex, hide);
emit SectionVisibleChanged(logicalIndex, !hide);
}
void PaiHeaderView::AdjustSectionSuitable(int logicalIndex, int /*oldSize*/, int newSize)
{
// 为0的时候认为其将要被隐藏这时就不能再限制它的长度注意这里使用isSectionHidden无效
if(newSize != 0)
{
int sSize = SectionSuitableWidth(logicalIndex); // 获得该列的最适大小
if(newSize < sSize) // 当最新的宽度小于最适大小时,重新调整其大小
{
resizeSection(logicalIndex, sSize);
}
}
}
int PaiHeaderView::SectionSuitableWidth(int logicalIndex)
{
// 文本长度
int sw = fontMetrics().width(model()->headerData(logicalIndex, Qt::Horizontal).toString());
if(isSortIndicatorShown()) // 当有排序箭头时,加上该箭头的宽度
{
sw += SORT_ARRAW_LEN;
}
if(m_SectionSelectableVisible && (logicalIndex == LastVisibleSection())) // 当时最后显示列的时候,加上列选择按钮宽度
{
sw += m_pSectionSelectToolButton->width();
sw += LAST_SECTION_ADD_SPACE_LEN;//加长点要不最后一列的后两个字母被pSectionSelectTBtn遮挡
}
return sw;
}
int PaiHeaderView::LastVisibleSection() const
{
for(int i = count() - 1; i >= 0; --i)
{
int lgIndex = logicalIndex(i); // 显示列索引变为逻辑索引
if(!isSectionHidden(lgIndex))
{
return lgIndex;
}
}
return -1;
}
void PaiHeaderView::SetUnselectableSections(const QStringList & sectionList)
{
m_UnselectableSections = sectionList;
}