logplus/Workflow/WFWidget/src/PaiComboBox.cpp
2026-01-16 17:18:41 +08:00

533 lines
15 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.

/**
* @file PaiComboBox.cpp
* @date 2011-10-17
*/
#include <QStyledItemDelegate>
#include <QApplication>
#include <QPainter>
#include <QValidator>
#include <QKeyEvent>
#include <QLineEdit>
#include <QPaintEvent>
#include <QDesktopWidget>
#include <QFontMetrics>
#include <QAbstractItemView>
#include "PaiComboBox.h"
using namespace pai::gui;
/**
* @class PaiComboBoxItemDelegate
* @brief 用来绘制ComboBox弹出选择框Item
*/
class PAI_WIDGET_EXPORT PaiComboBoxItemDelegate : public QStyledItemDelegate
{
public:
PaiComboBoxItemDelegate(QObject *pParent = 0) :
QStyledItemDelegate(pParent)
{
}
protected:
/**
* @brief 重新实现重绘函数
* @param[in] pPainter 画笔
* @param[in] option 类型选项
* @param[in] index Model 索引
*/
void paint(QPainter *pPainter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt, index);
opt.state = opt.state & ~QStyle::State_Selected;
if((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Enabled))
{
QLinearGradient linearGrad(opt.rect.topLeft(), opt.rect.bottomLeft());
linearGrad.setColorAt(0, QColor("#FFFFFF"));
linearGrad.setColorAt(1, QColor("#EAF2F4"));
opt.backgroundBrush = QBrush(linearGrad);
}
else
{
opt.backgroundBrush = QBrush("#EFF5FA");
}
const QWidget *pWidget = opt.widget;
if(pWidget == NULL)
{
return;
}
QStyle *pStyle = pWidget ? pWidget->style() : QApplication::style();
pStyle->drawControl(QStyle::CE_ItemViewItem, &opt, pPainter, pWidget);
if((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Enabled))
{
pPainter->save();
pPainter->setPen(QColor("#AACEDB"));
pPainter->drawRoundedRect(option.rect.adjusted(1, 1, -2, -2), 1, 1);
pPainter->restore();
}
}
/**
* @brief 重设高度
* @param[in] option 类型选项
*/
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & /*index*/) const
{
return QSize(option.rect.width(), 24);
}
};
/////////////////////////////////////////////////////////////////////
PaiComboBox::PaiComboBox(QWidget *pParent) :
QComboBox(pParent),
m_AddButton(false)
{
InitComboBox();
}
PaiComboBox::PaiComboBox(bool showAddButton, QWidget *pParent)
: QComboBox(pParent),
m_AddButton(showAddButton)
{
InitComboBox();
SetShowAddButton(m_AddButton);
}
PaiComboBox::~PaiComboBox()
{
}
void PaiComboBox::InitComboBox()
{
m_pEditButton = NULL;
m_RealTimeValidate = false;
m_MaxVisibleItems = 10; // 默认显示10个
QAbstractItemView *pView = view();
// 将view初始设置为最小防止调整位置和大小时闪烁
pView->parentWidget()->setFixedSize(QSize(0, 0));
pView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
// 当Item文本过长则在右侧用“...”代替
pView->setTextElideMode(Qt::ElideRight);
const QObjectList objs = view()->parentWidget()->children ();
// 隐藏多余控件
foreach(QObject *pObj, objs)
{
QWidget *pWgt = qobject_cast<QWidget*>(pObj);
if(pWgt && (pWgt != pView))
{
pWgt->setMaximumHeight(0);
}
}
setItemDelegate(new PaiComboBoxItemDelegate(this));
}
void PaiComboBox::SetShowAddButton(bool show)
{
if (show && m_pEditButton == NULL)
{
m_AddButton = true;
m_pEditButton = new QToolButton(this);
m_pEditButton->setIcon(QIcon(":/Edit.png"));
m_pEditButton->setStyleSheet(
"QToolButton{ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
"stop: 0.0 #FFFFFF, stop: 0.4 #F7FAFD,stop: 0.4 #E5EFF8, stop: 1.0 #BDD7EC);"
"border: 1px solid #839CB6; border-left:0px;"
"border-top-right-radius: 2px; border-bottom-right-radius: 2px}"
"QToolButton:hover { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
"stop: 0.0 #F1F9FE, stop: 0.4 #E1F3FC, stop: 0.4 #C9E9F9, stop: 1.0 #9BD7F1);}"
"QToolButton:focus { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
"stop: 0 #E6F5FB, stop: 0.38 #D0ECF8, stop: 0.39 #AADDF2, stop: 1.0 #70C5EA);}"
);
connect(m_pEditButton, SIGNAL(clicked()), this, SIGNAL(EditButtonClicked()));
}
else if(!show && m_pEditButton)
{
// 去掉按钮所占空间
setStyleSheet(QString::fromUtf8("QComboBox{margin-right:0px}"));
m_pEditButton->hide();
}
}
void PaiComboBox::showPopup ()
{
QComboBox::showPopup();
if(count() == 0)
{
return;
}
// 每条Item的高度为24;
const int ITEM_HEIGHT = 24;
int popWidgetHeight = ITEM_HEIGHT * (m_MaxVisibleItems < count() ? m_MaxVisibleItems : count()) + 2;
QPoint popWidgetPos(0, 0);
// 屏幕高度
int iHeightOfScreen = QApplication::desktop()->height();
// 下方空间高度
int iBottomSpace = iHeightOfScreen - mapToGlobal(rect().bottomLeft()).y();
// 上方空间高度
int iTopSpace = mapToGlobal(rect().topLeft()).y();
if(iBottomSpace >= popWidgetHeight) //如果下方区域足够显示选择列表则在下方显示
{
popWidgetPos = mapToGlobal(rect().bottomLeft()) - QPoint( -m_margin.left(), m_margin.bottom());
}
else if(iTopSpace >= popWidgetHeight) //如果下方空间不足,而上方空间充足,则上方显示
{
popWidgetPos = mapToGlobal(rect().topLeft()) - QPoint(0, popWidgetHeight) + QPoint(m_margin.left(), m_margin.top());
}
else // 如果上下空间都不足,则调整显示条数
{
if(iBottomSpace > iTopSpace)
{
popWidgetHeight = iBottomSpace;
popWidgetPos = mapToGlobal(rect().bottomLeft()) - QPoint( -m_margin.left(), m_margin.bottom());
}
else
{
popWidgetHeight = iTopSpace;
popWidgetPos = mapToGlobal(rect().topLeft()) - QPoint(0, popWidgetHeight) + QPoint(m_margin.left(), m_margin.top());
}
}
// 调整view的位置和大小
QAbstractItemView *pView = view();
if(pView && pView->parentWidget())
{
pView->parentWidget()->setFixedHeight(popWidgetHeight);
pView->parentWidget()->move(popWidgetPos);
int minWidth = rect().width() - m_margin.left() - m_margin.right();
int widthOfScreen = QApplication::desktop()->width();
// 如果ComboBox的宽度小于屏幕三分之一根据每行Item文字长度调节下拉框宽度
if(rect().width() < widthOfScreen / 3)
{
// 根据每行Item文字长度确定List的最小宽度
QFontMetrics metrics(pView->font());
QString text;
for(int i = 0; i < count(); i++)
{
text = itemText(i) + " ";//添空格为边框占位
minWidth = minWidth > metrics.width(text) ? minWidth : metrics.width(text);
}
// 如果下拉框宽度超过显示屏宽度三分之一,则将其宽度设置为屏幕宽度的三分之一
if(minWidth > widthOfScreen / 3)
{
minWidth = widthOfScreen / 3;
}
}
pView->parentWidget()->setFixedWidth(minWidth);
}
}
int PaiComboBox::GetMaxVisibleItems() const
{
return m_MaxVisibleItems;
}
void PaiComboBox::SetMaxVisibleItems(const int count)
{
m_MaxVisibleItems = count;
}
void PaiComboBox::setStyleSheet(const QString & styleSheet)
{
QComboBox::setStyleSheet(styleSheet);
QRegExp reg("(margin)\\s*:\\s*\\d+\\s*(px)(.*);");
int offset = styleSheet.toLower().indexOf(reg);
if(offset != -1)
{
int end = styleSheet.indexOf(";", offset);
QString str = styleSheet.mid(offset, end - offset);
QStringList items = str.split(" ");
if(items.size() == 1)
{
QRegExp num("(\\d+)");
int pos = num.indexIn(items[0]);
QString value;
if (pos > -1)
{
value = num.cap(1);
}
bool ok;
int margin = value.toInt(&ok, 10);
if(ok)
{
m_margin = QMargins(margin, margin, margin, margin);
}
}
else if(items.size() == 4)
{
QRegExp num("(\\d+)");
int pos;
bool ok;
QString value;
int left, top, right, bottom;
pos = num.indexIn(items[0]);
if (pos > -1)
{
value = num.cap(1);
}
left = value.toInt(&ok, 10);
if(!ok)
{
return;
}
pos = num.indexIn(items[1]);
if (pos > -1)
{
value = num.cap(1);
}
top = value.toInt(&ok, 10);
if(!ok)
{
return;
}
pos = num.indexIn(items[2]);
if (pos > -1)
{
value = num.cap(1);
}
right = value.toInt(&ok, 10);
if(!ok)
{
return;
}
pos = num.indexIn(items[3]);
if (pos > -1)
{
value = num.cap(1);
}
bottom = value.toInt(&ok, 10);
if(!ok)
{
return;
}
m_margin = QMargins(left, top, right, bottom);
}
}
else
{
int left;
QRegExp regLeft("(margin-left)\\s*:\\s*\\d+\\s*(px)\\s*;");
offset = styleSheet.toLower().indexOf(regLeft);
if(offset == -1)
{
left = 0;
}
else
{
int end = styleSheet.indexOf(";", offset);
QString str = styleSheet.mid(offset, end - offset);
QRegExp num("(\\d+)");
int pos = num.indexIn(str);
QString value;
if (pos > -1)
{
value = num.cap(1);
}
bool ok;
int margin = value.toInt(&ok, 10);
if(ok)
{
left = margin;
}
}
int top;
QRegExp regTop("(margin-top)\\s*:\\s*\\d+\\s*(px)\\s*;");
offset = styleSheet.toLower().indexOf(regTop);
if(offset == -1)
{
top = 0;
}
else
{
int end = styleSheet.indexOf(";", offset);
QString str = styleSheet.mid(offset, end - offset);
QRegExp num("(\\d+)");
int pos = num.indexIn(str);
QString value;
if (pos > -1)
{
value = num.cap(1);
}
bool ok;
int margin = value.toInt(&ok, 10);
if(ok)
{
top = margin;
}
}
int right;
QRegExp regRight("(margin-right)\\s*:\\s*\\d+\\s*(px)\\s*;");
offset = styleSheet.toLower().indexOf(regRight);
if(offset == -1)
{
right = 0;
}
else
{
int end = styleSheet.indexOf(";", offset);
QString str = styleSheet.mid(offset, end - offset);
QRegExp num("(\\d+)");
int pos = num.indexIn(str);
QString value;
if (pos > -1)
{
value = num.cap(1);
}
bool ok;
int margin = value.toInt(&ok, 10);
if(ok)
{
right = margin;
}
}
int bottom;
QRegExp regBottom("(margin-top)\\s*:\\s*\\d+\\s*(px)\\s*;");
offset = styleSheet.toLower().indexOf(regBottom);
if(offset == -1)
{
bottom = 0;
}
else
{
int end = styleSheet.indexOf(";", offset);
QString str = styleSheet.mid(offset, end - offset);
QRegExp num("(\\d+)");
int pos = num.indexIn(str);
QString value;
if (pos > -1)
{
value = num.cap(1);
}
bool ok;
int margin = value.toInt(&ok, 10);
if(ok)
{
bottom = margin;
}
}
m_margin = QMargins(left, top, right, bottom);
}
}
void PaiComboBox::SetRealTimeValidate(bool flag)
{
m_RealTimeValidate = flag;
}
void PaiComboBox::keyPressEvent(QKeyEvent *pEvent)
{
if(pEvent->key() == Qt::Key_Return)
{
QComboBox::keyPressEvent(pEvent);
pEvent->accept(); // 过滤事件,使其不往父窗体传递
return;
}
if((!m_RealTimeValidate) || (pEvent->key() == Qt::Key_Backspace) || (pEvent->key() == Qt::Key_Delete))
{
QComboBox::keyPressEvent(pEvent);
return;
}
QString currentStr = currentText();
QLineEdit *pLineEdit = lineEdit();
QString selectedText = pLineEdit->selectedText();
QString pressAfterStr;
int pos = pLineEdit->cursorPosition();
if(selectedText.isEmpty())
{
pressAfterStr = currentStr.insert(pos, pEvent->text());
}
else
{
pressAfterStr = currentStr.replace(pLineEdit->selectionStart(), selectedText.size(), pEvent->text());
}
const QValidator *pValidator = validator();
int postion = pressAfterStr.size();
if(pValidator && (pValidator->validate(pressAfterStr, postion) == QValidator::Invalid))
{
pEvent->accept();
}
else
{
QComboBox::keyPressEvent(pEvent);
}
return;
}
void PaiComboBox::resizeEvent(QResizeEvent *pEvent)
{
QComboBox::resizeEvent(pEvent);
// 请不要添加m_pEditButton->isVisible()条件因为没有paint之前控件是不可见的
if(m_AddButton && m_pEditButton)
{
// 为按钮所准备空间
setStyleSheet(QString::fromUtf8("QComboBox{margin-right:%1px}").arg(height()));
}
}
void PaiComboBox::paintEvent(QPaintEvent *pEvent)
{
QComboBox::paintEvent(pEvent);
if(m_AddButton && m_pEditButton && m_pEditButton->isVisible())
{
// 添加限制条件确保这里不会频繁的调用
QRect rect(width()-height() - 1, 0, height() + 1, height());
if(m_pEditButton->geometry() != rect)
{
m_pEditButton->setGeometry(rect);
}
QPainter painter(this);
painter.setPen(QColor("#839CB6"));
painter.drawLine(width()-height() - 2, 6, width() - height() - 2, height() - 6);
}
}