533 lines
15 KiB
C++
533 lines
15 KiB
C++
/**
|
||
* @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);
|
||
}
|
||
}
|